Skip to content

Commit ccb9ba3

Browse files
authored
When installing a release, install operators in parallel (#202)
* When installing a release, install operators in parallel * Changelog * Reference the PR from the changelog
1 parent d885d3c commit ccb9ba3

File tree

7 files changed

+93
-61
lines changed

7 files changed

+93
-61
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/stackable-cockpit/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ tracing.workspace = true
3434
url.workspace = true
3535
utoipa = { workspace = true, optional = true }
3636
which.workspace = true
37+
futures.workspace = true
3738

3839
[dev-dependencies]
3940
rstest.workspace = true

rust/stackable-cockpit/src/helm.rs

Lines changed: 53 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::fmt::Display;
33

44
use serde::{Deserialize, Serialize};
55
use snafu::{ResultExt, Snafu};
6+
use tokio::task::block_in_place;
67
use tracing::{debug, error, info, instrument};
78
use url::Url;
89

@@ -202,62 +203,66 @@ pub fn install_release_from_repo(
202203
namespace: &str,
203204
suppress_output: bool,
204205
) -> Result<InstallReleaseStatus, Error> {
205-
debug!("Install Helm release from repo");
206-
207-
if check_release_exists(release_name, namespace)? {
208-
let release = get_release(release_name, namespace)?.ok_or(Error::InstallRelease {
209-
source: InstallReleaseError::NoSuchRelease {
210-
name: release_name.to_owned(),
211-
},
212-
})?;
213-
214-
let current_version = release.version;
215-
216-
match chart_version {
217-
Some(chart_version) => {
218-
if chart_version == current_version {
219-
return Ok(InstallReleaseStatus::ReleaseAlreadyInstalledWithVersion {
220-
requested_version: chart_version.to_string(),
206+
// Ideally, each Helm invocation would spawn_blocking instead in/around helm_sys,
207+
// but that requires a larger refactoring
208+
block_in_place(|| {
209+
debug!("Install Helm release from repo");
210+
211+
if check_release_exists(release_name, namespace)? {
212+
let release = get_release(release_name, namespace)?.ok_or(Error::InstallRelease {
213+
source: InstallReleaseError::NoSuchRelease {
214+
name: release_name.to_owned(),
215+
},
216+
})?;
217+
218+
let current_version = release.version;
219+
220+
match chart_version {
221+
Some(chart_version) => {
222+
if chart_version == current_version {
223+
return Ok(InstallReleaseStatus::ReleaseAlreadyInstalledWithVersion {
224+
requested_version: chart_version.to_string(),
225+
release_name: release_name.to_string(),
226+
current_version,
227+
});
228+
} else {
229+
return Err(Error::InstallRelease {
230+
source: InstallReleaseError::ReleaseAlreadyInstalled {
231+
requested_version: chart_version.into(),
232+
name: release_name.into(),
233+
current_version,
234+
},
235+
});
236+
}
237+
}
238+
None => {
239+
return Ok(InstallReleaseStatus::ReleaseAlreadyInstalledUnspecified {
221240
release_name: release_name.to_string(),
222241
current_version,
223-
});
224-
} else {
225-
return Err(Error::InstallRelease {
226-
source: InstallReleaseError::ReleaseAlreadyInstalled {
227-
requested_version: chart_version.into(),
228-
name: release_name.into(),
229-
current_version,
230-
},
231-
});
242+
})
232243
}
233244
}
234-
None => {
235-
return Ok(InstallReleaseStatus::ReleaseAlreadyInstalledUnspecified {
236-
release_name: release_name.to_string(),
237-
current_version,
238-
})
239-
}
240245
}
241-
}
242246

243-
let full_chart_name = format!("{repo_name}/{chart_name}");
244-
let chart_version = chart_version.unwrap_or(HELM_DEFAULT_CHART_VERSION);
247+
let full_chart_name = format!("{repo_name}/{chart_name}");
248+
let chart_version = chart_version.unwrap_or(HELM_DEFAULT_CHART_VERSION);
245249

246-
debug!(
247-
"Installing Helm release {} ({}) from chart {}",
248-
release_name, chart_version, full_chart_name
249-
);
250-
251-
install_release(
252-
release_name,
253-
&full_chart_name,
254-
chart_version,
255-
values_yaml,
256-
namespace,
257-
suppress_output,
258-
)?;
250+
debug!(
251+
"Installing Helm release {} ({}) from chart {}",
252+
release_name, chart_version, full_chart_name
253+
);
259254

260-
Ok(InstallReleaseStatus::Installed(release_name.to_string()))
255+
install_release(
256+
release_name,
257+
&full_chart_name,
258+
chart_version,
259+
values_yaml,
260+
namespace,
261+
suppress_output,
262+
)?;
263+
264+
Ok(InstallReleaseStatus::Installed(release_name.to_string()))
265+
})
261266
}
262267

263268
/// Installs a Helm release.

rust/stackable-cockpit/src/platform/release/spec.rs

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
use futures::{StreamExt as _, TryStreamExt};
12
use indexmap::IndexMap;
23
use serde::{Deserialize, Serialize};
34
use snafu::{ResultExt, Snafu};
5+
use tokio::task::JoinError;
46
use tracing::{info, instrument};
57

68
#[cfg(feature = "openapi")]
@@ -26,6 +28,9 @@ pub enum Error {
2628

2729
#[snafu(display("failed to uninstall release using Helm"))]
2830
HelmUninstall { source: helm::Error },
31+
32+
#[snafu(display("failed to launch background task"))]
33+
BackgroundTask { source: JoinError },
2934
}
3035

3136
#[derive(Clone, Debug, Deserialize, Serialize)]
@@ -46,26 +51,39 @@ pub struct ReleaseSpec {
4651
impl ReleaseSpec {
4752
/// Installs a release by installing individual operators.
4853
#[instrument(skip_all)]
49-
pub fn install(
54+
pub async fn install(
5055
&self,
5156
include_products: &[String],
5257
exclude_products: &[String],
5358
namespace: &str,
5459
) -> Result<()> {
5560
info!("Installing release");
5661

57-
for (product_name, product) in self.filter_products(include_products, exclude_products) {
58-
info!("Installing {product_name}-operator");
59-
60-
// Create operator spec
61-
let operator = OperatorSpec::new(product_name, Some(product.version.clone()))
62-
.context(OperatorSpecParseSnafu)?;
63-
64-
// Install operator
65-
operator.install(namespace).context(HelmInstallSnafu)?
66-
}
67-
68-
Ok(())
62+
let namespace = namespace.to_string();
63+
futures::stream::iter(self.filter_products(include_products, exclude_products))
64+
.map(|(product_name, product)| {
65+
let namespace = namespace.clone();
66+
// Helm installs currently `block_in_place`, so we need to spawn each job onto a separate task to
67+
// get useful parallelism.
68+
tokio::spawn(async move {
69+
info!("Installing {product_name}-operator");
70+
71+
// Create operator spec
72+
let operator = OperatorSpec::new(&product_name, Some(product.version.clone()))
73+
.context(OperatorSpecParseSnafu)?;
74+
75+
// Install operator
76+
operator.install(&namespace).context(HelmInstallSnafu)?;
77+
78+
info!("Installed {product_name}-operator");
79+
80+
Ok(())
81+
})
82+
})
83+
.buffer_unordered(10)
84+
.map(|res| res.context(BackgroundTaskSnafu)?)
85+
.try_collect::<()>()
86+
.await
6987
}
7088

7189
#[instrument(skip_all)]

rust/stackable-cockpit/src/platform/stack/spec.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ impl StackSpec {
197197
// Install the release
198198
release
199199
.install(&self.operators, &[], operator_namespace)
200+
.await
200201
.context(InstallReleaseSnafu)
201202
}
202203

rust/stackablectl/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@ All notable changes to this project will be documented in this file.
44

55
## [Unreleased]
66

7+
### Changed
8+
9+
- Operators are now installed in parallel when installing a release ([#202]).
10+
711
### Fixed
812

913
- Fix `--cluster-name` not taking effect. The local test clusters always used the default cluster name ([#181]).
1014

1115
[#181]: https://github.com/stackabletech/stackable-cockpit/pull/181
16+
[#202]: https://github.com/stackabletech/stackable-cockpit/pull/202
1217

1318
## [23.11.3] - 2024-01-03
1419

rust/stackablectl/src/cmds/release.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ async fn install_cmd(
289289
&args.excluded_products,
290290
&args.operator_namespace,
291291
)
292+
.await
292293
.context(ReleaseInstallSnafu)?;
293294

294295
output

0 commit comments

Comments
 (0)