-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: flagd proxy resource ownership (#442)
Signed-off-by: James Milligan <james@omnant.co.uk>
- Loading branch information
1 parent
4cbe4f1
commit 31b5f7b
Showing
9 changed files
with
260 additions
and
233 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
251 changes: 251 additions & 0 deletions
251
controllers/core/flagsourceconfiguration/flagd-proxy.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,251 @@ | ||
package flagsourceconfiguration | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
|
||
"github.com/go-logr/logr" | ||
corev1alpha1 "github.com/open-feature/open-feature-operator/apis/core/v1alpha1" | ||
"github.com/open-feature/open-feature-operator/pkg/utils" | ||
appsV1 "k8s.io/api/apps/v1" | ||
corev1 "k8s.io/api/core/v1" | ||
"k8s.io/apimachinery/pkg/api/errors" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/util/intstr" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
) | ||
|
||
const ( | ||
ManagedByAnnotationValue = "open-feature-operator" | ||
FlagdProxyDeploymentName = "flagd-proxy" | ||
FlagdProxyServiceAccountName = "open-feature-operator-flagd-proxy" | ||
FlagdProxyServiceName = "flagd-proxy-svc" | ||
|
||
envVarPodNamespace = "POD_NAMESPACE" | ||
envVarProxyImage = "FLAGD_PROXY_IMAGE" | ||
envVarProxyTag = "FLAGD_PROXY_TAG" | ||
envVarProxyPort = "FLAGD_PROXY_PORT" | ||
envVarProxyMetricsPort = "FLAGD_PROXY_METRICS_PORT" | ||
envVarProxyDebugLogging = "FLAGD_PROXY_DEBUG_LOGGING" | ||
defaultFlagdProxyImage = "ghcr.io/open-feature/flagd-proxy" | ||
defaultFlagdProxyTag = "v0.2.0" //FLAGD_PROXY_TAG_RENOVATE | ||
defaultFlagdProxyPort = 8015 | ||
defaultFlagdProxyMetricsPort = 8016 | ||
defaultFlagdProxyDebugLogging = false | ||
defaultFlagdProxyNamespace = "open-feature-operator-system" | ||
operatorDeploymentName = "open-feature-operator-controller-manager" | ||
) | ||
|
||
type FlagdProxyHandler struct { | ||
client.Client | ||
config *FlagdProxyConfiguration | ||
Log logr.Logger | ||
} | ||
|
||
type FlagdProxyConfiguration struct { | ||
Port int | ||
MetricsPort int | ||
DebugLogging bool | ||
Image string | ||
Tag string | ||
Namespace string | ||
OperatorDeploymentName string | ||
} | ||
|
||
func NewFlagdProxyConfiguration() (*FlagdProxyConfiguration, error) { | ||
config := &FlagdProxyConfiguration{ | ||
Image: defaultFlagdProxyImage, | ||
Tag: defaultFlagdProxyTag, | ||
Namespace: defaultFlagdProxyNamespace, | ||
OperatorDeploymentName: operatorDeploymentName, | ||
} | ||
ns, ok := os.LookupEnv(envVarPodNamespace) | ||
if ok { | ||
config.Namespace = ns | ||
} | ||
kpi, ok := os.LookupEnv(envVarProxyImage) | ||
if ok { | ||
config.Image = kpi | ||
} | ||
kpt, ok := os.LookupEnv(envVarProxyTag) | ||
if ok { | ||
config.Tag = kpt | ||
} | ||
port, err := utils.GetIntEnvVar(envVarProxyPort, defaultFlagdProxyPort) | ||
if err != nil { | ||
return config, err | ||
} | ||
config.Port = port | ||
|
||
metricsPort, err := utils.GetIntEnvVar(envVarProxyMetricsPort, defaultFlagdProxyMetricsPort) | ||
if err != nil { | ||
return config, err | ||
} | ||
config.MetricsPort = metricsPort | ||
|
||
kpDebugLogging, err := utils.GetBoolEnvVar(envVarProxyDebugLogging, defaultFlagdProxyDebugLogging) | ||
if err != nil { | ||
return config, err | ||
} | ||
config.DebugLogging = kpDebugLogging | ||
|
||
return config, nil | ||
} | ||
|
||
func NewFlagdProxyHandler(config *FlagdProxyConfiguration, client client.Client, logger logr.Logger) *FlagdProxyHandler { | ||
return &FlagdProxyHandler{ | ||
config: config, | ||
Client: client, | ||
Log: logger, | ||
} | ||
} | ||
|
||
func (f *FlagdProxyHandler) Config() *FlagdProxyConfiguration { | ||
return f.config | ||
} | ||
|
||
func (f *FlagdProxyHandler) handleFlagdProxy(ctx context.Context, flagSourceConfiguration *corev1alpha1.FlagSourceConfiguration) error { | ||
exists, err := f.doesFlagdProxyExist(ctx) | ||
if err != nil { | ||
return err | ||
} | ||
if !exists { | ||
return f.deployFlagdProxy(ctx) | ||
} | ||
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) | ||
} | ||
|
||
f.Log.Info("deploying the flagd-proxy") | ||
if err := f.Client.Create(ctx, f.newFlagdProxyManifest(ownerReferences)); err != nil && !errors.IsAlreadyExists(err) { | ||
return err | ||
} | ||
f.Log.Info("deploying the flagd-proxy service") | ||
if err := f.Client.Create(ctx, f.newFlagdProxyServiceManifest(ownerReferences)); err != nil && !errors.IsAlreadyExists(err) { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
func (f *FlagdProxyHandler) newFlagdProxyServiceManifest(ownerReferences []metav1.OwnerReference) *corev1.Service { | ||
return &corev1.Service{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: FlagdProxyServiceName, | ||
Namespace: f.config.Namespace, | ||
OwnerReferences: ownerReferences, | ||
}, | ||
Spec: corev1.ServiceSpec{ | ||
Selector: map[string]string{ | ||
"app.kubernetes.io/name": FlagdProxyDeploymentName, | ||
"app.kubernetes.io/managed-by": ManagedByAnnotationValue, | ||
}, | ||
Ports: []corev1.ServicePort{ | ||
{ | ||
Name: "flagd-proxy", | ||
Port: int32(f.config.Port), | ||
TargetPort: intstr.FromInt(f.config.Port), | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func (f *FlagdProxyHandler) newFlagdProxyManifest(ownerReferences []metav1.OwnerReference) *appsV1.Deployment { | ||
replicas := int32(1) | ||
args := []string{ | ||
"start", | ||
"--metrics-port", | ||
fmt.Sprintf("%d", f.config.MetricsPort), | ||
} | ||
if f.config.DebugLogging { | ||
args = append(args, "--debug") | ||
} | ||
return &appsV1.Deployment{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: FlagdProxyDeploymentName, | ||
Namespace: f.config.Namespace, | ||
Labels: map[string]string{ | ||
"app": FlagdProxyDeploymentName, | ||
"app.kubernetes.io/managed-by": ManagedByAnnotationValue, | ||
"app.kubernetes.io/version": f.config.Tag, | ||
}, | ||
OwnerReferences: ownerReferences, | ||
}, | ||
Spec: appsV1.DeploymentSpec{ | ||
Replicas: &replicas, | ||
Selector: &metav1.LabelSelector{ | ||
MatchLabels: map[string]string{ | ||
"app": FlagdProxyDeploymentName, | ||
}, | ||
}, | ||
Template: corev1.PodTemplateSpec{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Labels: map[string]string{ | ||
"app": FlagdProxyDeploymentName, | ||
"app.kubernetes.io/name": FlagdProxyDeploymentName, | ||
"app.kubernetes.io/managed-by": ManagedByAnnotationValue, | ||
"app.kubernetes.io/version": f.config.Tag, | ||
}, | ||
}, | ||
Spec: corev1.PodSpec{ | ||
ServiceAccountName: FlagdProxyServiceAccountName, | ||
Containers: []corev1.Container{ | ||
{ | ||
Image: fmt.Sprintf("%s:%s", f.config.Image, f.config.Tag), | ||
Name: FlagdProxyDeploymentName, | ||
Ports: []corev1.ContainerPort{ | ||
{ | ||
Name: "port", | ||
ContainerPort: int32(f.config.Port), | ||
}, | ||
{ | ||
Name: "metrics-port", | ||
ContainerPort: int32(f.config.MetricsPort), | ||
}, | ||
}, | ||
Args: args, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func (f *FlagdProxyHandler) doesFlagdProxyExist(ctx context.Context) (bool, 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 | ||
} | ||
// does not exist, is not ready, is in error | ||
return false, err | ||
} | ||
// exists, at least one replica ready, no error | ||
return true, nil | ||
} | ||
|
||
func (f *FlagdProxyHandler) getOwnerReference(ctx context.Context) (metav1.OwnerReference, 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 metav1.OwnerReference{ | ||
UID: d.GetUID(), | ||
Name: d.GetName(), | ||
APIVersion: d.APIVersion, | ||
Kind: d.Kind, | ||
}, nil | ||
|
||
} |
Oops, something went wrong.