Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: auto-upgrade flagd-proxy with OFO upgrades #596

Merged
merged 9 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ make build-deploy-operator TAG=myTag RELEASE_REGISTRY=docker.io/user1 RELEASE_NA
Which will result in building the operator image `docker.io/user1/myImgName:myTag`, uploading it to your image registry
and deploying to your cluster. Please be aware that it is using the cluster your current kube-context is pointing to.

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

Expand Down
7 changes: 7 additions & 0 deletions chart/open-feature-operator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ helm repo update
helm upgrade --install open-feature-operator openfeature/open-feature-operator
```

> [!NOTE]
> If you have used `flagd-proxy` provider and upgrading to OFO version `v0.5.4` or higher,
`flagd-proxy` will be automatically upgraded to the latest supported version by the `open-feature-operator`.
odubajDT marked this conversation as resolved.
Show resolved Hide resolved
This upgrade will also consider your current `FeatureFlagSource` configuration and adapt
the `flagd-proxy` Deployment accordingly.
odubajDT marked this conversation as resolved.
Show resolved Hide resolved
If you are upgrading OFO to `v0.5.3` or lower, `flagd-proxy` (if present) won't be upgraded automatically.

#### Upgrade CRDs

CRDs are not upgraded automatically with helm (https://helm.sh/docs/chart_best_practices/custom_resource_definitions/).
Expand Down
81 changes: 55 additions & 26 deletions common/flagdproxy/flagdproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package flagdproxy
import (
"context"
"fmt"
"reflect"

"github.com/go-logr/logr"
"github.com/open-feature/open-feature-operator/common/types"
Expand All @@ -28,6 +29,7 @@ type FlagdProxyHandler struct {
Log logr.Logger
}

type CreateUpdateFunc func(ctx context.Context, obj client.Object) error
odubajDT marked this conversation as resolved.
Show resolved Hide resolved
type FlagdProxyConfiguration struct {
Port int
ManagementPort int
Expand Down Expand Up @@ -62,32 +64,44 @@ func (f *FlagdProxyHandler) Config() *FlagdProxyConfiguration {
return f.config
}

func (f *FlagdProxyHandler) createObject(ctx context.Context, obj client.Object) error {
return f.Client.Create(ctx, obj)
}

func (f *FlagdProxyHandler) updateObject(ctx context.Context, obj client.Object) error {
return f.Client.Update(ctx, obj)
}

func (f *FlagdProxyHandler) HandleFlagdProxy(ctx context.Context) error {
Kavindu-Dodan marked this conversation as resolved.
Show resolved Hide resolved
exists, err := f.doesFlagdProxyExist(ctx)
exists, deployment, err := f.doesFlagdProxyExist(ctx)
if err != nil {
return err
}

ownerReferences := f.getOwnerReferences(ctx)
newDeployment := f.newFlagdProxyManifest(ownerReferences)
newService := f.newFlagdProxyServiceManifest(ownerReferences)

if !exists {
return f.deployFlagdProxy(ctx)
f.Log.Info("flagd-proxy Deployment does not exist, creating")
return f.deployFlagdProxy(ctx, f.createObject, newDeployment, newService)
}
// flagd-proxy exists, need to check if it's the right version
if !f.isFlagdProxyUpToDate(deployment, newDeployment) {
f.Log.Info("flagd-proxy Deployment changed, updating")
return f.deployFlagdProxy(ctx, f.updateObject, newDeployment, newService)
}
f.Log.Info("flagd-proxy Deployment up-to-date")
return nil
}

func (f *FlagdProxyHandler) deployFlagdProxy(ctx context.Context) error {
ownerReferences := []metav1.OwnerReference{}
ownerReference, err := f.getOwnerReference(ctx)
if err != nil {
f.Log.Error(err, "unable to create owner reference for open-feature-operator, not appending")
} else {
ownerReferences = append(ownerReferences, ownerReference)
}

func (f *FlagdProxyHandler) deployFlagdProxy(ctx context.Context, ff CreateUpdateFunc, deployment *appsV1.Deployment, service *corev1.Service) error {
f.Log.Info("deploying the flagd-proxy")
if err := f.Client.Create(ctx, f.newFlagdProxyManifest(ownerReferences)); err != nil && !errors.IsAlreadyExists(err) {
if err := ff(ctx, deployment); err != nil && !errors.IsAlreadyExists(err) {
odubajDT marked this conversation as resolved.
Show resolved Hide resolved
return err
}
f.Log.Info("deploying the flagd-proxy service")
if err := f.Client.Create(ctx, f.newFlagdProxyServiceManifest(ownerReferences)); err != nil && !errors.IsAlreadyExists(err) {
if err := ff(ctx, service); err != nil && !errors.IsAlreadyExists(err) {
return err
}
return nil
Expand Down Expand Up @@ -178,31 +192,46 @@ func (f *FlagdProxyHandler) newFlagdProxyManifest(ownerReferences []metav1.Owner
}
}

func (f *FlagdProxyHandler) doesFlagdProxyExist(ctx context.Context) (bool, error) {
func (f *FlagdProxyHandler) doesFlagdProxyExist(ctx context.Context) (bool, *appsV1.Deployment, error) {
d := &appsV1.Deployment{}
err := f.Client.Get(ctx, client.ObjectKey{Name: FlagdProxyDeploymentName, Namespace: f.config.Namespace}, d)
if err != nil {
if errors.IsNotFound(err) {
// does not exist, is not ready, no error
return false, nil
return false, nil, nil
}
// does not exist, is not ready, is in error
return false, err
return false, nil, err
}
// exists, at least one replica ready, no error
return true, nil
return true, d, nil
}

func (f *FlagdProxyHandler) getOwnerReference(ctx context.Context) (metav1.OwnerReference, error) {
func (f *FlagdProxyHandler) isFlagdProxyUpToDate(old, new *appsV1.Deployment) bool {
return reflect.DeepEqual(old.Spec, new.Spec)
thisthat marked this conversation as resolved.
Show resolved Hide resolved
}

func (f *FlagdProxyHandler) getOperatorDeployment(ctx context.Context) (*appsV1.Deployment, error) {
d := &appsV1.Deployment{}
if err := f.Client.Get(ctx, client.ObjectKey{Name: f.config.OperatorDeploymentName, Namespace: f.config.Namespace}, d); err != nil {
return metav1.OwnerReference{}, fmt.Errorf("unable to fetch operator deployment to create owner reference: %w", err)
return nil, fmt.Errorf("unable to fetch operator deployment to create owner reference: %w", err)
}
return metav1.OwnerReference{
UID: d.GetUID(),
Name: d.GetName(),
APIVersion: d.APIVersion,
Kind: d.Kind,
}, nil
return d, nil

}

func (f *FlagdProxyHandler) getOwnerReferences(ctx context.Context) []metav1.OwnerReference {
odubajDT marked this conversation as resolved.
Show resolved Hide resolved
operatorDeployment, err := f.getOperatorDeployment(ctx)
if err != nil {
f.Log.Error(err, "unable to create owner reference for open-feature-operator")
return []metav1.OwnerReference{}
}

return []metav1.OwnerReference{
{
UID: operatorDeployment.GetUID(),
Name: operatorDeployment.GetName(),
APIVersion: operatorDeployment.APIVersion,
Kind: operatorDeployment.Kind,
},
}
}
46 changes: 42 additions & 4 deletions common/flagdproxy/flagdproxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,19 +104,21 @@ func TestDoesFlagdProxyExist(t *testing.T) {

require.NotNil(t, ph)

res, err := ph.doesFlagdProxyExist(context.TODO())
res, d, err := ph.doesFlagdProxyExist(context.TODO())
require.Nil(t, err)
require.Nil(t, d)
require.False(t, res)

err = fakeClient.Create(context.TODO(), deployment)
require.Nil(t, err)

res, err = ph.doesFlagdProxyExist(context.TODO())
res, d, err = ph.doesFlagdProxyExist(context.TODO())
require.Nil(t, err)
require.NotNil(t, d)
require.True(t, res)
}

func TestFlagdProxyHandler_HandleFlagdProxy_ProxyExists(t *testing.T) {
func TestFlagdProxyHandler_HandleFlagdProxy_ProxyExistsWithBadVersion(t *testing.T) {
env := types.EnvConfig{
PodNamespace: "ns",
}
Expand Down Expand Up @@ -159,8 +161,44 @@ func TestFlagdProxyHandler_HandleFlagdProxy_ProxyExists(t *testing.T) {
require.Nil(t, err)
require.NotNil(t, deployment)

// verify that the existing deployment has been changed
require.Equal(t, "flagd-proxy", deployment.Spec.Template.Spec.Containers[0].Name)
}

func TestFlagdProxyHandler_HandleFlagdProxy_ProxyExistsWithNewestVersion(t *testing.T) {
env := types.EnvConfig{
PodNamespace: "ns",
}
kpConfig := NewFlagdProxyConfiguration(env)

require.NotNil(t, kpConfig)

fakeClient := fake.NewClientBuilder().WithObjects().Build()

ph := NewFlagdProxyHandler(kpConfig, fakeClient, testr.New(t))

require.NotNil(t, ph)

proxy := ph.newFlagdProxyManifest([]metav1.OwnerReference{})

err := fakeClient.Create(context.TODO(), proxy)
require.Nil(t, err)

err = ph.HandleFlagdProxy(context.Background())

require.Nil(t, err)

deployment := &v1.Deployment{}
err = fakeClient.Get(context.Background(), client.ObjectKey{
Namespace: env.PodNamespace,
Name: FlagdProxyDeploymentName,
}, deployment)

require.Nil(t, err)
require.NotNil(t, deployment)

// verify that the existing deployment has not been changed
require.Equal(t, "my-container", deployment.Spec.Template.Spec.Containers[0].Name)
require.Equal(t, "flagd-proxy", deployment.Spec.Template.Spec.Containers[0].Name)
}

func TestFlagdProxyHandler_HandleFlagdProxy_CreateProxy(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions config/manager/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
images:
- name: controller
newName: controller
newTag: latest
newName: docker.io/odubajdt/operator
newTag: proxy-update
7 changes: 7 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ helm upgrade --install openfeature openfeature/open-feature-operator
helm upgrade --install openfeature openfeature/open-feature-operator
```

