diff --git a/api/v1alpha1/types.go b/api/v1alpha1/types.go index a537b1a9..c8aba559 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 diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index c5c0804e..9c038154 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -337,6 +337,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..29aaeb91 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 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/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,