From df4a8920d48fa7f25181bd05da125f848b3983c8 Mon Sep 17 00:00:00 2001 From: THuvaraki Date: Sun, 15 Mar 2026 03:31:03 +0530 Subject: [PATCH 1/3] feat: add flag metadata to FeatureFlag CRD Signed-off-by: THuvaraki --- api/core/v1beta1/featureflag_types.go | 16 ++++++++++++ api/core/v1beta1/zz_generated.deepcopy.go | 25 +++++++++++++++++++ .../core.openfeature.dev_featureflags.yaml | 18 +++++++++++++ 3 files changed, 59 insertions(+) diff --git a/api/core/v1beta1/featureflag_types.go b/api/core/v1beta1/featureflag_types.go index 3c6f332e2..a86878b5f 100644 --- a/api/core/v1beta1/featureflag_types.go +++ b/api/core/v1beta1/featureflag_types.go @@ -32,6 +32,11 @@ type FeatureFlagSpec struct { type FlagSpec struct { Flags `json:",inline"` + // Metadata holds optional flag-level metadata such as flag set identifiers. + // Additional metadata keys are preserved to support flagd metadata inheritance. + // +optional + // +kubebuilder:pruning:PreserveUnknownFields + Metadata *FlagMetadata `json:"metadata,omitempty"` // +optional // +kubebuilder:validation:Schemaless // +kubebuilder:pruning:PreserveUnknownFields @@ -39,6 +44,14 @@ type FlagSpec struct { Evaluators json.RawMessage `json:"$evaluators,omitempty"` } +// FlagMetadata captures optional metadata for a flag specification. +// PreserveUnknownFields keeps compatibility with flagd's metadata inheritance model. +// +kubebuilder:pruning:PreserveUnknownFields +type FlagMetadata struct { + // +optional + FlagSetID string `json:"flagSetId,omitempty"` +} + // Flags represent the flags specification type Flags struct { FlagsMap map[string]Flag `json:"flags"` @@ -47,6 +60,9 @@ type Flags struct { type Flag struct { // +kubebuilder:validation:Enum=ENABLED;DISABLED State string `json:"state"` + // +optional + // +kubebuilder:pruning:PreserveUnknownFields + Metadata *FlagMetadata `json:"metadata,omitempty"` // +kubebuilder:validation:Schemaless // +kubebuilder:pruning:PreserveUnknownFields // +kubebuilder:validation:Type=object diff --git a/api/core/v1beta1/zz_generated.deepcopy.go b/api/core/v1beta1/zz_generated.deepcopy.go index f1e132bbe..156f74370 100644 --- a/api/core/v1beta1/zz_generated.deepcopy.go +++ b/api/core/v1beta1/zz_generated.deepcopy.go @@ -243,6 +243,11 @@ func (in *FeatureFlagStatus) DeepCopy() *FeatureFlagStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Flag) DeepCopyInto(out *Flag) { *out = *in + if in.Metadata != nil { + in, out := &in.Metadata, &out.Metadata + *out = new(FlagMetadata) + **out = **in + } if in.Variants != nil { in, out := &in.Variants, &out.Variants *out = make(json.RawMessage, len(*in)) @@ -265,10 +270,30 @@ func (in *Flag) DeepCopy() *Flag { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FlagMetadata) DeepCopyInto(out *FlagMetadata) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FlagMetadata. +func (in *FlagMetadata) DeepCopy() *FlagMetadata { + if in == nil { + return nil + } + out := new(FlagMetadata) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FlagSpec) DeepCopyInto(out *FlagSpec) { *out = *in in.Flags.DeepCopyInto(&out.Flags) + if in.Metadata != nil { + in, out := &in.Metadata, &out.Metadata + *out = new(FlagMetadata) + **out = **in + } if in.Evaluators != nil { in, out := &in.Evaluators, &out.Evaluators *out = make(json.RawMessage, len(*in)) diff --git a/config/crd/bases/core.openfeature.dev_featureflags.yaml b/config/crd/bases/core.openfeature.dev_featureflags.yaml index 02566fd07..5f27896fc 100644 --- a/config/crd/bases/core.openfeature.dev_featureflags.yaml +++ b/config/crd/bases/core.openfeature.dev_featureflags.yaml @@ -53,6 +53,15 @@ spec: properties: defaultVariant: type: string + metadata: + description: |- + FlagMetadata captures optional metadata for a flag specification. + PreserveUnknownFields keeps compatibility with flagd's metadata inheritance model. + properties: + flagSetId: + type: string + type: object + x-kubernetes-preserve-unknown-fields: true state: enum: - ENABLED @@ -71,6 +80,15 @@ spec: - variants type: object type: object + metadata: + description: |- + Metadata holds optional flag-level metadata such as flag set identifiers. + Additional metadata keys are preserved to support flagd metadata inheritance. + properties: + flagSetId: + type: string + type: object + x-kubernetes-preserve-unknown-fields: true required: - flags type: object From 26148d172ed350d9187197193a7af6eaf7d12cfb Mon Sep 17 00:00:00 2001 From: THuvaraki Date: Sun, 15 Mar 2026 04:05:08 +0530 Subject: [PATCH 2/3] Issues fixed Signed-off-by: THuvaraki --- api/core/v1beta1/featureflag_types.go | 32 +++++++++---------- api/core/v1beta1/zz_generated.deepcopy.go | 29 ++++------------- .../core.openfeature.dev_featureflags.yaml | 12 ++----- config/manager/kustomization.yaml | 4 +-- go.mod | 2 ++ 5 files changed, 29 insertions(+), 50 deletions(-) diff --git a/api/core/v1beta1/featureflag_types.go b/api/core/v1beta1/featureflag_types.go index a86878b5f..e8daecb2c 100644 --- a/api/core/v1beta1/featureflag_types.go +++ b/api/core/v1beta1/featureflag_types.go @@ -32,11 +32,13 @@ type FeatureFlagSpec struct { type FlagSpec struct { Flags `json:",inline"` - // Metadata holds optional flag-level metadata such as flag set identifiers. - // Additional metadata keys are preserved to support flagd metadata inheritance. // +optional + // +kubebuilder:validation:Schemaless // +kubebuilder:pruning:PreserveUnknownFields - Metadata *FlagMetadata `json:"metadata,omitempty"` + // +kubebuilder:validation:Type=object + // Metadata holds optional flag-set level metadata. + // Additional metadata keys are preserved to support flagd metadata inheritance. + Metadata json.RawMessage `json:"metadata,omitempty"` // +optional // +kubebuilder:validation:Schemaless // +kubebuilder:pruning:PreserveUnknownFields @@ -44,14 +46,6 @@ type FlagSpec struct { Evaluators json.RawMessage `json:"$evaluators,omitempty"` } -// FlagMetadata captures optional metadata for a flag specification. -// PreserveUnknownFields keeps compatibility with flagd's metadata inheritance model. -// +kubebuilder:pruning:PreserveUnknownFields -type FlagMetadata struct { - // +optional - FlagSetID string `json:"flagSetId,omitempty"` -} - // Flags represent the flags specification type Flags struct { FlagsMap map[string]Flag `json:"flags"` @@ -60,20 +54,24 @@ type Flags struct { type Flag struct { // +kubebuilder:validation:Enum=ENABLED;DISABLED State string `json:"state"` - // +optional - // +kubebuilder:pruning:PreserveUnknownFields - Metadata *FlagMetadata `json:"metadata,omitempty"` // +kubebuilder:validation:Schemaless // +kubebuilder:pruning:PreserveUnknownFields // +kubebuilder:validation:Type=object - Variants json.RawMessage `json:"variants"` - DefaultVariant string `json:"defaultVariant"` + Variants json.RawMessage `json:"variants"` + DefaultVariant string `json:"defaultVariant"` // +optional // +kubebuilder:validation:Schemaless // +kubebuilder:pruning:PreserveUnknownFields // +kubebuilder:validation:Type=object // Targeting is the json targeting rule Targeting json.RawMessage `json:"targeting,omitempty"` + // +optional + // +kubebuilder:validation:Schemaless + // +kubebuilder:pruning:PreserveUnknownFields + // +kubebuilder:validation:Type=object + // Metadata holds optional per-flag metadata. + // Additional metadata keys are preserved to support flagd metadata inheritance. + Metadata json.RawMessage `json:"metadata,omitempty"` } // FeatureFlagStatus defines the observed state of FeatureFlag @@ -135,4 +133,4 @@ func (ff *FeatureFlag) GenerateConfigMap(name string, namespace string, referenc common.FeatureFlagConfigMapKey(namespace, name): string(b), }, }, nil -} +} \ No newline at end of file diff --git a/api/core/v1beta1/zz_generated.deepcopy.go b/api/core/v1beta1/zz_generated.deepcopy.go index 156f74370..1a48d3a54 100644 --- a/api/core/v1beta1/zz_generated.deepcopy.go +++ b/api/core/v1beta1/zz_generated.deepcopy.go @@ -243,11 +243,6 @@ func (in *FeatureFlagStatus) DeepCopy() *FeatureFlagStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Flag) DeepCopyInto(out *Flag) { *out = *in - if in.Metadata != nil { - in, out := &in.Metadata, &out.Metadata - *out = new(FlagMetadata) - **out = **in - } if in.Variants != nil { in, out := &in.Variants, &out.Variants *out = make(json.RawMessage, len(*in)) @@ -258,6 +253,11 @@ func (in *Flag) DeepCopyInto(out *Flag) { *out = make(json.RawMessage, len(*in)) copy(*out, *in) } + if in.Metadata != nil { + in, out := &in.Metadata, &out.Metadata + *out = make(json.RawMessage, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Flag. @@ -270,29 +270,14 @@ func (in *Flag) DeepCopy() *Flag { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FlagMetadata) DeepCopyInto(out *FlagMetadata) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FlagMetadata. -func (in *FlagMetadata) DeepCopy() *FlagMetadata { - if in == nil { - return nil - } - out := new(FlagMetadata) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FlagSpec) DeepCopyInto(out *FlagSpec) { *out = *in in.Flags.DeepCopyInto(&out.Flags) if in.Metadata != nil { in, out := &in.Metadata, &out.Metadata - *out = new(FlagMetadata) - **out = **in + *out = make(json.RawMessage, len(*in)) + copy(*out, *in) } if in.Evaluators != nil { in, out := &in.Evaluators, &out.Evaluators diff --git a/config/crd/bases/core.openfeature.dev_featureflags.yaml b/config/crd/bases/core.openfeature.dev_featureflags.yaml index 5f27896fc..87527d9ce 100644 --- a/config/crd/bases/core.openfeature.dev_featureflags.yaml +++ b/config/crd/bases/core.openfeature.dev_featureflags.yaml @@ -55,11 +55,8 @@ spec: type: string metadata: description: |- - FlagMetadata captures optional metadata for a flag specification. - PreserveUnknownFields keeps compatibility with flagd's metadata inheritance model. - properties: - flagSetId: - type: string + Metadata holds optional per-flag metadata. + Additional metadata keys are preserved to support flagd metadata inheritance. type: object x-kubernetes-preserve-unknown-fields: true state: @@ -82,11 +79,8 @@ spec: type: object metadata: description: |- - Metadata holds optional flag-level metadata such as flag set identifiers. + Metadata holds optional flag-set level metadata. Additional metadata keys are preserved to support flagd metadata inheritance. - properties: - flagSetId: - type: string type: object x-kubernetes-preserve-unknown-fields: true required: diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 9f7dbf01d..cf9d46bcf 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -14,5 +14,5 @@ configMapGenerator: images: - name: controller - newName: open-feature-operator-local - newTag: validate + newName: ghcr.io/openfeature/operator + newTag: latest diff --git a/go.mod b/go.mod index f0a14b409..4bf509ea8 100644 --- a/go.mod +++ b/go.mod @@ -108,4 +108,6 @@ require ( sigs.k8s.io/yaml v1.4.0 // indirect ) +replace github.com/open-feature/open-feature-operator/apis => ./api + replace golang.org/x/net => golang.org/x/net v0.27.0 From 5ed3246ee0bb75cdc843dc7aa1f0d5a1741c2d12 Mon Sep 17 00:00:00 2001 From: THuvaraki Date: Thu, 19 Mar 2026 10:21:02 +0530 Subject: [PATCH 3/3] remove local dev leftovers Signed-off-by: THuvaraki --- config/manager/kustomization.yaml | 4 ++-- go.mod | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index cf9d46bcf..9f7dbf01d 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -14,5 +14,5 @@ configMapGenerator: images: - name: controller - newName: ghcr.io/openfeature/operator - newTag: latest + newName: open-feature-operator-local + newTag: validate diff --git a/go.mod b/go.mod index 4bf509ea8..f0a14b409 100644 --- a/go.mod +++ b/go.mod @@ -108,6 +108,4 @@ require ( sigs.k8s.io/yaml v1.4.0 // indirect ) -replace github.com/open-feature/open-feature-operator/apis => ./api - replace golang.org/x/net => golang.org/x/net v0.27.0