Skip to content

Commit 3271f33

Browse files
odubajDTthisthat
andauthored
feat: auto-upgrade flagd-proxy with OFO upgrades (#596)
Signed-off-by: odubajDT <ondrej.dubaj@dynatrace.com> Signed-off-by: odubajDT <93584209+odubajDT@users.noreply.github.com> Co-authored-by: Giovanni Liva <giovanni.liva@dynatrace.com>
1 parent f3f9427 commit 3271f33

File tree

8 files changed

+249
-42
lines changed

8 files changed

+249
-42
lines changed

CONTRIBUTING.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ make build-deploy-operator TAG=myTag RELEASE_REGISTRY=docker.io/user1 RELEASE_NA
2929
Which will result in building the operator image `docker.io/user1/myImgName:myTag`, uploading it to your image registry
3030
and deploying to your cluster. Please be aware that it is using the cluster your current kube-context is pointing to.
3131

32-
**Note:** All bash variables are optional, the default values are set and will result in an image `ghcr.io/openfeature/operator:latest`
32+
> [!NOTE]
33+
> All bash variables are optional, the default values are set and will result in an image `ghcr.io/openfeature/operator:latest`
3334
3435
### Autogenerated Documentation
3536

chart/open-feature-operator/README.md

+7
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ helm repo update
4545
helm upgrade --install open-feature-operator openfeature/open-feature-operator
4646
```
4747

48+
> [!NOTE]
49+
> If you upgrade to OFO `v0.5.4` or higher while using a `flagd-proxy` provider, the instance of
50+
`flagd-proxy` will be automatically upgraded to the latest supported version by the `open-feature-operator`.
51+
The upgrade of `flagd-proxy` will also consider your current `FeatureFlagSource` configuration and adapt
52+
the `flagd-proxy` Deployment accordingly.
53+
If you are upgrading OFO to `v0.5.3` or lower, `flagd-proxy` (if present) won't be upgraded automatically.
54+
4855
#### Upgrade CRDs
4956

5057
CRDs are not upgraded automatically with helm (https://helm.sh/docs/chart_best_practices/custom_resource_definitions/).

common/flagdproxy/flagdproxy.go

+69-29
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package flagdproxy
33
import (
44
"context"
55
"fmt"
6+
"reflect"
67

78
"github.com/go-logr/logr"
89
"github.com/open-feature/open-feature-operator/common/types"
@@ -28,6 +29,8 @@ type FlagdProxyHandler struct {
2829
Log logr.Logger
2930
}
3031

32+
type CreateUpdateFunc func(ctx context.Context, obj client.Object) error
33+
3134
type FlagdProxyConfiguration struct {
3235
Port int
3336
ManagementPort int
@@ -62,43 +65,58 @@ func (f *FlagdProxyHandler) Config() *FlagdProxyConfiguration {
6265
return f.config
6366
}
6467

68+
func (f *FlagdProxyHandler) createObject(ctx context.Context, obj client.Object) error {
69+
return f.Client.Create(ctx, obj)
70+
}
71+
72+
func (f *FlagdProxyHandler) updateObject(ctx context.Context, obj client.Object) error {
73+
return f.Client.Update(ctx, obj)
74+
}
75+
6576
func (f *FlagdProxyHandler) HandleFlagdProxy(ctx context.Context) error {
66-
exists, err := f.doesFlagdProxyExist(ctx)
77+
exists, deployment, err := f.doesFlagdProxyExist(ctx)
6778
if err != nil {
6879
return err
6980
}
70-
if !exists {
71-
return f.deployFlagdProxy(ctx)
72-
}
73-
return nil
74-
}
7581

76-
func (f *FlagdProxyHandler) deployFlagdProxy(ctx context.Context) error {
77-
ownerReferences := []metav1.OwnerReference{}
7882
ownerReference, err := f.getOwnerReference(ctx)
7983
if err != nil {
80-
f.Log.Error(err, "unable to create owner reference for open-feature-operator, not appending")
81-
} else {
82-
ownerReferences = append(ownerReferences, ownerReference)
84+
return err
85+
}
86+
newDeployment := f.newFlagdProxyManifest(ownerReference)
87+
newService := f.newFlagdProxyServiceManifest(ownerReference)
88+
89+
if !exists {
90+
f.Log.Info("flagd-proxy Deployment does not exist, creating")
91+
return f.deployFlagdProxy(ctx, f.createObject, newDeployment, newService)
8392
}
93+
// flagd-proxy exists, need to check if we should update it
94+
if f.shouldUpdateFlagdProxy(deployment, newDeployment) {
95+
f.Log.Info("flagd-proxy Deployment out of sync, updating")
96+
return f.deployFlagdProxy(ctx, f.updateObject, newDeployment, newService)
97+
}
98+
f.Log.Info("flagd-proxy Deployment up-to-date")
99+
return nil
100+
}
84101

102+
func (f *FlagdProxyHandler) deployFlagdProxy(ctx context.Context, createUpdateFunc CreateUpdateFunc, deployment *appsV1.Deployment, service *corev1.Service) error {
85103
f.Log.Info("deploying the flagd-proxy")
86-
if err := f.Client.Create(ctx, f.newFlagdProxyManifest(ownerReferences)); err != nil && !errors.IsAlreadyExists(err) {
104+
if err := createUpdateFunc(ctx, deployment); err != nil && !errors.IsAlreadyExists(err) {
87105
return err
88106
}
89107
f.Log.Info("deploying the flagd-proxy service")
90-
if err := f.Client.Create(ctx, f.newFlagdProxyServiceManifest(ownerReferences)); err != nil && !errors.IsAlreadyExists(err) {
108+
if err := createUpdateFunc(ctx, service); err != nil && !errors.IsAlreadyExists(err) {
91109
return err
92110
}
93111
return nil
94112
}
95113

96-
func (f *FlagdProxyHandler) newFlagdProxyServiceManifest(ownerReferences []metav1.OwnerReference) *corev1.Service {
114+
func (f *FlagdProxyHandler) newFlagdProxyServiceManifest(ownerReference *metav1.OwnerReference) *corev1.Service {
97115
return &corev1.Service{
98116
ObjectMeta: metav1.ObjectMeta{
99117
Name: FlagdProxyServiceName,
100118
Namespace: f.config.Namespace,
101-
OwnerReferences: ownerReferences,
119+
OwnerReferences: []metav1.OwnerReference{*ownerReference},
102120
},
103121
Spec: corev1.ServiceSpec{
104122
Selector: map[string]string{
@@ -116,7 +134,7 @@ func (f *FlagdProxyHandler) newFlagdProxyServiceManifest(ownerReferences []metav
116134
}
117135
}
118136

119-
func (f *FlagdProxyHandler) newFlagdProxyManifest(ownerReferences []metav1.OwnerReference) *appsV1.Deployment {
137+
func (f *FlagdProxyHandler) newFlagdProxyManifest(ownerReference *metav1.OwnerReference) *appsV1.Deployment {
120138
replicas := int32(1)
121139
args := []string{
122140
"start",
@@ -135,7 +153,7 @@ func (f *FlagdProxyHandler) newFlagdProxyManifest(ownerReferences []metav1.Owner
135153
"app.kubernetes.io/managed-by": ManagedByAnnotationValue,
136154
"app.kubernetes.io/version": f.config.Tag,
137155
},
138-
OwnerReferences: ownerReferences,
156+
OwnerReferences: []metav1.OwnerReference{*ownerReference},
139157
},
140158
Spec: appsV1.DeploymentSpec{
141159
Replicas: &replicas,
@@ -178,31 +196,53 @@ func (f *FlagdProxyHandler) newFlagdProxyManifest(ownerReferences []metav1.Owner
178196
}
179197
}
180198

181-
func (f *FlagdProxyHandler) doesFlagdProxyExist(ctx context.Context) (bool, error) {
199+
func (f *FlagdProxyHandler) doesFlagdProxyExist(ctx context.Context) (bool, *appsV1.Deployment, error) {
182200
d := &appsV1.Deployment{}
183201
err := f.Client.Get(ctx, client.ObjectKey{Name: FlagdProxyDeploymentName, Namespace: f.config.Namespace}, d)
184202
if err != nil {
185203
if errors.IsNotFound(err) {
186204
// does not exist, is not ready, no error
187-
return false, nil
205+
return false, nil, nil
188206
}
189207
// does not exist, is not ready, is in error
190-
return false, err
208+
return false, nil, err
191209
}
192-
// exists, at least one replica ready, no error
193-
return true, nil
210+
return true, d, nil
194211
}
195212

196-
func (f *FlagdProxyHandler) getOwnerReference(ctx context.Context) (metav1.OwnerReference, error) {
213+
func (f *FlagdProxyHandler) shouldUpdateFlagdProxy(old, new *appsV1.Deployment) bool {
214+
if !isDeployedByOFO(old) {
215+
f.Log.Info("flagd-proxy Deployment not managed by OFO")
216+
return false
217+
}
218+
return !reflect.DeepEqual(old.Spec, new.Spec)
219+
}
220+
221+
func (f *FlagdProxyHandler) getOperatorDeployment(ctx context.Context) (*appsV1.Deployment, error) {
197222
d := &appsV1.Deployment{}
198223
if err := f.Client.Get(ctx, client.ObjectKey{Name: f.config.OperatorDeploymentName, Namespace: f.config.Namespace}, d); err != nil {
199-
return metav1.OwnerReference{}, fmt.Errorf("unable to fetch operator deployment to create owner reference: %w", err)
224+
return nil, fmt.Errorf("unable to fetch operator deployment: %w", err)
200225
}
201-
return metav1.OwnerReference{
202-
UID: d.GetUID(),
203-
Name: d.GetName(),
204-
APIVersion: d.APIVersion,
205-
Kind: d.Kind,
226+
return d, nil
227+
228+
}
229+
230+
func (f *FlagdProxyHandler) getOwnerReference(ctx context.Context) (*metav1.OwnerReference, error) {
231+
operatorDeployment, err := f.getOperatorDeployment(ctx)
232+
if err != nil {
233+
f.Log.Error(err, "unable to create owner reference for open-feature-operator")
234+
return nil, err
235+
}
236+
237+
return &metav1.OwnerReference{
238+
UID: operatorDeployment.GetUID(),
239+
Name: operatorDeployment.GetName(),
240+
APIVersion: operatorDeployment.APIVersion,
241+
Kind: operatorDeployment.Kind,
206242
}, nil
243+
}
207244

245+
func isDeployedByOFO(d *appsV1.Deployment) bool {
246+
val, ok := d.Labels["app.kubernetes.io/managed-by"]
247+
return ok && val == ManagedByAnnotationValue
208248
}

0 commit comments

Comments
 (0)