> [!NOTE]
> If you have used `flagd-proxy` provider and upgrading to OFO version `v0.5.4` or higher,
`flagd-proxy` will be automatically upgraded to the latest supported version by the `open-feature-operator`.
This upgrade will also consider your current `FeatureFlagSource` configuration and adapt
the `flagd-proxy` Deployment accordingly.
If you are upgrading OFO to `v0.5.3` or lower, `flagd-proxy` (if present) won't be upgraded automatically.

#### Upgrading CRDs

CRDs are not upgraded automatically with helm (https://helm.sh/docs/chart_best_practices/custom_resource_definitions/).
Expand Down
4 changes: 2 additions & 2 deletions docs/quick_start.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/
kubectl wait --for=condition=Available=True deploy --all -n 'cert-manager'
```

> **Note**
> [!NOTE]
> Requirement of this dependency is explained in the [installation](./installation.md) guide.

#### 3. Install OpenFeature Operator
Expand Down Expand Up @@ -52,7 +52,7 @@ Next steps focus on adding feature flags, flag source configuration and a worklo
kubectl create ns flags
```

> **Note**
> [!NOTE]
> We use the namespace `flags` for flag related custom resources

#### 5. Install feature flags definition
Expand Down
9 changes: 7 additions & 2 deletions docs/v1beta_migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,10 @@ We recommend following migration steps,
3. Install upgraded custom resources
4. Update annotation of your workloads to the latest supported version

If you have used `flagd-proxy` provider, then you have to upgrade the image used by the `flagd-proxy` deployment.
For this, please edit the deployment of `flagd-proxy` to version [v0.3.1](https://github.com/open-feature/flagd/pkgs/container/flagd-proxy/152333134?tag=v0.3.1) or above.
If you have used `flagd-proxy` provider, then you have to upgrade the image used by the `flagd-proxy` deployment.
For this, please edit the deployment of `flagd-proxy` to version [v0.3.1](https://github.com/open-feature/flagd/pkgs/container/flagd-proxy/152333134?tag=v0.3.1) or above.

> [!NOTE]
> Since OFO version `v0.5.4`, `flagd-proxy` pod (if present) will be upgraded automatically to the
to the latest supported version by `open-feature-operator`.
For more information see the [upgrade section](./installation.md#upgrading).
Loading