diff --git a/.changes/unreleased/operator-Added-20250114-113113.yaml b/.changes/unreleased/operator-Added-20250114-113113.yaml new file mode 100644 index 000000000..f12569c0e --- /dev/null +++ b/.changes/unreleased/operator-Added-20250114-113113.yaml @@ -0,0 +1,12 @@ +project: operator +kind: Added +body: | + Users in air-gapped environments that cannot access the official Redpanda Helm Chart repository (`https://charts.redpanda.com/`) + can now specify an alternative Helm chart repository using the `helm-repository-url` flag. In the Redpanda Operator Helm chart, + this flag is not exposed as an option in the Helm values. Instead, it must be set as an input in the `additionalCmdFlags` array. + + The given repository must include the following charts: + * Redpanda + * Console + * Connectors +time: 2025-01-14T11:31:13.061026+01:00 diff --git a/operator/CHANGELOG.md b/operator/CHANGELOG.md index f52ddcbbc..0d4b41124 100644 --- a/operator/CHANGELOG.md +++ b/operator/CHANGELOG.md @@ -6,6 +6,16 @@ and is generated by [Changie](https://github.com/miniscruff/changie). ## Unreleased +### Added +* Users in air-gapped environments that cannot access the official Redpanda Helm Chart repository (`https://charts.redpanda.com/`) + can now specify an alternative Helm chart repository using the `helm-repository-url` flag. In the Redpanda Operator Helm chart, + this flag is not exposed as an option in the Helm values. Instead, it must be set as an input in the `additionalCmdFlags` array. + + The given repository must include the following charts: + * Redpanda + * Console + * Connectors + ### Changed * For any user that is mirroring configurator image (air-gapped environment) and changes entrypoint or wraps configurator with additional script the following constraint need to be meet: diff --git a/operator/api/redpanda/v1alpha2/redpanda_types.go b/operator/api/redpanda/v1alpha2/redpanda_types.go index b7b3f95ae..c76074903 100644 --- a/operator/api/redpanda/v1alpha2/redpanda_types.go +++ b/operator/api/redpanda/v1alpha2/redpanda_types.go @@ -32,8 +32,6 @@ import ( ) const ( - RedpandaChartRepository = "https://charts.redpanda.com/" - // ClusterConfigSynced is a condition indicating whether or not the // redpanda cluster's configuration is up to date with the desired config. ClusterConfigSynced = "ClusterConfigSynced" diff --git a/operator/cmd/run/run.go b/operator/cmd/run/run.go index 6f3bf2628..f39c33814 100644 --- a/operator/cmd/run/run.go +++ b/operator/cmd/run/run.go @@ -135,6 +135,7 @@ func Command() *cobra.Command { unbinderSelector LabelSelectorValue autoDeletePVCs bool forceDefluxedMode bool + helmRepositoryURL string ) cmd := &cobra.Command{ @@ -166,6 +167,7 @@ func Command() *cobra.Command { autoDeletePVCs, forceDefluxedMode, pprofAddr, + helmRepositoryURL, ) }, } @@ -198,6 +200,7 @@ func Command() *cobra.Command { cmd.Flags().Var(&unbinderSelector, "unbinder-label-selector", "if provided, a Kubernetes label selector that will filter Pods to be considered by the PVCUnbinder.") cmd.Flags().BoolVar(&autoDeletePVCs, "auto-delete-pvcs", false, "Use StatefulSet PersistentVolumeClaimRetentionPolicy to auto delete PVCs on scale down and Cluster resource delete.") cmd.Flags().BoolVar(&forceDefluxedMode, "force-defluxed-mode", false, "specifies the default value for useFlux of Redpanda CRs if not specified. May be used in conjunction with enable-helm-controllers=false") + cmd.Flags().StringVar(&helmRepositoryURL, "helm-repository-url", "https://charts.redpanda.com/", "URL to overwrite official `https://charts.redpanda.com/` Redpanda Helm chart repository") // 3rd party flags. clientOptions.BindFlags(cmd.Flags()) @@ -234,6 +237,7 @@ func Run( autoDeletePVCs bool, forceDefluxedMode bool, pprofAddr string, + helmRepositoryURL string, ) error { setupLog := ctrl.LoggerFrom(ctx).WithName("setup") @@ -387,6 +391,7 @@ func Run( EventRecorder: mgr.GetEventRecorderFor("RedpandaReconciler"), ClientFactory: internalclient.NewFactory(mgr.GetConfig(), mgr.GetClient()), DefaultDisableFlux: forceDefluxedMode, + HelmRepositoryURL: helmRepositoryURL, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Redpanda") return err diff --git a/operator/internal/controller/redpanda/redpanda_controller.go b/operator/internal/controller/redpanda/redpanda_controller.go index e0af83067..cd871cbb8 100644 --- a/operator/internal/controller/redpanda/redpanda_controller.go +++ b/operator/internal/controller/redpanda/redpanda_controller.go @@ -83,6 +83,12 @@ type RedpandaReconciler struct { EventRecorder kuberecorder.EventRecorder ClientFactory internalclient.ClientFactory DefaultDisableFlux bool + // HelmRepositorySpec.URL points to Redpanda helm repository where the following charts can be located: + // * Redpanda + // * Console + // * Connectors + // If not provided the v1alpha2.RedpandaChartRepository constant will be used. + HelmRepositoryURL string } // flux resources main resources @@ -825,7 +831,7 @@ func (r *RedpandaReconciler) reconcileHelmRelease(ctx context.Context, rp *v1alp } func (r *RedpandaReconciler) reconcileHelmRepository(ctx context.Context, rp *v1alpha2.Redpanda) error { - repo := r.helmRepositoryFromTemplate(rp) + repo := r.HelmRepositoryFromTemplate(rp) if err := r.apply(ctx, repo); err != nil { return fmt.Errorf("applying HelmRepository: %w", err) @@ -949,7 +955,7 @@ func (r *RedpandaReconciler) createHelmReleaseFromTemplate(ctx context.Context, }, nil } -func (r *RedpandaReconciler) helmRepositoryFromTemplate(rp *v1alpha2.Redpanda) *sourcev1.HelmRepository { +func (r *RedpandaReconciler) HelmRepositoryFromTemplate(rp *v1alpha2.Redpanda) *sourcev1.HelmRepository { return &sourcev1.HelmRepository{ ObjectMeta: metav1.ObjectMeta{ Name: rp.GetHelmRepositoryName(), @@ -959,7 +965,7 @@ func (r *RedpandaReconciler) helmRepositoryFromTemplate(rp *v1alpha2.Redpanda) * Spec: sourcev1.HelmRepositorySpec{ Suspend: !r.IsFluxEnabled(rp.Spec.ChartRef.UseFlux), Interval: metav1.Duration{Duration: 30 * time.Second}, - URL: v1alpha2.RedpandaChartRepository, + URL: r.HelmRepositoryURL, }, } } diff --git a/operator/internal/controller/redpanda/redpanda_controller_test.go b/operator/internal/controller/redpanda/redpanda_controller_test.go index 7bfac9e53..4b6b59074 100644 --- a/operator/internal/controller/redpanda/redpanda_controller_test.go +++ b/operator/internal/controller/redpanda/redpanda_controller_test.go @@ -702,10 +702,11 @@ func (s *RedpandaControllerSuite) SetupSuite() { // TODO should probably run other reconcilers here. if err := (&redpanda.RedpandaReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - EventRecorder: mgr.GetEventRecorderFor("Redpanda"), - ClientFactory: s.clientFactory, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + EventRecorder: mgr.GetEventRecorderFor("Redpanda"), + ClientFactory: s.clientFactory, + HelmRepositoryURL: "https://charts.redpanda.com/", }).SetupWithManager(s.ctx, mgr); err != nil { return err } @@ -979,6 +980,31 @@ func TestIsFluxEnabled(t *testing.T) { } } +func TestHelmRepositoryURL(t *testing.T) { + for _, tc := range []struct { + helmRepoURL string + }{ + {""}, + {"http://some-url.com/"}, + {"address-that-can-be-resolved"}, + } { + r := redpanda.RedpandaReconciler{HelmRepositoryURL: tc.helmRepoURL} + rp := &redpandav1alpha2.Redpanda{ + ObjectMeta: metav1.ObjectMeta{ + Name: "Redpanda-resource", + }, + Spec: redpandav1alpha2.RedpandaSpec{ + ChartRef: redpandav1alpha2.ChartRef{ + ChartVersion: "5.x.x", + }, + ClusterSpec: &redpandav1alpha2.RedpandaClusterSpec{}, + }, + } + repo := r.HelmRepositoryFromTemplate(rp) + assert.Equal(t, tc.helmRepoURL, repo.Spec.URL) + } +} + // TestControllerRBAC asserts that the declared Roles and ClusterRoles of the // RedpandaReconciler line up with all the resource types it needs to manage. func TestControllerRBAC(t *testing.T) { diff --git a/operator/internal/controller/vectorized/test/suite_test.go b/operator/internal/controller/vectorized/test/suite_test.go index 847883202..79baeccca 100644 --- a/operator/internal/controller/vectorized/test/suite_test.go +++ b/operator/internal/controller/vectorized/test/suite_test.go @@ -199,10 +199,11 @@ var _ = BeforeSuite(func(suiteCtx SpecContext) { // Redpanda Reconciler err = (&redpanda.RedpandaReconciler{ - Client: k8sManager.GetClient(), - ClientFactory: internalclient.NewFactory(k8sManager.GetConfig(), k8sManager.GetClient()), - Scheme: k8sManager.GetScheme(), - EventRecorder: k8sManager.GetEventRecorderFor("RedpandaReconciler"), + Client: k8sManager.GetClient(), + ClientFactory: internalclient.NewFactory(k8sManager.GetConfig(), k8sManager.GetClient()), + Scheme: k8sManager.GetScheme(), + EventRecorder: k8sManager.GetEventRecorderFor("RedpandaReconciler"), + HelmRepositoryURL: "https://charts.redpanda.com/", }).SetupWithManager(ctx, k8sManager) Expect(err).ToNot(HaveOccurred())