From d88d3100439b1e756ef9b438a38a22a8fe3ad49a Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Wed, 5 Jun 2024 15:05:51 -0400 Subject: [PATCH] fix: handle multiple imagePullSecrets Signed-off-by: Todd Baert --- .github/scripts/strip-kustomize-helm.sh | 18 ++++++++++ Makefile | 1 + chart/open-feature-operator/README.md | 2 +- chart/open-feature-operator/values.yaml | 4 +-- charts/open-feature-operator-v0.6.0.tgz | Bin 0 -> 27951 bytes common/flagdproxy/flagdproxy.go | 10 +++--- common/flagdproxy/flagdproxy_test.go | 24 ++++++------- config/manager/kustomization.yaml | 2 +- config/overlays/helm/manager.yaml | 6 ++-- .../core/featureflagsource/controller_test.go | 4 +-- controllers/core/flagd/common/common.go | 16 ++++----- controllers/core/flagd/config.go | 4 +-- .../core/flagd/resources/deployment.go | 4 +-- index.yaml | 32 ++++++++++++++++++ main.go | 12 +++---- 15 files changed, 95 insertions(+), 44 deletions(-) create mode 100755 .github/scripts/strip-kustomize-helm.sh create mode 100644 charts/open-feature-operator-v0.6.0.tgz create mode 100644 index.yaml diff --git a/.github/scripts/strip-kustomize-helm.sh b/.github/scripts/strip-kustomize-helm.sh new file mode 100755 index 000000000..dc69fc0b2 --- /dev/null +++ b/.github/scripts/strip-kustomize-helm.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# This script is a hack to support helm flow control in kustomize overlays, which would otherwise break them. +# It allows us to render helm template bindings and add newlines. +# For instance, it transforms "__{{ .Value.myValue }}__" to {{ .Value.myValue }}. +# It also adds newlines wherever __newline__ is found. + +CHARTS_DIR='./chart/open-feature-operator/templates'; + +echo 'Running strip-kustomize-helm.sh script' +filenames=`find $CHARTS_DIR -name "*.yaml"` +for file in $filenames; do + sed -i "s/__newline__/\\n/g" $file + sed -i "s/\"__//g" $file + sed -i "s/__\"//g" $file + sed -i "s/__//g" $file +done +echo 'Done running strip-kustomize-helm.sh script' \ No newline at end of file diff --git a/Makefile b/Makefile index b182407b5..962be12d0 100644 --- a/Makefile +++ b/Makefile @@ -252,6 +252,7 @@ set-helm-overlay: helm-package: set-helm-overlay generate release-manifests helm mkdir -p chart/open-feature-operator/templates/crds mv chart/open-feature-operator/templates/*customresourcedefinition* chart/open-feature-operator/templates/crds + sh .github/scripts/strip-kustomize-helm.sh $(HELM) package --version $(CHART_VERSION) chart/open-feature-operator mkdir -p charts && mv open-feature-operator-*.tgz charts $(HELM) repo index --url https://open-feature.github.io/open-feature-operator/charts charts diff --git a/chart/open-feature-operator/README.md b/chart/open-feature-operator/README.md index 6a5f12a53..382ec8e8b 100644 --- a/chart/open-feature-operator/README.md +++ b/chart/open-feature-operator/README.md @@ -96,7 +96,7 @@ The command removes all the Kubernetes components associated with the chart and | Name | Description | Value | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------ | | `defaultNamespace` | To override the namespace use the `--namespace` flag. This default is provided to ensure that the kustomize build charts in `/templates` deploy correctly when no `namespace` is provided via the `-n` flag. | `open-feature-operator-system` | -| `imagePullSecret` | Secret containing credentials for images pulled by the operator (flagdProxyConfiguration.image, flagdConfiguration.image, controllerManager.manager.image, controllerManager.kubeRbacProxy.image). | `""` | +| `imagePullSecrets` | Secret containing credentials for images pulled by the operator (flagdProxyConfiguration.image, flagdConfiguration.image, controllerManager.manager.image, controllerManager.kubeRbacProxy.image). | `[]` | ### Sidecar configuration diff --git a/chart/open-feature-operator/values.yaml b/chart/open-feature-operator/values.yaml index 822cfc010..f72ff770e 100644 --- a/chart/open-feature-operator/values.yaml +++ b/chart/open-feature-operator/values.yaml @@ -2,8 +2,8 @@ ## @section Global ## @param defaultNamespace To override the namespace use the `--namespace` flag. This default is provided to ensure that the kustomize build charts in `/templates` deploy correctly when no `namespace` is provided via the `-n` flag. defaultNamespace: open-feature-operator-system -## @param imagePullSecret Secret containing credentials for images pulled by the operator (flagdProxyConfiguration.image, flagdConfiguration.image, controllerManager.manager.image, controllerManager.kubeRbacProxy.image). -imagePullSecret: "" +## @param imagePullSecrets Secret containing credentials for images pulled by the operator (flagdProxyConfiguration.image, flagdConfiguration.image, controllerManager.manager.image, controllerManager.kubeRbacProxy.image). +imagePullSecrets: [] ## @section Sidecar configuration sidecarConfiguration: diff --git a/charts/open-feature-operator-v0.6.0.tgz b/charts/open-feature-operator-v0.6.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..c3610f0a0b5c3b24886d94278031b1c35bf6ab70 GIT binary patch literal 27951 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 literal 0 HcmV?d00001 diff --git a/common/flagdproxy/flagdproxy.go b/common/flagdproxy/flagdproxy.go index e1ea4932c..eefdb8abc 100644 --- a/common/flagdproxy/flagdproxy.go +++ b/common/flagdproxy/flagdproxy.go @@ -38,10 +38,10 @@ type FlagdProxyConfiguration struct { Tag string Namespace string OperatorDeploymentName string - ImagePullSecret string + ImagePullSecrets []string } -func NewFlagdProxyConfiguration(env types.EnvConfig, imagePullSecret string) *FlagdProxyConfiguration { +func NewFlagdProxyConfiguration(env types.EnvConfig, imagePullSecrets []string) *FlagdProxyConfiguration { return &FlagdProxyConfiguration{ Image: env.FlagdProxyImage, Tag: env.FlagdProxyTag, @@ -50,7 +50,7 @@ func NewFlagdProxyConfiguration(env types.EnvConfig, imagePullSecret string) *Fl Port: env.FlagdProxyPort, ManagementPort: env.FlagdProxyManagementPort, DebugLogging: env.FlagdProxyDebugLogging, - ImagePullSecret: imagePullSecret, + ImagePullSecrets: imagePullSecrets, } } @@ -146,9 +146,9 @@ func (f *FlagdProxyHandler) newFlagdProxyManifest(ownerReference *metav1.OwnerRe args = append(args, "--debug") } imagePullSecrets := []corev1.LocalObjectReference{} - if f.config.ImagePullSecret != "" { + for _, secret := range f.config.ImagePullSecrets { imagePullSecrets = append(imagePullSecrets, corev1.LocalObjectReference{ - Name: f.config.ImagePullSecret, + Name: secret, }) } diff --git a/common/flagdproxy/flagdproxy_test.go b/common/flagdproxy/flagdproxy_test.go index 112336fc3..ce324b2ed 100644 --- a/common/flagdproxy/flagdproxy_test.go +++ b/common/flagdproxy/flagdproxy_test.go @@ -19,14 +19,14 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" ) -const pullSecret = "test-pullSecret" +var pullSecrets = []string{"test-pullSecret"} func TestNewFlagdProxyConfiguration(t *testing.T) { kpConfig := NewFlagdProxyConfiguration(types.EnvConfig{ FlagdProxyPort: 8015, FlagdProxyManagementPort: 8016, - }, pullSecret) + }, pullSecrets) require.NotNil(t, kpConfig) require.Equal(t, &FlagdProxyConfiguration{ @@ -34,7 +34,7 @@ func TestNewFlagdProxyConfiguration(t *testing.T) { ManagementPort: 8016, DebugLogging: false, OperatorDeploymentName: common.OperatorDeploymentName, - ImagePullSecret: pullSecret, + ImagePullSecrets: pullSecrets, }, kpConfig) } @@ -48,7 +48,7 @@ func TestNewFlagdProxyConfiguration_OverrideEnvVars(t *testing.T) { FlagdProxyDebugLogging: true, } - kpConfig := NewFlagdProxyConfiguration(env, pullSecret) + kpConfig := NewFlagdProxyConfiguration(env, pullSecrets) require.NotNil(t, kpConfig) require.Equal(t, &FlagdProxyConfiguration{ @@ -59,12 +59,12 @@ func TestNewFlagdProxyConfiguration_OverrideEnvVars(t *testing.T) { Tag: "my-tag", Namespace: "my-namespace", OperatorDeploymentName: common.OperatorDeploymentName, - ImagePullSecret: pullSecret, + ImagePullSecrets: pullSecrets, }, kpConfig) } func TestNewFlagdProxyHandler(t *testing.T) { - kpConfig := NewFlagdProxyConfiguration(types.EnvConfig{}, pullSecret) + kpConfig := NewFlagdProxyConfiguration(types.EnvConfig{}, pullSecrets) require.NotNil(t, kpConfig) @@ -100,7 +100,7 @@ func TestDoesFlagdProxyExist(t *testing.T) { }, } - kpConfig := NewFlagdProxyConfiguration(env, pullSecret) + kpConfig := NewFlagdProxyConfiguration(env, pullSecrets) require.NotNil(t, kpConfig) @@ -128,7 +128,7 @@ func TestFlagdProxyHandler_HandleFlagdProxy_ProxyExistsWithBadVersion(t *testing env := types.EnvConfig{ PodNamespace: "ns", } - kpConfig := NewFlagdProxyConfiguration(env, pullSecret) + kpConfig := NewFlagdProxyConfiguration(env, pullSecrets) require.NotNil(t, kpConfig) @@ -187,7 +187,7 @@ func TestFlagdProxyHandler_HandleFlagdProxy_ProxyExistsWithoutLabel(t *testing.T env := types.EnvConfig{ PodNamespace: "ns", } - kpConfig := NewFlagdProxyConfiguration(env, pullSecret) + kpConfig := NewFlagdProxyConfiguration(env, pullSecrets) require.NotNil(t, kpConfig) @@ -236,7 +236,7 @@ func TestFlagdProxyHandler_HandleFlagdProxy_ProxyExistsWithNewestVersion(t *test env := types.EnvConfig{ PodNamespace: "ns", } - kpConfig := NewFlagdProxyConfiguration(env, pullSecret) + kpConfig := NewFlagdProxyConfiguration(env, pullSecrets) require.NotNil(t, kpConfig) @@ -280,7 +280,7 @@ func TestFlagdProxyHandler_HandleFlagdProxy_CreateProxy(t *testing.T) { FlagdProxyManagementPort: 90, FlagdProxyDebugLogging: true, } - kpConfig := NewFlagdProxyConfiguration(env, pullSecret) + kpConfig := NewFlagdProxyConfiguration(env, pullSecrets) require.NotNil(t, kpConfig) @@ -362,7 +362,7 @@ func TestFlagdProxyHandler_HandleFlagdProxy_CreateProxy(t *testing.T) { Spec: corev1.PodSpec{ ServiceAccountName: FlagdProxyServiceAccountName, ImagePullSecrets: []corev1.LocalObjectReference{ - {Name: pullSecret}, + {Name: pullSecrets[0]}, }, Containers: []corev1.Container{ { diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 5679bb554..b72d6557a 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -10,5 +10,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: controller + newName: ghcr.io/openfeature/operator newTag: latest diff --git a/config/overlays/helm/manager.yaml b/config/overlays/helm/manager.yaml index e2c48c051..24d256cf5 100644 --- a/config/overlays/helm/manager.yaml +++ b/config/overlays/helm/manager.yaml @@ -7,8 +7,8 @@ spec: replicas: 0{{ .Values.controllerManager.replicas }} template: spec: - imagePullSecrets: - - name: "{{ .Values.imagePullSecret }}" + # this is transformed by .github/scripts/strip-kustomize-helm.sh + __imagePullSecrets__: "__ __newline__{{ toYaml .Values.imagePullSecrets | indent 8 }}__" containers: - name: manager image: "{{ .Values.controllerManager.manager.image.repository }}:{{ .Values.controllerManager.manager.image.tag }}" @@ -92,7 +92,7 @@ spec: - --sidecar-ram-limit={{ .Values.sidecarConfiguration.resources.limits.memory }} - --sidecar-cpu-request={{ .Values.sidecarConfiguration.resources.requests.cpu }} - --sidecar-ram-request={{ .Values.sidecarConfiguration.resources.requests.memory }} - - --image-pull-secret={{ .Values.imagePullSecret }} + - --image-pull-secret={{ range .Values.imagePullSecrets }}{{ .name }},{{- end }} - name: kube-rbac-proxy image: "{{ .Values.controllerManager.kubeRbacProxy.image.repository }}:{{ .Values.controllerManager.kubeRbacProxy.image.tag }}" resources: diff --git a/controllers/core/featureflagsource/controller_test.go b/controllers/core/featureflagsource/controller_test.go index b1069ef83..e5f9b367e 100644 --- a/controllers/core/featureflagsource/controller_test.go +++ b/controllers/core/featureflagsource/controller_test.go @@ -27,8 +27,8 @@ func TestFeatureFlagSourceReconciler_Reconcile(t *testing.T) { testNamespace = "test-namespace" fsConfigName = "test-config" deploymentName = "test-deploy" - pullSecret = "test-pullsecret" ) + var pullSecrets = []string{"test-pullsecret"} tests := []struct { name string @@ -93,7 +93,7 @@ func TestFeatureFlagSourceReconciler_Reconcile(t *testing.T) { kpConfig := flagdproxy.NewFlagdProxyConfiguration(commontypes.EnvConfig{ FlagdProxyImage: "ghcr.io/open-feature/flagd-proxy", FlagdProxyTag: flagdProxyTag, - }, pullSecret) + }, pullSecrets) kpConfig.Namespace = testNamespace kph := flagdproxy.NewFlagdProxyHandler( diff --git a/controllers/core/flagd/common/common.go b/controllers/core/flagd/common/common.go index b66abe506..fcdd61b74 100644 --- a/controllers/core/flagd/common/common.go +++ b/controllers/core/flagd/common/common.go @@ -1,14 +1,14 @@ package resources type FlagdConfiguration struct { - FlagdPort int - OFREPPort int - SyncPort int - ManagementPort int - DebugLogging bool - Image string - Tag string - ImagePullSecret string + FlagdPort int + OFREPPort int + SyncPort int + ManagementPort int + DebugLogging bool + Image string + Tag string + ImagePullSecrets []string OperatorNamespace string OperatorDeploymentName string diff --git a/controllers/core/flagd/config.go b/controllers/core/flagd/config.go index 624301954..e4ce776b7 100644 --- a/controllers/core/flagd/config.go +++ b/controllers/core/flagd/config.go @@ -6,7 +6,7 @@ import ( resources "github.com/open-feature/open-feature-operator/controllers/core/flagd/common" ) -func NewFlagdConfiguration(env types.EnvConfig, imagePullSecret string) resources.FlagdConfiguration { +func NewFlagdConfiguration(env types.EnvConfig, imagePullSecrets []string) resources.FlagdConfiguration { return resources.FlagdConfiguration{ Image: env.FlagdImage, Tag: env.FlagdTag, @@ -16,6 +16,6 @@ func NewFlagdConfiguration(env types.EnvConfig, imagePullSecret string) resource SyncPort: env.FlagdSyncPort, ManagementPort: env.FlagdManagementPort, DebugLogging: env.FlagdDebugLogging, - ImagePullSecret: imagePullSecret, + ImagePullSecrets: imagePullSecrets, } } diff --git a/controllers/core/flagd/resources/deployment.go b/controllers/core/flagd/resources/deployment.go index dca133c1c..faedb5226 100644 --- a/controllers/core/flagd/resources/deployment.go +++ b/controllers/core/flagd/resources/deployment.go @@ -78,9 +78,9 @@ func (r *FlagdDeployment) GetResource(ctx context.Context, flagd *api.Flagd) (cl featureFlagSource := &api.FeatureFlagSource{} imagePullSecrets := []corev1.LocalObjectReference{} - if r.FlagdConfig.ImagePullSecret != "" { + for _, secret := range r.FlagdConfig.ImagePullSecrets { imagePullSecrets = append(imagePullSecrets, corev1.LocalObjectReference{ - Name: r.FlagdConfig.ImagePullSecret, + Name: secret, }) } diff --git a/index.yaml b/index.yaml new file mode 100644 index 000000000..eed0f38dc --- /dev/null +++ b/index.yaml @@ -0,0 +1,32 @@ +apiVersion: v1 +entries: + open-feature-operator: + - annotations: + artifacthub.io/category: integration-delivery + artifacthub.io/links: | + - name: support + url: https://github.com/open-feature/open-feature-operator/issues + artifacthub.io/operator: "true" + apiVersion: v2 + appVersion: v0.6.0 + created: "2024-06-05T15:06:34.18946423-04:00" + description: A feature flag operator for Kubernetes + digest: 29b466e707892cfbc443377859117d9420c16b6c5deca4f5d8061797cb812622 + home: https://openfeature.dev + icon: https://open-feature.github.io/open-feature-operator/chart/open-feature-operator/openfeature-logo.png + keywords: + - OpenFeature + - feature flags + - feature toggles + - OpenFeature Operator + - open feature + - open feature operator + - OFO + name: open-feature-operator + sources: + - https://github.com/open-feature/open-feature-operator + type: application + urls: + - https://open-feature.github.io/open-feature-operator/charts/open-feature-operator-v0.6.0.tgz + version: v0.6.0 +generated: "2024-06-05T15:06:34.188535112-04:00" diff --git a/main.go b/main.go index db1bd940c..5b69cba0f 100644 --- a/main.go +++ b/main.go @@ -22,6 +22,7 @@ import ( "fmt" "log" "os" + "strings" "github.com/kelseyhightower/envconfig" corev1beta1 "github.com/open-feature/open-feature-operator/apis/core/v1beta1" @@ -66,7 +67,7 @@ const ( sidecarCpuRequestDefault = "0.2" sidecarRamRequestDefault = "32M" imagePullSecretFlagName = "image-pull-secret" - imagePullSecretDefault = "" + imagePullSecretFlagDefault = "" ) var ( @@ -77,7 +78,7 @@ var ( probeAddr string verbose bool sidecarCpuLimit, sidecarRamLimit, sidecarCpuRequest, sidecarRamRequest string - imagePullSecret string + imagePullSecrets string ) func init() { @@ -105,8 +106,7 @@ func main() { flag.StringVar(&sidecarRamLimit, sidecarRamLimitFlagName, sidecarRamLimitDefault, "sidecar memory limit, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024)") flag.StringVar(&sidecarCpuRequest, sidecarCpuRequestFlagName, sidecarCpuRequestDefault, "sidecar CPU minimum, in cores. (500m = .5 cores)") flag.StringVar(&sidecarRamRequest, sidecarRamRequestFlagName, sidecarRamRequestDefault, "sidecar memory minimum, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024)") - - flag.StringVar(&imagePullSecret, imagePullSecretFlagName, imagePullSecretDefault, "secret containing credentials to pull images.") + flag.StringVar(&imagePullSecrets, imagePullSecretFlagName, imagePullSecretFlagDefault, "Secrets containing credentials to pull images.") flag.Parse() @@ -183,7 +183,7 @@ func main() { } kph := flagdproxy.NewFlagdProxyHandler( - flagdproxy.NewFlagdProxyConfiguration(env, imagePullSecret), + flagdproxy.NewFlagdProxyConfiguration(env, strings.Split(imagePullSecrets, ",")), mgr.GetClient(), ctrl.Log.WithName("FeatureFlagSource FlagdProxyHandler"), ) @@ -215,7 +215,7 @@ func main() { Scheme: mgr.GetScheme(), Log: flagdControllerLogger, } - flagdConfig := flagd.NewFlagdConfiguration(env, imagePullSecret) + flagdConfig := flagd.NewFlagdConfiguration(env, strings.Split(imagePullSecrets, ",")) if err = (&flagd.FlagdReconciler{ Client: mgr.GetClient(),