Skip to content

Commit 31b5f7b

Browse files
feat: flagd proxy resource ownership (#442)
Signed-off-by: James Milligan <[email protected]>
1 parent 4cbe4f1 commit 31b5f7b

File tree

9 files changed

+260
-233
lines changed

9 files changed

+260
-233
lines changed

config/rbac/kustomization.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ resources:
44
# if your manager will use a service account that exists at
55
# runtime. Be sure to update RoleBinding and ClusterRoleBinding
66
# subjects if changing service account names.
7-
- service_account_kube_flagd_proxy.yaml
7+
- service_account_flagd_proxy.yaml
88
- service_account_manager.yaml
99
- role.yaml
1010
- role_binding.yaml

controllers/core/flagsourceconfiguration/controller.go

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -35,25 +35,6 @@ import (
3535
corev1alpha1 "github.com/open-feature/open-feature-operator/apis/core/v1alpha1"
3636
)
3737

38-
const (
39-
FlagdProxyDeploymentName = "flagd-proxy"
40-
FlagdProxyServiceAccountName = "open-feature-operator-flagd-proxy"
41-
FlagdProxyServiceName = "flagd-proxy-svc"
42-
43-
envVarPodNamespace = "POD_NAMESPACE"
44-
envVarProxyImage = "FLAGD_PROXY_IMAGE"
45-
envVarProxyTag = "FLAGD_PROXY_TAG"
46-
envVarProxyPort = "FLAGD_PROXY_PORT"
47-
envVarProxyMetricsPort = "FLAGD_PROXY_METRICS_PORT"
48-
envVarProxyDebugLogging = "FLAGD_PROXY_DEBUG_LOGGING"
49-
defaultFlagdProxyImage = "ghcr.io/open-feature/flagd-proxy"
50-
defaultFlagdProxyTag = "v0.2.1" //FLAGD_PROXY_TAG_RENOVATE
51-
defaultFlagdProxyPort = 8015
52-
defaultFlagdProxyMetricsPort = 8016
53-
defaultFlagdProxyDebugLogging = false
54-
defaultFlagdProxyNamespace = "open-feature-operator-system"
55-
)
56-
5738
// FlagSourceConfigurationReconciler reconciles a FlagSourceConfiguration object
5839
type FlagSourceConfigurationReconciler struct {
5940
client.Client
@@ -92,7 +73,7 @@ func (r *FlagSourceConfigurationReconciler) Reconcile(ctx context.Context, req c
9273
for _, source := range fsConfig.Spec.Sources {
9374
if source.Provider.IsFlagdProxy() {
9475
r.Log.Info(fmt.Sprintf("flagsourceconfiguration %s uses flagd-proxy, checking deployment", req.NamespacedName))
95-
if err := r.FlagdProxy.handleFlagdProxy(ctx); err != nil {
76+
if err := r.FlagdProxy.handleFlagdProxy(ctx, fsConfig); err != nil {
9677
r.Log.Error(err, "error handling the flagd-proxy deployment")
9778
}
9879
break
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
package flagsourceconfiguration
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
8+
"github.com/go-logr/logr"
9+
corev1alpha1 "github.com/open-feature/open-feature-operator/apis/core/v1alpha1"
10+
"github.com/open-feature/open-feature-operator/pkg/utils"
11+
appsV1 "k8s.io/api/apps/v1"
12+
corev1 "k8s.io/api/core/v1"
13+
"k8s.io/apimachinery/pkg/api/errors"
14+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15+
"k8s.io/apimachinery/pkg/util/intstr"
16+
"sigs.k8s.io/controller-runtime/pkg/client"
17+
)
18+
19+
const (
20+
ManagedByAnnotationValue = "open-feature-operator"
21+
FlagdProxyDeploymentName = "flagd-proxy"
22+
FlagdProxyServiceAccountName = "open-feature-operator-flagd-proxy"
23+
FlagdProxyServiceName = "flagd-proxy-svc"
24+
25+
envVarPodNamespace = "POD_NAMESPACE"
26+
envVarProxyImage = "FLAGD_PROXY_IMAGE"
27+
envVarProxyTag = "FLAGD_PROXY_TAG"
28+
envVarProxyPort = "FLAGD_PROXY_PORT"
29+
envVarProxyMetricsPort = "FLAGD_PROXY_METRICS_PORT"
30+
envVarProxyDebugLogging = "FLAGD_PROXY_DEBUG_LOGGING"
31+
defaultFlagdProxyImage = "ghcr.io/open-feature/flagd-proxy"
32+
defaultFlagdProxyTag = "v0.2.0" //FLAGD_PROXY_TAG_RENOVATE
33+
defaultFlagdProxyPort = 8015
34+
defaultFlagdProxyMetricsPort = 8016
35+
defaultFlagdProxyDebugLogging = false
36+
defaultFlagdProxyNamespace = "open-feature-operator-system"
37+
operatorDeploymentName = "open-feature-operator-controller-manager"
38+
)
39+
40+
type FlagdProxyHandler struct {
41+
client.Client
42+
config *FlagdProxyConfiguration
43+
Log logr.Logger
44+
}
45+
46+
type FlagdProxyConfiguration struct {
47+
Port int
48+
MetricsPort int
49+
DebugLogging bool
50+
Image string
51+
Tag string
52+
Namespace string
53+
OperatorDeploymentName string
54+
}
55+
56+
func NewFlagdProxyConfiguration() (*FlagdProxyConfiguration, error) {
57+
config := &FlagdProxyConfiguration{
58+
Image: defaultFlagdProxyImage,
59+
Tag: defaultFlagdProxyTag,
60+
Namespace: defaultFlagdProxyNamespace,
61+
OperatorDeploymentName: operatorDeploymentName,
62+
}
63+
ns, ok := os.LookupEnv(envVarPodNamespace)
64+
if ok {
65+
config.Namespace = ns
66+
}
67+
kpi, ok := os.LookupEnv(envVarProxyImage)
68+
if ok {
69+
config.Image = kpi
70+
}
71+
kpt, ok := os.LookupEnv(envVarProxyTag)
72+
if ok {
73+
config.Tag = kpt
74+
}
75+
port, err := utils.GetIntEnvVar(envVarProxyPort, defaultFlagdProxyPort)
76+
if err != nil {
77+
return config, err
78+
}
79+
config.Port = port
80+
81+
metricsPort, err := utils.GetIntEnvVar(envVarProxyMetricsPort, defaultFlagdProxyMetricsPort)
82+
if err != nil {
83+
return config, err
84+
}
85+
config.MetricsPort = metricsPort
86+
87+
kpDebugLogging, err := utils.GetBoolEnvVar(envVarProxyDebugLogging, defaultFlagdProxyDebugLogging)
88+
if err != nil {
89+
return config, err
90+
}
91+
config.DebugLogging = kpDebugLogging
92+
93+
return config, nil
94+
}
95+
96+
func NewFlagdProxyHandler(config *FlagdProxyConfiguration, client client.Client, logger logr.Logger) *FlagdProxyHandler {
97+
return &FlagdProxyHandler{
98+
config: config,
99+
Client: client,
100+
Log: logger,
101+
}
102+
}
103+
104+
func (f *FlagdProxyHandler) Config() *FlagdProxyConfiguration {
105+
return f.config
106+
}
107+
108+
func (f *FlagdProxyHandler) handleFlagdProxy(ctx context.Context, flagSourceConfiguration *corev1alpha1.FlagSourceConfiguration) error {
109+
exists, err := f.doesFlagdProxyExist(ctx)
110+
if err != nil {
111+
return err
112+
}
113+
if !exists {
114+
return f.deployFlagdProxy(ctx)
115+
}
116+
return nil
117+
}
118+
119+
func (f *FlagdProxyHandler) deployFlagdProxy(ctx context.Context) error {
120+
ownerReferences := []metav1.OwnerReference{}
121+
ownerReference, err := f.getOwnerReference(ctx)
122+
if err != nil {
123+
f.Log.Error(err, "unable to create owner reference for open-feature-operator, not appending")
124+
} else {
125+
ownerReferences = append(ownerReferences, ownerReference)
126+
}
127+
128+
f.Log.Info("deploying the flagd-proxy")
129+
if err := f.Client.Create(ctx, f.newFlagdProxyManifest(ownerReferences)); err != nil && !errors.IsAlreadyExists(err) {
130+
return err
131+
}
132+
f.Log.Info("deploying the flagd-proxy service")
133+
if err := f.Client.Create(ctx, f.newFlagdProxyServiceManifest(ownerReferences)); err != nil && !errors.IsAlreadyExists(err) {
134+
return err
135+
}
136+
return nil
137+
}
138+
139+
func (f *FlagdProxyHandler) newFlagdProxyServiceManifest(ownerReferences []metav1.OwnerReference) *corev1.Service {
140+
return &corev1.Service{
141+
ObjectMeta: metav1.ObjectMeta{
142+
Name: FlagdProxyServiceName,
143+
Namespace: f.config.Namespace,
144+
OwnerReferences: ownerReferences,
145+
},
146+
Spec: corev1.ServiceSpec{
147+
Selector: map[string]string{
148+
"app.kubernetes.io/name": FlagdProxyDeploymentName,
149+
"app.kubernetes.io/managed-by": ManagedByAnnotationValue,
150+
},
151+
Ports: []corev1.ServicePort{
152+
{
153+
Name: "flagd-proxy",
154+
Port: int32(f.config.Port),
155+
TargetPort: intstr.FromInt(f.config.Port),
156+
},
157+
},
158+
},
159+
}
160+
}
161+
162+
func (f *FlagdProxyHandler) newFlagdProxyManifest(ownerReferences []metav1.OwnerReference) *appsV1.Deployment {
163+
replicas := int32(1)
164+
args := []string{
165+
"start",
166+
"--metrics-port",
167+
fmt.Sprintf("%d", f.config.MetricsPort),
168+
}
169+
if f.config.DebugLogging {
170+
args = append(args, "--debug")
171+
}
172+
return &appsV1.Deployment{
173+
ObjectMeta: metav1.ObjectMeta{
174+
Name: FlagdProxyDeploymentName,
175+
Namespace: f.config.Namespace,
176+
Labels: map[string]string{
177+
"app": FlagdProxyDeploymentName,
178+
"app.kubernetes.io/managed-by": ManagedByAnnotationValue,
179+
"app.kubernetes.io/version": f.config.Tag,
180+
},
181+
OwnerReferences: ownerReferences,
182+
},
183+
Spec: appsV1.DeploymentSpec{
184+
Replicas: &replicas,
185+
Selector: &metav1.LabelSelector{
186+
MatchLabels: map[string]string{
187+
"app": FlagdProxyDeploymentName,
188+
},
189+
},
190+
Template: corev1.PodTemplateSpec{
191+
ObjectMeta: metav1.ObjectMeta{
192+
Labels: map[string]string{
193+
"app": FlagdProxyDeploymentName,
194+
"app.kubernetes.io/name": FlagdProxyDeploymentName,
195+
"app.kubernetes.io/managed-by": ManagedByAnnotationValue,
196+
"app.kubernetes.io/version": f.config.Tag,
197+
},
198+
},
199+
Spec: corev1.PodSpec{
200+
ServiceAccountName: FlagdProxyServiceAccountName,
201+
Containers: []corev1.Container{
202+
{
203+
Image: fmt.Sprintf("%s:%s", f.config.Image, f.config.Tag),
204+
Name: FlagdProxyDeploymentName,
205+
Ports: []corev1.ContainerPort{
206+
{
207+
Name: "port",
208+
ContainerPort: int32(f.config.Port),
209+
},
210+
{
211+
Name: "metrics-port",
212+
ContainerPort: int32(f.config.MetricsPort),
213+
},
214+
},
215+
Args: args,
216+
},
217+
},
218+
},
219+
},
220+
},
221+
}
222+
}
223+
224+
func (f *FlagdProxyHandler) doesFlagdProxyExist(ctx context.Context) (bool, error) {
225+
d := &appsV1.Deployment{}
226+
err := f.Client.Get(ctx, client.ObjectKey{Name: FlagdProxyDeploymentName, Namespace: f.config.Namespace}, d)
227+
if err != nil {
228+
if errors.IsNotFound(err) {
229+
// does not exist, is not ready, no error
230+
return false, nil
231+
}
232+
// does not exist, is not ready, is in error
233+
return false, err
234+
}
235+
// exists, at least one replica ready, no error
236+
return true, nil
237+
}
238+
239+
func (f *FlagdProxyHandler) getOwnerReference(ctx context.Context) (metav1.OwnerReference, error) {
240+
d := &appsV1.Deployment{}
241+
if err := f.Client.Get(ctx, client.ObjectKey{Name: f.config.OperatorDeploymentName, Namespace: f.config.Namespace}, d); err != nil {
242+
return metav1.OwnerReference{}, fmt.Errorf("unable to fetch operator deployment to create owner reference: %w", err)
243+
}
244+
return metav1.OwnerReference{
245+
UID: d.GetUID(),
246+
Name: d.GetName(),
247+
APIVersion: d.APIVersion,
248+
Kind: d.Kind,
249+
}, nil
250+
251+
}

0 commit comments

Comments
 (0)