diff --git a/api/v1alpha1/types.go b/api/v1alpha1/types.go index a537b1a9..4b2a05d6 100644 --- a/api/v1alpha1/types.go +++ b/api/v1alpha1/types.go @@ -32,6 +32,9 @@ type ValkeySpec struct { Binding *BindingProperties `json:"binding,omitempty"` ExtraEnvVars []corev1.EnvVar `json:"extraEnvVars,omitempty"` ExtraFlags []string `json:"extraFlags,omitempty"` + // Additional annotations added to the generated workload metadata. + // +optional + Annotations map[string]string `json:"annotations,omitempty"` } // SentinelProperties models attributes of the sentinel sidecar @@ -69,6 +72,9 @@ type MetricsPrometheusRuleProperties struct { type TLSProperties struct { Enabled bool `json:"enabled,omitempty"` CertManager *CertManagerProperties `json:"certManager,omitempty"` + // Duration of the issued certificate. Defaults to 876000h (~100 years) if not set. + // +optional + Duration *metav1.Duration `json:"duration,omitempty"` } // CertManagerProperties models cert-manager related attributes diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index c5c0804e..b8a10ac2 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -12,6 +12,7 @@ package v1alpha1 import ( monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -217,6 +218,11 @@ func (in *TLSProperties) DeepCopyInto(out *TLSProperties) { *out = new(CertManagerProperties) (*in).DeepCopyInto(*out) } + if in.Duration != nil { + in, out := &in.Duration, &out.Duration + *out = new(metav1.Duration) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSProperties. @@ -337,6 +343,13 @@ func (in *ValkeySpec) DeepCopyInto(out *ValkeySpec) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ValkeySpec. diff --git a/crds/cache.cs.sap.com_valkeys.yaml b/crds/cache.cs.sap.com_valkeys.yaml index 592b216f..492a84cd 100644 --- a/crds/cache.cs.sap.com_valkeys.yaml +++ b/crds/cache.cs.sap.com_valkeys.yaml @@ -958,6 +958,12 @@ spec: x-kubernetes-list-type: atomic type: object type: object + annotations: + additionalProperties: + type: string + description: Additional annotations added to the generated workload + metadata. + type: object binding: description: BindingProperties models custom properties for the generated binding secret @@ -6036,6 +6042,10 @@ spec: type: string type: object type: object + duration: + description: Duration of the issued certificate. Defaults to 876000h + (~100 years) if not set. + type: string enabled: type: boolean type: object diff --git a/internal/transformer/annotation_generator.go b/internal/transformer/annotation_generator.go new file mode 100644 index 00000000..913c016b --- /dev/null +++ b/internal/transformer/annotation_generator.go @@ -0,0 +1,64 @@ +/* +SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and valkey-operator contributors +SPDX-License-Identifier: Apache-2.0 +*/ + +package transformer + +import ( + "context" + + "github.com/sap/component-operator-runtime/pkg/manifests" + componentoperatorruntimetypes "github.com/sap/component-operator-runtime/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// annotationGenerator wraps a generator and merges the component's spec.annotations +// onto the metadata of every generated StatefulSet. It is applied in the operator +// (not the vendored chart) so the behaviour is independent of the chart templates. +type annotationGenerator struct { + generator manifests.Generator +} + +func NewAnnotationGenerator(generator manifests.Generator) *annotationGenerator { + return &annotationGenerator{generator: generator} +} + +func (g *annotationGenerator) Generate(ctx context.Context, namespace string, name string, parameters componentoperatorruntimetypes.Unstructurable) ([]client.Object, error) { + objects, err := g.generator.Generate(ctx, namespace, name, parameters) + if err != nil { + return nil, err + } + annotations := annotationsFromParameters(parameters) + if len(annotations) == 0 { + return objects, nil + } + for i := 0; i < len(objects); i++ { + if statefulSet := asStatefulSet(objects[i]); statefulSet != nil { + merged := statefulSet.GetAnnotations() + if merged == nil { + merged = make(map[string]string, len(annotations)) + } + for key, value := range annotations { + merged[key] = value + } + statefulSet.SetAnnotations(merged) + objects[i] = asUnstructurable(statefulSet) + } + } + return objects, nil +} + +func annotationsFromParameters(parameters componentoperatorruntimetypes.Unstructurable) map[string]string { + raw, ok := parameters.ToUnstructured()["annotations"].(map[string]any) + if !ok { + return nil + } + annotations := make(map[string]string, len(raw)) + for key, value := range raw { + if s, ok := value.(string); ok { + annotations[key] = s + } + } + return annotations +} diff --git a/pkg/operator/data/parameters.yaml b/pkg/operator/data/parameters.yaml index 1665b162..999ea975 100644 --- a/pkg/operator/data/parameters.yaml +++ b/pkg/operator/data/parameters.yaml @@ -289,7 +289,7 @@ extraDeploy: {{- else }} name: {{ $fullname }}-selfsigned {{- end }} - duration: 876000h + duration: {{ .tls.duration | default "876000h" }} {{- if not .tls.certManager.issuer }} - apiVersion: cert-manager.io/v1 kind: Issuer diff --git a/pkg/operator/operator.go b/pkg/operator/operator.go index 7cbdfc9e..e7465100 100644 --- a/pkg/operator/operator.go +++ b/pkg/operator/operator.go @@ -122,7 +122,7 @@ func (o *Operator) Setup(mgr ctrl.Manager) error { if err := component.NewReconciler[*operatorv1alpha1.Valkey]( o.options.Name, - resourceGenerator, + transformer.NewAnnotationGenerator(resourceGenerator), component.ReconcilerOptions{}, ).WithPostReconcileHook( reconcileBinding,