Skip to content

Commit

Permalink
feat: improve deployment pattern (#344)
Browse files Browse the repository at this point in the history
Signed-off-by: James Milligan <james@omnant.co.uk>
Signed-off-by: James Milligan <75740990+james-milligan@users.noreply.github.com>
Co-authored-by: Skye Gill <gill.skye95@gmail.com>
  • Loading branch information
james-milligan and skyerus committed Feb 24, 2023
1 parent 8b779b1 commit 572ba96
Show file tree
Hide file tree
Showing 40 changed files with 2,002 additions and 559 deletions.
9 changes: 9 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,13 @@ resources:
kind: FlagSourceConfiguration
path: github.com/open-feature/open-feature-operator/apis/core/v1alpha1
version: v1alpha1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: openfeature.dev
group: core
kind: FlagSourceConfiguration
path: github.com/open-feature/open-feature-operator/apis/core/v1alpha3
version: v1alpha3
version: "3"
5 changes: 4 additions & 1 deletion apis/core/v1alpha1/featureflagconfiguration_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ import (
type FeatureFlagConfigurationSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file

// ServiceProvider [DEPRECATED]: superseded by FlagSourceConfiguration
// +optional
// +nullable
ServiceProvider *FeatureFlagServiceProvider `json:"serviceProvider"`
// SyncProvider [DEPRECATED]: superseded by FlagSourceConfiguration
// +optional
// +nullable
SyncProvider *FeatureFlagSyncProvider `json:"syncProvider"`
Expand All @@ -53,7 +56,7 @@ type FlagDSpec struct {
}

type FeatureFlagSyncProvider struct {
Name SyncProviderType `json:"name"`
Name string `json:"name"`
// +optional
// +nullable
HttpSyncConfiguration *HttpSyncConfiguration `json:"httpSyncConfiguration"`
Expand Down
4 changes: 3 additions & 1 deletion apis/core/v1alpha1/flagsourceconfiguration_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ limitations under the License.

package v1alpha1

import ctrl "sigs.k8s.io/controller-runtime"
import (
ctrl "sigs.k8s.io/controller-runtime"
)

// Hub marks this type as a conversion hub.
func (ffc *FlagSourceConfiguration) Hub() {}
Expand Down
166 changes: 111 additions & 55 deletions apis/core/v1alpha1/flagsourceconfiguration_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type SyncProviderType string

const (
SidecarEnvVarPrefix string = "SIDECAR_ENV_VAR_PREFIX"
InputConfigurationEnvVarPrefix string = "SIDECAR"
SidecarMetricPortEnvVar string = "METRICS_PORT"
SidecarPortEnvVar string = "PORT"
SidecarSocketPathEnvVar string = "SOCKET_PATH"
Expand All @@ -40,13 +41,12 @@ const (
SidecarDefaultSyncProviderEnvVar string = "SYNC_PROVIDER"
SidecarLogFormatEnvVar string = "LOG_FORMAT"
defaultSidecarEnvVarPrefix string = "FLAGD"
InputConfigurationEnvVarPrefix string = "SIDECAR"
defaultMetricPort int32 = 8014
DefaultMetricPort int32 = 8014
defaultPort int32 = 8013
defaultSocketPath string = ""
defaultEvaluator string = "json"
defaultImage string = "ghcr.io/open-feature/flagd"
// `INPUT_FLAGD_VERSION` is replaced in the `update-flagd` Makefile target
// INPUT_FLAGD_VERSION` is replaced in the `update-flagd` Makefile target
defaultTag string = "INPUT_FLAGD_VERSION"
defaultLogFormat string = "json"
SyncProviderKubernetes SyncProviderType = "kubernetes"
Expand Down Expand Up @@ -95,68 +95,134 @@ type FlagSourceConfigurationSpec struct {
// +optional
DefaultSyncProvider SyncProviderType `json:"defaultSyncProvider"`

// Sources defines the syncProviders and associated configuration to be applied to the sidecar
// +kubebuilder:validation:MinItems=1
Sources []Source `json:"sources"`

// EnvVars define the env vars to be applied to the sidecar, any env vars in FeatureFlagConfiguration CRs
// are added at the lowest index, all values will have the EnvVarPrefix applied
// +optional
EnvVars []corev1.EnvVar `json:"envVars"`

// EnvVarPrefix defines the prefix to be applied to all environment variables applied to the sidecar, default FLAGD
// +optional
EnvVarPrefix string `json:"envVarPrefix"`

// LogFormat allows for the sidecar log format to be overridden, defaults to 'json'
// +optional
LogFormat string `json:"logFormat"`

// RolloutOnChange dictates whether annotated deployments will be restarted when configuration changes are
// detected in this CR, defaults to false
// +optional
RolloutOnChange *bool `json:"rolloutOnChange"`
}

type Source struct {
Source string `json:"source"`
// +optional
Provider SyncProviderType `json:"provider"`
// +optional
HttpSyncBearerToken string `json:"httpSyncBearerToken"`
// LogFormat allows for the sidecar log format to be overridden, defaults to 'json'
// +optional
LogFormat string `json:"logFormat"`
}

// FlagSourceConfigurationStatus defines the observed state of FlagSourceConfiguration
type FlagSourceConfigurationStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}

//+kubebuilder:resource:shortName="fsc"
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:storageversion

// FlagSourceConfiguration is the Schema for the FlagSourceConfigurations API
type FlagSourceConfiguration struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec FlagSourceConfigurationSpec `json:"spec,omitempty"`
Status FlagSourceConfigurationStatus `json:"status,omitempty"`
}

//+kubebuilder:object:root=true

// FlagSourceConfigurationList contains a list of FlagSourceConfiguration
type FlagSourceConfigurationList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []FlagSourceConfiguration `json:"items"`
}

func NewFlagSourceConfigurationSpec() (*FlagSourceConfigurationSpec, error) {
fsc := &FlagSourceConfigurationSpec{
MetricsPort: defaultMetricPort,
MetricsPort: DefaultMetricPort,
Port: defaultPort,
SocketPath: defaultSocketPath,
SyncProviderArgs: []string{},
Evaluator: defaultEvaluator,
Image: defaultImage,
Tag: defaultTag,
Sources: []Source{},
EnvVars: []corev1.EnvVar{},
SyncProviderArgs: []string{},
DefaultSyncProvider: SyncProviderKubernetes,
EnvVarPrefix: defaultSidecarEnvVarPrefix,
LogFormat: defaultLogFormat,
RolloutOnChange: nil,
}

if metricsPort := os.Getenv(fmt.Sprintf("%s_%s", InputConfigurationEnvVarPrefix, SidecarMetricPortEnvVar)); metricsPort != "" {
if metricsPort := os.Getenv(envVarKey(InputConfigurationEnvVarPrefix, SidecarMetricPortEnvVar)); metricsPort != "" {
metricsPortI, err := strconv.Atoi(metricsPort)
if err != nil {
return fsc, fmt.Errorf("unable to parse metrics port value %s to int32: %w", metricsPort, err)
}
fsc.MetricsPort = int32(metricsPortI)
}

if port := os.Getenv(fmt.Sprintf("%s_%s", InputConfigurationEnvVarPrefix, SidecarPortEnvVar)); port != "" {
if port := os.Getenv(envVarKey(InputConfigurationEnvVarPrefix, SidecarPortEnvVar)); port != "" {
portI, err := strconv.Atoi(port)
if err != nil {
return fsc, fmt.Errorf("unable to parse sidecar port value %s to int32: %w", port, err)
}
fsc.Port = int32(portI)
}

if socketPath := os.Getenv(fmt.Sprintf("%s_%s", InputConfigurationEnvVarPrefix, SidecarSocketPathEnvVar)); socketPath != "" {
if socketPath := os.Getenv(envVarKey(InputConfigurationEnvVarPrefix, SidecarSocketPathEnvVar)); socketPath != "" {
fsc.SocketPath = socketPath
}

if evaluator := os.Getenv(fmt.Sprintf("%s_%s", InputConfigurationEnvVarPrefix, SidecarEvaluatorEnvVar)); evaluator != "" {
if evaluator := os.Getenv(envVarKey(InputConfigurationEnvVarPrefix, SidecarEvaluatorEnvVar)); evaluator != "" {
fsc.Evaluator = evaluator
}

if image := os.Getenv(fmt.Sprintf("%s_%s", InputConfigurationEnvVarPrefix, SidecarImageEnvVar)); image != "" {
if image := os.Getenv(envVarKey(InputConfigurationEnvVarPrefix, SidecarImageEnvVar)); image != "" {
fsc.Image = image
}

if tag := os.Getenv(fmt.Sprintf("%s_%s", InputConfigurationEnvVarPrefix, SidecarVersionEnvVar)); tag != "" {
if tag := os.Getenv(envVarKey(InputConfigurationEnvVarPrefix, SidecarVersionEnvVar)); tag != "" {
fsc.Tag = tag
}

if syncProviderArgs := os.Getenv(fmt.Sprintf("%s_%s", InputConfigurationEnvVarPrefix, SidecarProviderArgsEnvVar)); syncProviderArgs != "" {
if syncProviderArgs := os.Getenv(envVarKey(InputConfigurationEnvVarPrefix, SidecarProviderArgsEnvVar)); syncProviderArgs != "" {
fsc.SyncProviderArgs = strings.Split(syncProviderArgs, ",") // todo: add documentation for this
}

if syncProvider := os.Getenv(fmt.Sprintf("%s_%s", InputConfigurationEnvVarPrefix, SidecarDefaultSyncProviderEnvVar)); syncProvider != "" {
if syncProvider := os.Getenv(envVarKey(InputConfigurationEnvVarPrefix, SidecarDefaultSyncProviderEnvVar)); syncProvider != "" {
fsc.DefaultSyncProvider = SyncProviderType(syncProvider)
}

if logFormat := os.Getenv(fmt.Sprintf("%s_%s", InputConfigurationEnvVarPrefix, SidecarLogFormatEnvVar)); logFormat != "" {
fsc.LogFormat = logFormat
}

if envVarPrefix := os.Getenv(SidecarEnvVarPrefix); envVarPrefix != "" {
fsc.EnvVarPrefix = envVarPrefix
}

return fsc, nil
}

Expand All @@ -182,93 +248,75 @@ func (fc *FlagSourceConfigurationSpec) Merge(new *FlagSourceConfigurationSpec) {
if new.Tag != "" {
fc.Tag = new.Tag
}
if len(new.Sources) != 0 {
fc.Sources = append(fc.Sources, new.Sources...)
}
if len(new.EnvVars) != 0 {
fc.EnvVars = append(fc.EnvVars, new.EnvVars...)
}
if new.SyncProviderArgs != nil && len(new.SyncProviderArgs) > 0 {
fc.SyncProviderArgs = append(fc.SyncProviderArgs, new.SyncProviderArgs...)
}
if new.EnvVarPrefix != "" {
fc.EnvVarPrefix = new.EnvVarPrefix
}
if new.DefaultSyncProvider != "" {
fc.DefaultSyncProvider = new.DefaultSyncProvider
}
if new.LogFormat != "" {
fc.LogFormat = new.LogFormat
}
if new.RolloutOnChange != nil {
fc.RolloutOnChange = new.RolloutOnChange
}
}

func (fc *FlagSourceConfigurationSpec) ToEnvVars() []corev1.EnvVar {
envs := []corev1.EnvVar{}

prefix := defaultSidecarEnvVarPrefix
if p := os.Getenv(SidecarEnvVarPrefix); p != "" {
prefix = p
for _, envVar := range fc.EnvVars {
envs = append(envs, corev1.EnvVar{
Name: envVarKey(fc.EnvVarPrefix, envVar.Name),
Value: envVar.Value,
})
}

if fc.MetricsPort != defaultMetricPort {
if fc.MetricsPort != DefaultMetricPort {
envs = append(envs, corev1.EnvVar{
Name: fmt.Sprintf("%s_%s", prefix, SidecarMetricPortEnvVar),
Name: envVarKey(fc.EnvVarPrefix, SidecarMetricPortEnvVar),
Value: fmt.Sprintf("%d", fc.MetricsPort),
})
}

if fc.Port != defaultPort {
envs = append(envs, corev1.EnvVar{
Name: fmt.Sprintf("%s_%s", prefix, SidecarPortEnvVar),
Name: envVarKey(fc.EnvVarPrefix, SidecarPortEnvVar),
Value: fmt.Sprintf("%d", fc.Port),
})
}

if fc.Evaluator != defaultEvaluator {
envs = append(envs, corev1.EnvVar{
Name: fmt.Sprintf("%s_%s", prefix, SidecarEvaluatorEnvVar),
Name: envVarKey(fc.EnvVarPrefix, SidecarEvaluatorEnvVar),
Value: fc.Evaluator,
})
}

if fc.SocketPath != defaultSocketPath {
envs = append(envs, corev1.EnvVar{
Name: fmt.Sprintf("%s_%s", prefix, SidecarSocketPathEnvVar),
Name: envVarKey(fc.EnvVarPrefix, SidecarSocketPathEnvVar),
Value: fc.SocketPath,
})
}

if fc.LogFormat != defaultLogFormat {
envs = append(envs, corev1.EnvVar{
Name: fmt.Sprintf("%s_%s", prefix, SidecarLogFormatEnvVar),
Name: envVarKey(fc.EnvVarPrefix, SidecarLogFormatEnvVar),
Value: fc.LogFormat,
})
}
return envs
}

// FlagSourceConfigurationStatus defines the observed state of FlagSourceConfiguration
type FlagSourceConfigurationStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}

//+kubebuilder:resource:shortName="fsc"
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:storageversion

// FlagSourceConfiguration is the Schema for the FlagSourceConfigurations API
type FlagSourceConfiguration struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec FlagSourceConfigurationSpec `json:"spec,omitempty"`
Status FlagSourceConfigurationStatus `json:"status,omitempty"`
}

//+kubebuilder:object:root=true

// FlagSourceConfigurationList contains a list of FlagSourceConfiguration
type FlagSourceConfigurationList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []FlagSourceConfiguration `json:"items"`
}

func init() {
SchemeBuilder.Register(&FlagSourceConfiguration{}, &FlagSourceConfigurationList{})
return envs
}

func (s SyncProviderType) IsKubernetes() bool {
Expand All @@ -282,3 +330,11 @@ func (s SyncProviderType) IsHttp() bool {
func (s SyncProviderType) IsFilepath() bool {
return s == SyncProviderFilepath
}

func envVarKey(prefix string, suffix string) string {
return fmt.Sprintf("%s_%s", prefix, suffix)
}

func init() {
SchemeBuilder.Register(&FlagSourceConfiguration{}, &FlagSourceConfigurationList{})
}
32 changes: 32 additions & 0 deletions apis/core/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apis/core/v1alpha2/featureflagconfiguration_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func (src *FeatureFlagConfiguration) ConvertTo(dstRaw conversion.Hub) error {
}

if src.Spec.SyncProvider != nil {
dst.Spec.SyncProvider = &v1alpha1.FeatureFlagSyncProvider{Name: v1alpha1.SyncProviderType(src.Spec.SyncProvider.Name)}
dst.Spec.SyncProvider = &v1alpha1.FeatureFlagSyncProvider{Name: src.Spec.SyncProvider.Name}
if src.Spec.SyncProvider.HttpSyncConfiguration != nil {
dst.Spec.SyncProvider.HttpSyncConfiguration = &v1alpha1.HttpSyncConfiguration{
Target: src.Spec.SyncProvider.HttpSyncConfiguration.Target,
Expand Down
Loading

0 comments on commit 572ba96

Please sign in to comment.