From 483d40c1676560646b144f2fb018479df6f9f0b5 Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Wed, 22 Apr 2026 18:58:09 +0200 Subject: [PATCH 1/7] chore: Add ChartSourceKind for oci/repo/local Note: Local isn't yet supported, but it is a fallback for when the URL doesn't match oci:// or http(s):// --- rust/stackable-cockpit/src/helm.rs | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/rust/stackable-cockpit/src/helm.rs b/rust/stackable-cockpit/src/helm.rs index 76528584..b63c3ee9 100644 --- a/rust/stackable-cockpit/src/helm.rs +++ b/rust/stackable-cockpit/src/helm.rs @@ -38,6 +38,40 @@ pub struct ChartRepo { pub url: String, } +/// The kind of source a chart repo URL refers to. +/// +/// [Self::Oci] and [Self::Local] don't need special handling, but [Self::Repo] +/// needs to call `helm::add_repo`. +/// +/// Note: We don't yet support local repositories, so an error should be emitted +/// if the source is [Self::Local]. +#[derive(Debug, PartialEq)] +pub enum ChartSourceKind { + /// OCI registry (url starts with `oci://`) + Oci, + + /// Traditional index.yaml-based repository (url starts with `http://` or `https://`) + Repo, + + /// Local filesystem path (not yet supported) + /// + /// This is the fallback if not oci or http(s). + Local, +} + +impl ChartRepo { + /// Determine the kind of chart source based on the URL scheme. + pub fn source_kind(&self) -> ChartSourceKind { + if self.url.starts_with("oci://") { + ChartSourceKind::Oci + } else if self.url.starts_with("http://") || self.url.starts_with("https://") { + ChartSourceKind::Repo + } else { + ChartSourceKind::Local + } + } +} + #[derive(Debug, Snafu)] pub enum Error { #[snafu(display("failed to parse URL"))] From 91a4b42ffb7d2a46ea0e5f8f6f314ec4ffb81d39 Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Wed, 22 Apr 2026 19:02:39 +0200 Subject: [PATCH 2/7] feat: Add support for OCI repositories Note: Only non-OCI remote charts need the helm::add_repo() call. We emit an error if a Local (non oci:// or http(s)://) value is given. --- .../src/platform/manifests.rs | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/rust/stackable-cockpit/src/platform/manifests.rs b/rust/stackable-cockpit/src/platform/manifests.rs index 6e5dd45d..1fd4e108 100644 --- a/rust/stackable-cockpit/src/platform/manifests.rs +++ b/rust/stackable-cockpit/src/platform/manifests.rs @@ -56,6 +56,10 @@ pub enum Error { source: helm::Error, }, + /// This error indicates that the Helm chart source kind is not supported. + #[snafu(display("local Helm chart sources are not yet supported (url: {url})"))] + UnsupportedChartSource { url: String }, + /// This error indicates that Helm chart options could not be serialized /// into YAML. #[snafu(display("failed to serialize Helm chart options"))] @@ -104,12 +108,23 @@ pub trait InstallManifestsExt { info!(helm_chart.name, helm_chart.version, "Installing Helm chart",); - // Assumption: that all manifest helm charts refer to repos not registries - helm::add_repo(&helm_chart.repo.name, &helm_chart.repo.url).context( - AddHelmRepositorySnafu { - repo_name: helm_chart.repo.name.clone(), - }, - )?; + let chart_source = match helm_chart.repo.source_kind() { + helm::ChartSourceKind::Repo => { + helm::add_repo(&helm_chart.repo.name, &helm_chart.repo.url).context( + AddHelmRepositorySnafu { + repo_name: helm_chart.repo.name.clone(), + }, + )?; + &helm_chart.repo.name + } + helm::ChartSourceKind::Oci => &helm_chart.repo.url, + helm::ChartSourceKind::Local => { + return UnsupportedChartSourceSnafu { + url: helm_chart.repo.url.clone(), + } + .fail(); + } + }; // Serialize chart options to string let values_yaml = serde_yaml::to_string(&helm_chart.options) @@ -119,7 +134,7 @@ pub trait InstallManifestsExt { helm::upgrade_or_install_release_from_repo_or_registry( &helm_chart.release_name, helm::ChartVersion { - chart_source: &helm_chart.repo.name, + chart_source, chart_name: &helm_chart.name, chart_version: Some(&helm_chart.version), }, From 600e81159466ff7fa3fe0022784b74e59532a5e0 Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Wed, 22 Apr 2026 19:04:51 +0200 Subject: [PATCH 3/7] test: Ensure ChartRepo::source_kind gives expected variants This seems trivial now in this set of changes, but is intended to catch future regressions --- rust/stackable-cockpit/src/helm.rs | 50 ++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/rust/stackable-cockpit/src/helm.rs b/rust/stackable-cockpit/src/helm.rs index b63c3ee9..21f99d5b 100644 --- a/rust/stackable-cockpit/src/helm.rs +++ b/rust/stackable-cockpit/src/helm.rs @@ -539,3 +539,53 @@ where serde_yaml::from_str(&index_file_content).context(DeserializeYamlSnafu) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn source_kind_oci() { + let repo = ChartRepo { + name: "nifi-operator".to_string(), + url: "oci://oci.stackable.tech/sdp-charts".to_string(), + }; + assert_eq!(repo.source_kind(), ChartSourceKind::Oci); + } + + #[test] + fn source_kind_https_repo() { + let repo = ChartRepo { + name: "stackable-stable".to_string(), + url: "https://repo.stackable.tech/repository/helm-stable".to_string(), + }; + assert_eq!(repo.source_kind(), ChartSourceKind::Repo); + } + + #[test] + fn source_kind_http_repo() { + let repo = ChartRepo { + name: "example".to_string(), + url: "http://example.com/charts".to_string(), + }; + assert_eq!(repo.source_kind(), ChartSourceKind::Repo); + } + + #[test] + fn source_kind_relative_path_is_local() { + let repo = ChartRepo { + name: "local".to_string(), + url: "./charts/my-chart".to_string(), + }; + assert_eq!(repo.source_kind(), ChartSourceKind::Local); + } + + #[test] + fn source_kind_absolute_path_is_local() { + let repo = ChartRepo { + name: "local".to_string(), + url: "/absolute/path/to/chart".to_string(), + }; + assert_eq!(repo.source_kind(), ChartSourceKind::Local); + } +} From d7db4a7456835a01614f65d5dd5ddd2c028376a7 Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Wed, 22 Apr 2026 19:23:57 +0200 Subject: [PATCH 4/7] chore: Update changelog --- rust/stackablectl/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/stackablectl/CHANGELOG.md b/rust/stackablectl/CHANGELOG.md index bc493c58..91507e68 100644 --- a/rust/stackablectl/CHANGELOG.md +++ b/rust/stackablectl/CHANGELOG.md @@ -10,9 +10,11 @@ All notable changes to this project will be documented in this file. - Add `uninstall` subcommand for `demo`/`stack` commands ([#429]). - Add confirmation prompt to `install` subcommand for namespace selection ([#429]). - Add `--assume-yes` option for running commands non-interactively ([#429]). +- Support Helm charts sourced from OCI registries in demo/stack manifests ([#440]). [#429]: https://github.com/stackabletech/stackable-cockpit/pull/429 [#438]: https://github.com/stackabletech/stackable-cockpit/pull/438 +[#440]: https://github.com/stackabletech/stackable-cockpit/pull/440 ## [1.4.0] - 2026-03-18 From c595df4028fcdc1d3d7facd8394df2758c801e3e Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Wed, 22 Apr 2026 19:35:21 +0200 Subject: [PATCH 5/7] chore: cargo update -p rustls-webpki --- Cargo.lock | 4 ++-- Cargo.nix | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 100f6188..600b48dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3145,9 +3145,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.12" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8279bb85272c9f10811ae6a6c547ff594d6a7f3c6c6b02ee9726d1d0dcfcdd06" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "aws-lc-rs", "ring", diff --git a/Cargo.nix b/Cargo.nix index db19601b..9abd0950 100644 --- a/Cargo.nix +++ b/Cargo.nix @@ -10491,9 +10491,9 @@ rec { }; "rustls-webpki" = rec { crateName = "rustls-webpki"; - version = "0.103.12"; + version = "0.103.13"; edition = "2021"; - sha256 = "01nxzkfd1l96jzp04svc7iznlkarzx3wb9p63a0i17rc4y2vnyc2"; + sha256 = "0vkm7z9pnxz5qz66p2kmyy2pwx0g4jnsbqk5xzfhs4czcjl2ki31"; libName = "webpki"; dependencies = [ { From b3d6cd76c0d945c1852388b73c9b863c8740705a Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Thu, 23 Apr 2026 08:54:43 +0200 Subject: [PATCH 6/7] chore: Rename path to chart_source, and use the Debug impl --- rust/stackable-cockpit/src/platform/manifests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/stackable-cockpit/src/platform/manifests.rs b/rust/stackable-cockpit/src/platform/manifests.rs index 1fd4e108..36531ded 100644 --- a/rust/stackable-cockpit/src/platform/manifests.rs +++ b/rust/stackable-cockpit/src/platform/manifests.rs @@ -57,8 +57,8 @@ pub enum Error { }, /// This error indicates that the Helm chart source kind is not supported. - #[snafu(display("local Helm chart sources are not yet supported (url: {url})"))] - UnsupportedChartSource { url: String }, + #[snafu(display("local Helm chart sources are not yet supported (source: {chart_source:?})"))] + UnsupportedChartSource { chart_source: String }, /// This error indicates that Helm chart options could not be serialized /// into YAML. @@ -120,7 +120,7 @@ pub trait InstallManifestsExt { helm::ChartSourceKind::Oci => &helm_chart.repo.url, helm::ChartSourceKind::Local => { return UnsupportedChartSourceSnafu { - url: helm_chart.repo.url.clone(), + chart_source: helm_chart.repo.url.clone(), } .fail(); } From c519e4b527f7ef619bb3396ac821d24953851875 Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Thu, 23 Apr 2026 08:57:02 +0200 Subject: [PATCH 7/7] test: Use rstest Also use str::to_owned() --- rust/stackable-cockpit/src/helm.rs | 56 ++++++++---------------------- 1 file changed, 15 insertions(+), 41 deletions(-) diff --git a/rust/stackable-cockpit/src/helm.rs b/rust/stackable-cockpit/src/helm.rs index 21f99d5b..baad9ee1 100644 --- a/rust/stackable-cockpit/src/helm.rs +++ b/rust/stackable-cockpit/src/helm.rs @@ -542,50 +542,24 @@ where #[cfg(test)] mod tests { - use super::*; - - #[test] - fn source_kind_oci() { - let repo = ChartRepo { - name: "nifi-operator".to_string(), - url: "oci://oci.stackable.tech/sdp-charts".to_string(), - }; - assert_eq!(repo.source_kind(), ChartSourceKind::Oci); - } - - #[test] - fn source_kind_https_repo() { - let repo = ChartRepo { - name: "stackable-stable".to_string(), - url: "https://repo.stackable.tech/repository/helm-stable".to_string(), - }; - assert_eq!(repo.source_kind(), ChartSourceKind::Repo); - } + use rstest::rstest; - #[test] - fn source_kind_http_repo() { - let repo = ChartRepo { - name: "example".to_string(), - url: "http://example.com/charts".to_string(), - }; - assert_eq!(repo.source_kind(), ChartSourceKind::Repo); - } - - #[test] - fn source_kind_relative_path_is_local() { - let repo = ChartRepo { - name: "local".to_string(), - url: "./charts/my-chart".to_string(), - }; - assert_eq!(repo.source_kind(), ChartSourceKind::Local); - } + use super::*; - #[test] - fn source_kind_absolute_path_is_local() { + #[rstest] + #[case("oci://oci.stackable.tech/sdp-charts", ChartSourceKind::Oci)] + #[case( + "https://repo.stackable.tech/repository/helm-stable", + ChartSourceKind::Repo + )] + #[case("http://example.com/charts", ChartSourceKind::Repo)] + #[case("./charts/my-chart", ChartSourceKind::Local)] + #[case("/absolute/path/to/chart", ChartSourceKind::Local)] + fn source_kind(#[case] url: &str, #[case] expected: ChartSourceKind) { let repo = ChartRepo { - name: "local".to_string(), - url: "/absolute/path/to/chart".to_string(), + name: "test".to_owned(), + url: url.to_owned(), }; - assert_eq!(repo.source_kind(), ChartSourceKind::Local); + assert_eq!(repo.source_kind(), expected); } }