Skip to content

Commit

Permalink
feat: kube-flagd-proxy deployment (#412)
Browse files Browse the repository at this point in the history
Signed-off-by: James Milligan <[email protected]>
Signed-off-by: James Milligan <[email protected]>
Co-authored-by: Giovanni Liva <[email protected]>
  • Loading branch information
james-milligan and thisthat authored Mar 31, 2023
1 parent 5ba5bc9 commit 651c63c
Show file tree
Hide file tree
Showing 27 changed files with 601 additions and 53 deletions.
5 changes: 5 additions & 0 deletions apis/core/v1alpha1/flagsourceconfiguration_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const (
SyncProviderKubernetes SyncProviderType = "kubernetes"
SyncProviderFilepath SyncProviderType = "filepath"
SyncProviderHttp SyncProviderType = "http"
SyncProviderFlagdProxy SyncProviderType = "flagd-proxy"
)

// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
Expand Down Expand Up @@ -350,6 +351,10 @@ func (s SyncProviderType) IsFilepath() bool {
return s == SyncProviderFilepath
}

func (s SyncProviderType) IsFlagdProxy() bool {
return s == SyncProviderFlagdProxy
}

func envVarKey(prefix string, suffix string) string {
return fmt.Sprintf("%s_%s", prefix, suffix)
}
Expand Down
12 changes: 11 additions & 1 deletion chart/open-feature-operator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ The command removes all the Kubernetes components associated with the chart and
| `sidecarConfiguration.metricsPort` | 8014 | Sets the value of the `XXX_METRICS_PORT` environment variable for the injected sidecar container. |
| `sidecarConfiguration.socketPath` | `""` | Sets the value of the `XXX_SOCKET_PATH` environment variable for the injected sidecar container. |
| `sidecarConfiguration.image.repository` | `ghcr.io/open-feature/flagd` | Sets the image for the injected sidecar container. |
| `sidecarConfiguration.image.tag` | current flagd version: `v0.4.4` | Sets the version tag for the injected sidecar container. |
| `sidecarConfiguration.image.tag` | current flagd version: `v0.4.5` | Sets the version tag for the injected sidecar container. |
| `sidecarConfiguration.providerArgs` | `""` | Used to append arguments to the sidecar startup command. This value is a comma separated string of key values separated by '=', e.g. `key=value,key2=value2` results in the appending of `--sync-provider-args key=value --sync-provider-args key2=value2` |
| `sidecarConfiguration.defaultSyncProvider` | `kubernetes` | Sets the value of the `XXX_SYNC_PROVIDER` environment variable for the injected sidecar container. There are 3 valid sync providers: `kubernetes`, `filepath` and `http` |
| `sidecarConfiguration.logFormat` | `json` | Sets the value of the `XXX_LOG_FORMAT` environment variable for the injected sidecar container. There are 2 valid log formats: `json` and `console` |
Expand Down Expand Up @@ -124,6 +124,16 @@ The command removes all the Kubernetes components associated with the chart and
| `managerConfig.replicas.metrics.bindAddress` | `127.0.0.1:8080` | |
| `managerConfig.replicas.webhook.port` | `9443` | |

### flagd-proxy Configuration

| Value | Default | Explanation |
| -------------------------------------------------- | -------------------------------------------- |---------------------------------------------------------------------------------- |
| `flagdProxyConfiguration.port` | `8015` | Sets the port to expose the sync API on |
| `flagdProxyConfiguration.metricsPort` | `8016` | Sets the port to expose the metrics API on |
| `flagdProxyConfiguration.image.repository` | `ghcr.io/open-feature/flagd-proxy` | Sets the image for the flagd-proxy deployment |
| `flagdProxyConfiguration.image.tag` | current flagd-proxy version: `v0.2.0` | Sets the tag for the flagd-proxy deployment |
| `flagdProxyConfiguration.debugLogging` | `false` | Controls the addition of the `--debug` flag to the container startup arguments |

## Changelog

See [CHANGELOG.md](https://github.com/open-feature/open-feature-operator/blob/main/CHANGELOG.md)
11 changes: 10 additions & 1 deletion chart/open-feature-operator/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,22 @@ sidecarConfiguration:
image:
# these fields must remain in the same order, renovate uses a regex to update the tag value
repository: "ghcr.io/open-feature/flagd"
tag: v0.4.4
tag: v0.4.5
providerArgs: ""
envVarPrefix: "FLAGD"
defaultSyncProvider: kubernetes
evaluator: json
logFormat: "json"
probesEnabled: true
debugLogging: false

flagdProxyConfiguration:
port: 8015
metricsPort: 8016
image:
repository: "ghcr.io/open-feature/flagd-proxy"
tag: v0.2.0
debugLogging: false

controllerManager:
kubeRbacProxy:
Expand Down
5 changes: 5 additions & 0 deletions config/manager/manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ spec:
containers:
- command:
- /manager
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
args:
- --leader-elect
- --flagd-cpu-limit=0.5
Expand Down
10 changes: 10 additions & 0 deletions config/overlays/helm/manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ spec:
value: "{{ .Values.sidecarConfiguration.logFormat }}"
- name: SIDECAR_PROBES_ENABLED
value: "{{ .Values.sidecarConfiguration.probesEnabled }}"
- name: KUBE_PROXY_IMAGE
value: "{{ .Values.flagdProxyConfiguration.image.repository }}"
- name: KUBE_PROXY_TAG
value: "{{ .Values.flagdProxyConfiguration.image.tag }}"
- name: KUBE_PROXY_PORT
value: "{{ .Values.flagdProxyConfiguration.port }}"
- name: KUBE_PROXY_METRICS_PORT
value: "{{ .Values.flagdProxyConfiguration.metricsPort }}"
- name: KUBE_PROXY_DEBUG_LOGGING
value: "{{ .Values.flagdProxyConfiguration.debugLogging }}"
- name: kube-rbac-proxy
image: "{{ .Values.controllerManager.kubeRbacProxy.image.repository }}:{{ .Values.controllerManager.kubeRbacProxy.image.tag }}"
resources:
Expand Down
4 changes: 4 additions & 0 deletions config/rbac/flagd_kubernetes_sync_clusterrolebinding.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ subjects:
name: open-feature-operator-controller-manager
namespace: system
apiGroup: ""
- kind: ServiceAccount
name: open-feature-operator-flagd-proxy
namespace: system
apiGroup: ""
roleRef:
kind: ClusterRole
name: open-feature-operator-flagd-kubernetes-sync
Expand Down
3 changes: 2 additions & 1 deletion config/rbac/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ resources:
# if your manager will use a service account that exists at
# runtime. Be sure to update RoleBinding and ClusterRoleBinding
# subjects if changing service account names.
- service_account.yaml
- service_account_kube_flagd_proxy.yaml
- service_account_manager.yaml
- role.yaml
- role_binding.yaml
- leader_election_role.yaml
Expand Down
8 changes: 8 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ rules:
- get
- list
- watch
- apiGroups:
- ""
resources:
- services
verbs:
- create
- get
- list
- apiGroups:
- apps
resources:
Expand Down
5 changes: 5 additions & 0 deletions config/rbac/service_account_kube_flagd_proxy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: flagd-proxy
namespace: system
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ apiVersion: v1
kind: ServiceAccount
metadata:
name: controller-manager
namespace: system
namespace: system
33 changes: 32 additions & 1 deletion controllers/core/flagsourceconfiguration/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,38 @@ import (
corev1alpha1 "github.com/open-feature/open-feature-operator/apis/core/v1alpha1"
)

const (
FlagdProxyDeploymentName = "flagd-proxy"
FlagdProxyServiceAccountName = "open-feature-operator-flagd-proxy"
FlagdProxyServiceName = "flagd-proxy-svc"

envVarPodNamespace = "POD_NAMESPACE"
envVarProxyImage = "KUBE_PROXY_IMAGE"
envVarProxyTag = "KUBE_PROXY_TAG"
envVarProxyPort = "KUBE_PROXY_PORT"
envVarProxyMetricsPort = "KUBE_PROXY_METRICS_PORT"
envVarProxyDebugLogging = "KUBE_PROXY_DEBUG_LOGGING"
defaultFlagdProxyImage = "ghcr.io/open-feature/flagd-proxy"
defaultFlagdProxyTag = "v0.2.0" //KUBE_PROXY_TAG_RENOVATE
defaultFlagdProxyPort = 8015
defaultFlagdProxyMetricsPort = 8016
defaultFlagdProxyDebugLogging = false
defaultFlagdProxyNamespace = "open-feature-operator-system"
)

// FlagSourceConfigurationReconciler reconciles a FlagSourceConfiguration object
type FlagSourceConfigurationReconciler struct {
client.Client
Scheme *runtime.Scheme
// ReqLogger contains the Logger of this controller
Log logr.Logger
Log logr.Logger
FlagdProxy *FlagdProxyHandler
}

//+kubebuilder:rbac:groups=core.openfeature.dev,resources=flagsourceconfigurations,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=core.openfeature.dev,resources=flagsourceconfigurations/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups="",resources=services,verbs=get;list;create
//+kubebuilder:rbac:groups=core.openfeature.dev,resources=flagsourceconfigurations/finalizers,verbs=update

// Reconcile is part of the main kubernetes reconciliation loop which aims to
Expand All @@ -68,6 +89,16 @@ func (r *FlagSourceConfigurationReconciler) Reconcile(ctx context.Context, req c
return r.finishReconcile(err, false)
}

for _, source := range fsConfig.Spec.Sources {
if source.Provider.IsFlagdProxy() {
r.Log.Info(fmt.Sprintf("flagsourceconfiguration %s uses flagd-proxy, checking deployment", req.NamespacedName))
if err := r.FlagdProxy.handleFlagdProxy(ctx); err != nil {
r.Log.Error(err, "error handling the flagd-proxy deployment")
}
break
}
}

if fsConfig.Spec.RolloutOnChange == nil || !*fsConfig.Spec.RolloutOnChange {
return r.finishReconcile(nil, false)
}
Expand Down
57 changes: 45 additions & 12 deletions controllers/core/flagsourceconfiguration/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,37 @@ func TestFlagSourceConfigurationReconciler_Reconcile(t *testing.T) {
deployment *appsv1.Deployment
restartedAtValueBeforeReconcile string
restartedAtValueAfterReconcile string
flagdProxyDeployment bool
}{
{
name: "deployment gets restarted with rollout",
fsConfig: createTestFSConfig(fsConfigName, testNamespace, deploymentName, true),
fsConfig: createTestFSConfig(fsConfigName, testNamespace, deploymentName, true, v1alpha1.SyncProviderHttp),
deployment: createTestDeployment(fsConfigName, testNamespace, deploymentName),
restartedAtValueBeforeReconcile: "",
restartedAtValueAfterReconcile: time.Now().Format(time.RFC3339),
},
{
name: "deployment without rollout",
fsConfig: createTestFSConfig(fsConfigName, testNamespace, deploymentName, false),
fsConfig: createTestFSConfig(fsConfigName, testNamespace, deploymentName, false, v1alpha1.SyncProviderHttp),
deployment: createTestDeployment(fsConfigName, testNamespace, deploymentName),
restartedAtValueBeforeReconcile: "",
restartedAtValueAfterReconcile: "",
},
{
name: "no deployment",
fsConfig: createTestFSConfig(fsConfigName, testNamespace, deploymentName, true),
fsConfig: createTestFSConfig(fsConfigName, testNamespace, deploymentName, true, v1alpha1.SyncProviderHttp),
deployment: nil,
restartedAtValueBeforeReconcile: "",
restartedAtValueAfterReconcile: "",
},
{
name: "no deployment, kube proxy deployment",
fsConfig: createTestFSConfig(fsConfigName, testNamespace, deploymentName, true, v1alpha1.SyncProviderFlagdProxy),
deployment: nil,
restartedAtValueBeforeReconcile: "",
restartedAtValueAfterReconcile: "",
flagdProxyDeployment: true,
},
}

err := v1alpha1.AddToScheme(scheme.Scheme)
Expand All @@ -77,17 +86,26 @@ func TestFlagSourceConfigurationReconciler_Reconcile(t *testing.T) {
} else {
fakeClient = fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(tt.fsConfig).WithIndex(&appsv1.Deployment{}, fmt.Sprintf("%s/%s", common.OpenFeatureAnnotationPath, common.FlagSourceConfigurationAnnotation), common.FlagSourceConfigurationIndex).Build()
}
kpConfig, err := NewFlagdProxyConfiguration()
require.Nil(t, err)
kph := NewFlagdProxyHandler(
kpConfig,
fakeClient,
ctrl.Log.WithName("flagsourceconfiguration-FlagdProxyhandler"),
)
kph.config.Namespace = testNamespace

r := &FlagSourceConfigurationReconciler{
Client: fakeClient,
Log: ctrl.Log.WithName("flagsourceconfiguration-controller"),
Scheme: fakeClient.Scheme(),
Client: fakeClient,
Log: ctrl.Log.WithName("flagsourceconfiguration-controller"),
Scheme: fakeClient.Scheme(),
FlagdProxy: kph,
}

if tt.deployment != nil {
// checking that the deployment does have 'restartedAt' set to the expected value before reconciliation
deployment := &appsv1.Deployment{}
err = fakeClient.Get(ctx, types.NamespacedName{Name: deploymentName, Namespace: testNamespace}, deployment)
err = fakeClient.Get(ctx, types.NamespacedName{Name: deploymentName, Namespace: kph.config.Namespace}, deployment)
require.Nil(t, err)
restartAt := deployment.Spec.Template.ObjectMeta.Annotations["kubectl.kubernetes.io/restartedAt"]
require.Equal(t, tt.restartedAtValueBeforeReconcile, restartAt)
Expand All @@ -105,10 +123,25 @@ func TestFlagSourceConfigurationReconciler_Reconcile(t *testing.T) {

require.Equal(t, tt.restartedAtValueAfterReconcile, deployment.Spec.Template.ObjectMeta.Annotations["kubectl.kubernetes.io/restartedAt"])
}
})

}
if tt.flagdProxyDeployment {
// check that a deployment exists in the default namespace with the correct image and tag
// ensure that the associated service has also been deployed
deployment := &appsv1.Deployment{}
err = fakeClient.Get(ctx, types.NamespacedName{Name: FlagdProxyDeploymentName, Namespace: testNamespace}, deployment)
require.Nil(t, err)
require.Equal(t, len(deployment.Spec.Template.Spec.Containers), 1)
require.Equal(t, len(deployment.Spec.Template.Spec.Containers[0].Ports), 2)
require.Equal(t, deployment.Spec.Template.Spec.Containers[0].Image, fmt.Sprintf("%s:%s", defaultFlagdProxyImage, defaultFlagdProxyTag))

service := &corev1.Service{}
err = fakeClient.Get(ctx, types.NamespacedName{Name: FlagdProxyServiceName, Namespace: testNamespace}, service)
require.Nil(t, err)
require.Equal(t, len(service.Spec.Ports), 1)
require.Equal(t, service.Spec.Ports[0].TargetPort.IntVal, deployment.Spec.Template.Spec.Containers[0].Ports[0].ContainerPort)
}
})
}
}

func createTestDeployment(fsConfigName string, testNamespace string, deploymentName string) *appsv1.Deployment {
Expand Down Expand Up @@ -152,7 +185,7 @@ func createTestDeployment(fsConfigName string, testNamespace string, deploymentN
return deployment
}

func createTestFSConfig(fsConfigName string, testNamespace string, deploymentName string, rollout bool) *v1alpha1.FlagSourceConfiguration {
func createTestFSConfig(fsConfigName string, testNamespace string, deploymentName string, rollout bool, provider v1alpha1.SyncProviderType) *v1alpha1.FlagSourceConfiguration {
fsConfig := &v1alpha1.FlagSourceConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: fsConfigName,
Expand All @@ -162,8 +195,8 @@ func createTestFSConfig(fsConfigName string, testNamespace string, deploymentNam
Image: deploymentName,
Sources: []v1alpha1.Source{
{
Source: "not-real.com",
Provider: "http",
Source: "my-source",
Provider: provider,
},
},
RolloutOnChange: &rollout,
Expand Down
Loading

0 comments on commit 651c63c

Please sign in to comment.