diff --git a/api/v1alpha1/helmchartproxy_types.go b/api/v1alpha1/helmchartproxy_types.go index 1ed25f86..c1763205 100644 --- a/api/v1alpha1/helmchartproxy_types.go +++ b/api/v1alpha1/helmchartproxy_types.go @@ -26,6 +26,9 @@ const ( // HelmChartProxyFinalizer is the finalizer used by the HelmChartProxy controller to cleanup add-on resources when // a HelmChartProxy is being deleted. HelmChartProxyFinalizer = "helmchartproxy.addons.cluster.x-k8s.io" + + // Default OCI secret key + DefaultOCIKey = "config.json" ) // HelmChartProxySpec defines the desired state of HelmChartProxy. @@ -68,7 +71,7 @@ type HelmChartProxySpec struct { // CredentialsSecretRef is a reference to a Secret containing the OCI credentials. If it is not specified, no Secret will be used. // +optional - CredentialsSecretRef *corev1.SecretReference `json:"credentialsSecretRef,omitempty"` + CredentialsSecretRef *SecretKeyRef `json:"credentialsSecretRef,omitempty"` } type HelmOptions struct { @@ -185,6 +188,17 @@ type HelmUninstallOptions struct { Description string `json:"description,omitempty"` } +type SecretKeyRef struct { + // Name is the name of the Secret containing the OCI credentials. + Name string `json:"name"` + + // Namespace is the namespace of the Secret containing the OCI credentials. + Namespace string `json:"namespace"` + + // Key is the key in the Secret containing the OCI credentials. + Key string `json:"key"` +} + // HelmChartProxyStatus defines the observed state of HelmChartProxy. type HelmChartProxyStatus struct { // Conditions defines current state of the HelmChartProxy. diff --git a/api/v1alpha1/helmreleaseproxy_types.go b/api/v1alpha1/helmreleaseproxy_types.go index 32c13eee..bc6d5aca 100644 --- a/api/v1alpha1/helmreleaseproxy_types.go +++ b/api/v1alpha1/helmreleaseproxy_types.go @@ -73,7 +73,7 @@ type HelmReleaseProxySpec struct { // CredentialsSecretRef is a reference to a Secret containing the OCI credentials. If it is not specified, no Secret will be used. // +optional - CredentialsSecretRef *corev1.SecretReference `json:"credentialsSecretRef,omitempty"` + CredentialsSecretRef *SecretKeyRef `json:"credentialsSecretRef,omitempty"` } // HelmReleaseProxyStatus defines the observed state of HelmReleaseProxy. diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index e8b7a187..9dcb337d 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -22,8 +22,8 @@ limitations under the License. package v1alpha1 import ( - "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/cluster-api/api/v1beta1" ) @@ -98,7 +98,7 @@ func (in *HelmChartProxySpec) DeepCopyInto(out *HelmChartProxySpec) { } if in.CredentialsSecretRef != nil { in, out := &in.CredentialsSecretRef, &out.CredentialsSecretRef - *out = new(v1.SecretReference) + *out = new(SecretKeyRef) **out = **in } } @@ -125,7 +125,7 @@ func (in *HelmChartProxyStatus) DeepCopyInto(out *HelmChartProxyStatus) { } if in.MatchingClusters != nil { in, out := &in.MatchingClusters, &out.MatchingClusters - *out = make([]v1.ObjectReference, len(*in)) + *out = make([]corev1.ObjectReference, len(*in)) copy(*out, *in) } } @@ -165,7 +165,7 @@ func (in *HelmOptions) DeepCopyInto(out *HelmOptions) { *out = *in if in.Timeout != nil { in, out := &in.Timeout, &out.Timeout - *out = new(metav1.Duration) + *out = new(v1.Duration) **out = **in } if in.Install != nil { @@ -270,7 +270,7 @@ func (in *HelmReleaseProxySpec) DeepCopyInto(out *HelmReleaseProxySpec) { } if in.CredentialsSecretRef != nil { in, out := &in.CredentialsSecretRef, &out.CredentialsSecretRef - *out = new(v1.SecretReference) + *out = new(SecretKeyRef) **out = **in } } @@ -336,3 +336,18 @@ func (in *HelmUpgradeOptions) DeepCopy() *HelmUpgradeOptions { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretKeyRef) DeepCopyInto(out *SecretKeyRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretKeyRef. +func (in *SecretKeyRef) DeepCopy() *SecretKeyRef { + if in == nil { + return nil + } + out := new(SecretKeyRef) + in.DeepCopyInto(out) + return out +} diff --git a/config/crd/bases/addons.cluster.x-k8s.io_helmchartproxies.yaml b/config/crd/bases/addons.cluster.x-k8s.io_helmchartproxies.yaml index 633d3ce2..ae57cdcf 100644 --- a/config/crd/bases/addons.cluster.x-k8s.io_helmchartproxies.yaml +++ b/config/crd/bases/addons.cluster.x-k8s.io_helmchartproxies.yaml @@ -105,16 +105,22 @@ spec: description: CredentialsSecretRef is a reference to a Secret containing the OCI credentials. If it is not specified, no Secret will be used. properties: + key: + description: Key is the key in the Secret containing the OCI credentials. + type: string name: - description: name is unique within a namespace to reference a - secret resource. + description: Name is the name of the Secret containing the OCI + credentials. type: string namespace: - description: namespace defines the space within which the secret - name must be unique. + description: Namespace is the namespace of the Secret containing + the OCI credentials. type: string + required: + - key + - name + - namespace type: object - x-kubernetes-map-type: atomic namespace: description: ReleaseNamespace is the namespace the Helm release will be installed on each selected Cluster. If it is not specified, it diff --git a/config/crd/bases/addons.cluster.x-k8s.io_helmreleaseproxies.yaml b/config/crd/bases/addons.cluster.x-k8s.io_helmreleaseproxies.yaml index 5f1e2895..e84eb9ae 100644 --- a/config/crd/bases/addons.cluster.x-k8s.io_helmreleaseproxies.yaml +++ b/config/crd/bases/addons.cluster.x-k8s.io_helmreleaseproxies.yaml @@ -105,16 +105,22 @@ spec: description: CredentialsSecretRef is a reference to a Secret containing the OCI credentials. If it is not specified, no Secret will be used. properties: + key: + description: Key is the key in the Secret containing the OCI credentials. + type: string name: - description: name is unique within a namespace to reference a - secret resource. + description: Name is the name of the Secret containing the OCI + credentials. type: string namespace: - description: namespace defines the space within which the secret - name must be unique. + description: Namespace is the namespace of the Secret containing + the OCI credentials. type: string + required: + - key + - name + - namespace type: object - x-kubernetes-map-type: atomic namespace: description: ReleaseNamespace is the namespace the Helm release will be installed on the referenced Cluster. If it is not specified, diff --git a/controllers/helmchartproxy/helmchartproxy_controller_phases.go b/controllers/helmchartproxy/helmchartproxy_controller_phases.go index 6a5e1586..e9f8ece6 100644 --- a/controllers/helmchartproxy/helmchartproxy_controller_phases.go +++ b/controllers/helmchartproxy/helmchartproxy_controller_phases.go @@ -217,6 +217,18 @@ func constructHelmReleaseProxy(existing *addonsv1alpha1.HelmReleaseProxy, helmCh helmReleaseProxy.Spec.Options = helmChartProxy.Spec.Options helmReleaseProxy.Spec.CredentialsSecretRef = helmChartProxy.Spec.CredentialsSecretRef + if helmReleaseProxy.Spec.CredentialsSecretRef != nil { + // If the namespace is not set, set it to the namespace of the HelmChartProxy + if helmReleaseProxy.Spec.CredentialsSecretRef.Namespace == "" { + helmReleaseProxy.Spec.CredentialsSecretRef.Namespace = helmChartProxy.Namespace + } + + // If the key is not set, set it to the default key + if helmReleaseProxy.Spec.CredentialsSecretRef.Key == "" { + helmReleaseProxy.Spec.CredentialsSecretRef.Key = addonsv1alpha1.DefaultOCIKey + } + } + // Set the default value for EnableClientCache if it is not set if helmReleaseProxy.Spec.Options.EnableClientCache == nil { helmReleaseProxy.Spec.Options.EnableClientCache = &defaultEnableClientCache diff --git a/controllers/helmchartproxy/helmchartproxy_controller_phases_test.go b/controllers/helmchartproxy/helmchartproxy_controller_phases_test.go index 09e082ad..2020c5e5 100644 --- a/controllers/helmchartproxy/helmchartproxy_controller_phases_test.go +++ b/controllers/helmchartproxy/helmchartproxy_controller_phases_test.go @@ -787,6 +787,227 @@ func TestConstructHelmReleaseProxy(t *testing.T) { }, }, }, + { + name: "construct helm release proxy with secret", + existing: nil, + helmChartProxy: &addonsv1alpha1.HelmChartProxy{ + TypeMeta: metav1.TypeMeta{ + APIVersion: addonsv1alpha1.GroupVersion.String(), + Kind: "HelmChartProxy", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-hcp", + Namespace: "test-namespace", + }, + Spec: addonsv1alpha1.HelmChartProxySpec{ + ReleaseName: "test-release-name", + ChartName: "test-chart-name", + RepoURL: "https://test-repo-url", + ReleaseNamespace: "test-release-namespace", + Options: &addonsv1alpha1.HelmOptions{}, + CredentialsSecretRef: &addonsv1alpha1.SecretKeyRef{ + Name: "test-secret", + }, + }, + }, + parsedValues: "test-parsed-values", + cluster: &clusterv1.Cluster{ + TypeMeta: metav1.TypeMeta{ + APIVersion: clusterv1.GroupVersion.String(), + Kind: "Cluster", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + }, + expected: &addonsv1alpha1.HelmReleaseProxy{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "test-chart-name-test-cluster-", + Namespace: "test-namespace", + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: addonsv1alpha1.GroupVersion.String(), + Kind: "HelmChartProxy", + Name: "test-hcp", + Controller: pointer.Bool(true), + BlockOwnerDeletion: pointer.Bool(true), + }, + }, + Labels: map[string]string{ + clusterv1.ClusterNameLabel: "test-cluster", + addonsv1alpha1.HelmChartProxyLabelName: "test-hcp", + }, + }, + Spec: addonsv1alpha1.HelmReleaseProxySpec{ + ClusterRef: corev1.ObjectReference{ + APIVersion: clusterv1.GroupVersion.String(), + Kind: "Cluster", + Name: "test-cluster", + }, + ReleaseName: "test-release-name", + ChartName: "test-chart-name", + RepoURL: "https://test-repo-url", + ReleaseNamespace: "test-release-namespace", + Values: "test-parsed-values", + Options: &addonsv1alpha1.HelmOptions{ + EnableClientCache: pointer.Bool(true), + }, + CredentialsSecretRef: &addonsv1alpha1.SecretKeyRef{ + Name: "test-secret", + Namespace: "test-namespace", + Key: "config.json", + }, + }, + }, + }, + { + name: "construct helm release proxy with secret and custom namespace", + existing: nil, + helmChartProxy: &addonsv1alpha1.HelmChartProxy{ + TypeMeta: metav1.TypeMeta{ + APIVersion: addonsv1alpha1.GroupVersion.String(), + Kind: "HelmChartProxy", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-hcp", + Namespace: "test-namespace", + }, + Spec: addonsv1alpha1.HelmChartProxySpec{ + ReleaseName: "test-release-name", + ChartName: "test-chart-name", + RepoURL: "https://test-repo-url", + ReleaseNamespace: "test-release-namespace", + Options: &addonsv1alpha1.HelmOptions{}, + CredentialsSecretRef: &addonsv1alpha1.SecretKeyRef{ + Name: "test-secret", + Namespace: "my-namespace", + }, + }, + }, + parsedValues: "test-parsed-values", + cluster: &clusterv1.Cluster{ + TypeMeta: metav1.TypeMeta{ + APIVersion: clusterv1.GroupVersion.String(), + Kind: "Cluster", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + }, + expected: &addonsv1alpha1.HelmReleaseProxy{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "test-chart-name-test-cluster-", + Namespace: "test-namespace", + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: addonsv1alpha1.GroupVersion.String(), + Kind: "HelmChartProxy", + Name: "test-hcp", + Controller: pointer.Bool(true), + BlockOwnerDeletion: pointer.Bool(true), + }, + }, + Labels: map[string]string{ + clusterv1.ClusterNameLabel: "test-cluster", + addonsv1alpha1.HelmChartProxyLabelName: "test-hcp", + }, + }, + Spec: addonsv1alpha1.HelmReleaseProxySpec{ + ClusterRef: corev1.ObjectReference{ + APIVersion: clusterv1.GroupVersion.String(), + Kind: "Cluster", + Name: "test-cluster", + }, + ReleaseName: "test-release-name", + ChartName: "test-chart-name", + RepoURL: "https://test-repo-url", + ReleaseNamespace: "test-release-namespace", + Values: "test-parsed-values", + Options: &addonsv1alpha1.HelmOptions{ + EnableClientCache: pointer.Bool(true), + }, + CredentialsSecretRef: &addonsv1alpha1.SecretKeyRef{ + Name: "test-secret", + Namespace: "my-namespace", + Key: "config.json", + }, + }, + }, + }, + { + name: "construct helm release proxy with secret and custom key", + existing: nil, + helmChartProxy: &addonsv1alpha1.HelmChartProxy{ + TypeMeta: metav1.TypeMeta{ + APIVersion: addonsv1alpha1.GroupVersion.String(), + Kind: "HelmChartProxy", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-hcp", + Namespace: "test-namespace", + }, + Spec: addonsv1alpha1.HelmChartProxySpec{ + ReleaseName: "test-release-name", + ChartName: "test-chart-name", + RepoURL: "https://test-repo-url", + ReleaseNamespace: "test-release-namespace", + Options: &addonsv1alpha1.HelmOptions{}, + CredentialsSecretRef: &addonsv1alpha1.SecretKeyRef{ + Name: "test-secret", + Key: "custom-key.json", + }, + }, + }, + parsedValues: "test-parsed-values", + cluster: &clusterv1.Cluster{ + TypeMeta: metav1.TypeMeta{ + APIVersion: clusterv1.GroupVersion.String(), + Kind: "Cluster", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + }, + expected: &addonsv1alpha1.HelmReleaseProxy{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "test-chart-name-test-cluster-", + Namespace: "test-namespace", + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: addonsv1alpha1.GroupVersion.String(), + Kind: "HelmChartProxy", + Name: "test-hcp", + Controller: pointer.Bool(true), + BlockOwnerDeletion: pointer.Bool(true), + }, + }, + Labels: map[string]string{ + clusterv1.ClusterNameLabel: "test-cluster", + addonsv1alpha1.HelmChartProxyLabelName: "test-hcp", + }, + }, + Spec: addonsv1alpha1.HelmReleaseProxySpec{ + ClusterRef: corev1.ObjectReference{ + APIVersion: clusterv1.GroupVersion.String(), + Kind: "Cluster", + Name: "test-cluster", + }, + ReleaseName: "test-release-name", + ChartName: "test-chart-name", + RepoURL: "https://test-repo-url", + ReleaseNamespace: "test-release-namespace", + Values: "test-parsed-values", + Options: &addonsv1alpha1.HelmOptions{ + EnableClientCache: pointer.Bool(true), + }, + CredentialsSecretRef: &addonsv1alpha1.SecretKeyRef{ + Name: "test-secret", + Namespace: "test-namespace", + Key: "custom-key.json", + }, + }, + }, + }, } for _, tc := range testCases { diff --git a/controllers/helmreleaseproxy/helmreleaseproxy_controller.go b/controllers/helmreleaseproxy/helmreleaseproxy_controller.go index 5a29826b..9276f501 100644 --- a/controllers/helmreleaseproxy/helmreleaseproxy_controller.go +++ b/controllers/helmreleaseproxy/helmreleaseproxy_controller.go @@ -351,7 +351,7 @@ func patchHelmReleaseProxy(ctx context.Context, patchHelper *patch.Helper, helmR func (r *HelmReleaseProxyReconciler) GetCredentials(ctx context.Context, helmReleaseProxy *addonsv1alpha1.HelmReleaseProxy) (string, error) { credentialsPath := "" if helmReleaseProxy.Spec.CredentialsSecretRef != nil { - credentialsValues, err := r.getCredentialsFromSecret(ctx, helmReleaseProxy.Spec.CredentialsSecretRef.Name, helmReleaseProxy.Spec.CredentialsSecretRef.Namespace) + credentialsValues, err := r.getCredentialsFromSecret(ctx, helmReleaseProxy.Spec.CredentialsSecretRef.Name, helmReleaseProxy.Spec.CredentialsSecretRef.Namespace, helmReleaseProxy.Spec.CredentialsSecretRef.Key) if err != nil { return "", err } @@ -369,15 +369,15 @@ func (r *HelmReleaseProxyReconciler) GetCredentials(ctx context.Context, helmRel } // getCredentialsFromSecret returns the OCI credentials from a Secret -func (r *HelmReleaseProxyReconciler) getCredentialsFromSecret(ctx context.Context, name, namespace string) ([]byte, error) { +func (r *HelmReleaseProxyReconciler) getCredentialsFromSecret(ctx context.Context, name, namespace, key string) ([]byte, error) { secret := &corev1.Secret{} if err := r.Client.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, secret); err != nil { return nil, err } - credentials, ok := secret.Data["config.json"] + credentials, ok := secret.Data[key] if !ok { - return nil, errors.New("config.json not found in secret") + return nil, errors.New(fmt.Sprintf("key %s not found in secret %s/%s", key, namespace, name)) } return credentials, nil diff --git a/controllers/helmreleaseproxy/helmreleaseproxy_controller_test.go b/controllers/helmreleaseproxy/helmreleaseproxy_controller_test.go index 0dccfda7..ae03cc0f 100644 --- a/controllers/helmreleaseproxy/helmreleaseproxy_controller_test.go +++ b/controllers/helmreleaseproxy/helmreleaseproxy_controller_test.go @@ -84,7 +84,7 @@ var ( ReleaseName: "test-release", ReleaseNamespace: "default", Values: "test-values", - CredentialsSecretRef: &corev1.SecretReference{ + CredentialsSecretRef: &addonsv1alpha1.SecretKeyRef{ Name: "test-secret", Namespace: "default", },