@@ -4,12 +4,12 @@ import (
44 "context"
55 "encoding/json"
66 "fmt"
7- "net/http"
8-
97 "github.com/go-logr/logr"
10- corev1alpha1 "github.com/open-feature/open-feature-operator/apis/core/v1alpha1"
8+ configv1alpha1 "github.com/open-feature/open-feature-operator/apis/core/v1alpha1"
119 corev1 "k8s.io/api/core/v1"
10+ "k8s.io/apimachinery/pkg/api/errors"
1211 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+ "net/http"
1313 "sigs.k8s.io/controller-runtime/pkg/client"
1414 "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
1515)
@@ -42,66 +42,107 @@ func (m *PodMutator) Handle(ctx context.Context, req admission.Request) admissio
4242 return admission .Allowed ("openfeature is disabled" )
4343 }
4444 }
45- // Check if the pod is static or orphaned
46- name := pod .Name
47- if len (pod .GetOwnerReferences ()) != 0 {
48- name = pod .GetOwnerReferences ()[0 ].Name
49- } else {
50- return admission .Denied ("static or orphaned pods cannot be mutated" )
51- }
5245
53- var featureFlagCustomResource corev1alpha1.FeatureFlagConfiguration
54- // Check CustomResource
46+ // Check configuration
5547 val , ok = pod .GetAnnotations ()["openfeature.dev/featureflagconfiguration" ]
5648 if ! ok {
5749 return admission .Allowed ("FeatureFlagConfiguration not found" )
58- } else {
59- // Current limitation is to use the same namespace, this is easy to fix though
60- // e.g. namespace/name check
61- err = m .Client .Get (context .TODO (), client.ObjectKey {Name : val ,
62- Namespace : req .Namespace },
63- & featureFlagCustomResource )
50+ }
51+
52+ // Check if the pod is static or orphaned
53+ if len (pod .GetOwnerReferences ()) == 0 {
54+ return admission .Denied ("static or orphaned pods cannot be mutated" )
55+ }
56+
57+ // Check for ConfigMap and create it if it doesn't exist
58+ cm := corev1.ConfigMap {}
59+ if err := m .Client .Get (ctx , client.ObjectKey {Name : val , Namespace : req .Namespace }, & cm ); errors .IsNotFound (err ) {
60+ err := m .CreateConfigMap (ctx , val , req .Namespace , pod )
6461 if err != nil {
65- return admission .Denied ("FeatureFlagConfiguration not found" )
62+ m .Log .V (1 ).Info (fmt .Sprintf ("failed to create config map %s error: %s" , val , err .Error ()))
63+ return admission .Errored (http .StatusInternalServerError , err )
6664 }
6765 }
68- // TODO: this should be a short sha to avoid collisions
69- configName := name
70- // Create the agent configmap
71- m .Client .Delete (context .TODO (), & corev1.ConfigMap {
72- ObjectMeta : metav1.ObjectMeta {
73- Name : configName ,
74- Namespace : req .Namespace ,
75- },
76- }) // Delete the configmap if it exists
7766
78- m .Log .V (1 ).Info (fmt .Sprintf ("Creating configmap %s" , configName ))
79- if err := m .Client .Create (ctx , & corev1.ConfigMap {
67+ if ! CheckOwnerReference (pod , cm ) {
68+ reference := pod .OwnerReferences [0 ]
69+ reference .Controller = m .falseVal ()
70+ cm .OwnerReferences = append (cm .OwnerReferences , reference )
71+ err := m .Client .Update (ctx , & cm )
72+ if err != nil {
73+ m .Log .V (1 ).Info (fmt .Sprintf ("failed to update owner reference for %s error: %s" , val , err .Error ()))
74+ }
75+ }
76+
77+ marshaledPod , err := m .InjectSidecar (pod , val )
78+ if err != nil {
79+ return admission .Errored (http .StatusInternalServerError , err )
80+ }
81+
82+ return admission .PatchResponseFromRaw (req .Object .Raw , marshaledPod )
83+ }
84+
85+ // PodMutator implements admission.DecoderInjector.
86+ // A decoder will be automatically injected.
87+
88+ // InjectDecoder injects the decoder.
89+ func (m * PodMutator ) InjectDecoder (d * admission.Decoder ) error {
90+ m .decoder = d
91+ return nil
92+ }
93+
94+ func CheckOwnerReference (pod * corev1.Pod , cm corev1.ConfigMap ) bool {
95+ for _ , cmOwner := range cm .OwnerReferences {
96+ for _ , podOwner := range pod .OwnerReferences {
97+ if cmOwner == podOwner {
98+ return true
99+ }
100+ }
101+ }
102+ return false
103+ }
104+
105+ func (m * PodMutator ) CreateConfigMap (ctx context.Context , name string , namespace string , pod * corev1.Pod ) error {
106+ m .Log .V (1 ).Info (fmt .Sprintf ("Creating configmap %s" , name ))
107+ reference := pod .OwnerReferences [0 ]
108+ reference .Controller = m .falseVal ()
109+
110+ spec := m .GetFeatureFlagSpec (ctx , name , namespace )
111+ cm := corev1.ConfigMap {
80112 ObjectMeta : metav1.ObjectMeta {
81- Name : configName ,
82- Namespace : req . Namespace ,
113+ Name : name ,
114+ Namespace : namespace ,
83115 Annotations : map [string ]string {
84- "openfeature.dev/featureflagconfiguration" : featureFlagCustomResource .Name ,
116+ "openfeature.dev/featureflagconfiguration" : name ,
117+ },
118+ OwnerReferences : []metav1.OwnerReference {
119+ reference ,
85120 },
86121 },
87- //TODO
88122 Data : map [string ]string {
89- "config.yaml" : featureFlagCustomResource . Spec .FeatureFlagSpec ,
123+ "config.yaml" : spec .FeatureFlagSpec ,
90124 },
91- }); err != nil {
125+ }
126+ return m .Client .Create (ctx , & cm )
127+ }
92128
93- m .Log .V (1 ).Info (fmt .Sprintf ("failed to create config map %s error: %s" , configName , err .Error ()))
94- return admission .Errored (http .StatusInternalServerError , err )
129+ func (m * PodMutator ) GetFeatureFlagSpec (ctx context.Context , name string , namespace string ) configv1alpha1.FeatureFlagConfigurationSpec {
130+ ffConfig := configv1alpha1.FeatureFlagConfiguration {}
131+ if err := m .Client .Get (ctx , client.ObjectKey {Name : name , Namespace : namespace }, & ffConfig ); errors .IsNotFound (err ) {
132+ return configv1alpha1.FeatureFlagConfigurationSpec {}
95133 }
134+ return ffConfig .Spec
135+ }
96136
137+ func (m * PodMutator ) InjectSidecar (pod * corev1.Pod , configMap string ) ([]byte , error ) {
97138 m .Log .V (1 ).Info (fmt .Sprintf ("Creating sidecar for pod %s/%s" , pod .Namespace , pod .Name ))
98139 // Inject the agent
99140 pod .Spec .Volumes = append (pod .Spec .Volumes , corev1.Volume {
100141 Name : "flagd-config" ,
101142 VolumeSource : corev1.VolumeSource {
102143 ConfigMap : & corev1.ConfigMapVolumeSource {
103144 LocalObjectReference : corev1.LocalObjectReference {
104- Name : configName ,
145+ Name : configMap ,
105146 },
106147 },
107148 },
@@ -119,20 +160,10 @@ func (m *PodMutator) Handle(ctx context.Context, req admission.Request) admissio
119160 },
120161 },
121162 })
122-
123- marshaledPod , err := json .Marshal (pod )
124- if err != nil {
125- return admission .Errored (http .StatusInternalServerError , err )
126- }
127-
128- return admission .PatchResponseFromRaw (req .Object .Raw , marshaledPod )
163+ return json .Marshal (pod )
129164}
130165
131- // PodMutator implements admission.DecoderInjector.
132- // A decoder will be automatically injected.
133-
134- // InjectDecoder injects the decoder.
135- func (m * PodMutator ) InjectDecoder (d * admission.Decoder ) error {
136- m .decoder = d
137- return nil
166+ func (m * PodMutator ) falseVal () * bool {
167+ b := false
168+ return & b
138169}
0 commit comments