Skip to content

Commit

Permalink
feat: introduce new CRD for in-process evaluation (#632)
Browse files Browse the repository at this point in the history
Signed-off-by: odubajDT <ondrej.dubaj@dynatrace.com>
Signed-off-by: odubajDT <93584209+odubajDT@users.noreply.github.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Todd Baert <toddbaert@gmail.com>
Co-authored-by: Kavindu Dodanduwa <Kavindu-Dodan@users.noreply.github.com>
  • Loading branch information
4 people authored May 27, 2024
1 parent a8b7ad4 commit 51db913
Show file tree
Hide file tree
Showing 30 changed files with 1,927 additions and 111 deletions.
9 changes: 8 additions & 1 deletion PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ resources:
path: github.com/open-feature/open-feature-operator/apis/core/v1beta1
version: v1beta1
webhooks:
defaulting: false
validation: true
webhookVersion: v1
- api:
Expand All @@ -76,4 +75,12 @@ resources:
kind: Flagd
path: github.com/open-feature/open-feature-operator/apis/core/v1beta1
version: v1beta1
- api:
crdVersion: v1
namespaced: true
domain: openfeature.dev
group: core
kind: InProcessConfiguration
path: github.com/open-feature/open-feature-operator/apis/core/v1beta1
version: v1beta1
version: "3"
71 changes: 70 additions & 1 deletion apis/core/v1beta1/common/common.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package common

import "fmt"
import (
"fmt"

corev1 "k8s.io/api/core/v1"
)

type SyncProviderType string

Expand All @@ -12,6 +16,39 @@ const (
SyncProviderFlagdProxy SyncProviderType = "flagd-proxy"
)

const (
ManagementPortEnvVar string = "MANAGEMENT_PORT"
PortEnvVar string = "PORT"
HostEnvVar string = "HOST"
TLSEnvVar string = "TLS"
SocketPathEnvVar string = "SOCKET_PATH"
OfflineFlagSourcePathEnvVar string = "OFFLINE_FLAG_SOURCE_PATH"
SelectorEnvVar string = "SOURCE_SELECTOR"
CacheEnvVar string = "CACHE"
CacheMaxSizeEnvVar string = "MAX_CACHE_SIZE"
ResolverEnvVar string = "RESOLVER"
EvaluatorEnvVar string = "EVALUATOR"
ImageEnvVar string = "IMAGE"
VersionEnvVar string = "TAG"
ProviderArgsEnvVar string = "PROVIDER_ARGS"
DefaultSyncProviderEnvVar string = "SYNC_PROVIDER"
LogFormatEnvVar string = "LOG_FORMAT"
ProbesEnabledVar string = "PROBES_ENABLED"
DefaultEnvVarPrefix string = "FLAGD"
DefaultManagementPort int32 = 8014
DefaultRPCPort int32 = 8013
DefaultInProcessPort int32 = 8015
DefaultEvaluator string = "json"
DefaultLogFormat string = "json"
DefaultProbesEnabled bool = true
DefaultTLS bool = false
DefaultHost string = "localhost"
DefaultCache string = "lru"
DefaultCacheMaxSize int32 = 1000
InProcessResolverType string = "in-process"
RPCResolverType string = "rpc"
)

func (s SyncProviderType) IsKubernetes() bool {
return s == SyncProviderKubernetes
}
Expand Down Expand Up @@ -55,3 +92,35 @@ func FeatureFlagConfigurationId(namespace, name string) string {
func FeatureFlagConfigMapKey(namespace, name string) string {
return fmt.Sprintf("%s.flagd.json", FeatureFlagConfigurationId(namespace, name))
}

func RemoveDuplicateEnvVars(input []corev1.EnvVar) []corev1.EnvVar {
out := make([]corev1.EnvVar, 0, len(input))
for i := len(input) - 1; i >= 0; i-- {
if !isEnvVarNamePresent(out, input[i]) {
out = append(out, input[i])
}
}
return out
}

func isEnvVarNamePresent(slice []corev1.EnvVar, item corev1.EnvVar) bool {
for _, i := range slice {
if i.Name == item.Name {
return true
}
}
return false
}

func RemoveDuplicatesFromSlice[T comparable](input []T) []T {
seen := make(map[T]bool)
result := []T{}

for _, item := range input {
if _, ok := seen[item]; !ok {
seen[item] = true
result = append(result, item)
}
}
return result
}
112 changes: 112 additions & 0 deletions apis/core/v1beta1/common/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
)

func Test_FeatureFlagSource_SyncProvider(t *testing.T) {
Expand Down Expand Up @@ -34,3 +35,114 @@ func Test_FLagSourceConfiguration_FeatureFlagConfigurationId(t *testing.T) {
func Test_FLagSourceConfiguration_FeatureFlagConfigMapKey(t *testing.T) {
require.Equal(t, "pre_suf.flagd.json", FeatureFlagConfigMapKey("pre", "suf"))
}

func Test_RemoveDuplicateEnvVars(t *testing.T) {
input1 := []corev1.EnvVar{
{
Name: "key1",
Value: "val1",
},
{
Name: "key2",
Value: "val2",
},
{
Name: "key1",
Value: "val3",
},
}
input2 := []corev1.EnvVar{
{
Name: "key1",
Value: "val1",
},
{
Name: "key2",
Value: "val2",
},
{
Name: "key3",
Value: "val3",
},
}
input3 := []corev1.EnvVar{
{
Name: "key1",
Value: "val1",
},
{
Name: "key2",
Value: "val2",
},
{
Name: "key1",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "secret",
},
},
},
},
}

out1 := RemoveDuplicateEnvVars(input1)
require.Len(t, out1, 2)
require.Contains(t, out1, corev1.EnvVar{
Name: "key1",
Value: "val3",
})
require.Contains(t, out1, corev1.EnvVar{
Name: "key2",
Value: "val2",
})

out2 := RemoveDuplicateEnvVars(input2)
require.Len(t, out2, 3)
require.Contains(t, out2, corev1.EnvVar{
Name: "key1",
Value: "val1",
})
require.Contains(t, out2, corev1.EnvVar{
Name: "key2",
Value: "val2",
})
require.Contains(t, out2, corev1.EnvVar{
Name: "key3",
Value: "val3",
})

out3 := RemoveDuplicateEnvVars(input3)
require.Len(t, out3, 2)
require.Contains(t, out3, corev1.EnvVar{
Name: "key1",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "secret",
},
},
},
})
require.Contains(t, out3, corev1.EnvVar{
Name: "key2",
Value: "val2",
})
}

func Test_RRemoveDuplicatesFromSlice(t *testing.T) {
input1 := []string{
"some", "input", "duplicate", "some",
}
input2 := []int{
1, 2, 3, 4, 2,
}

require.Equal(t, RemoveDuplicatesFromSlice(input1), []string{
"some", "input", "duplicate",
})

require.Equal(t, RemoveDuplicatesFromSlice(input2), []int{
1, 2, 3, 4,
})
}
50 changes: 18 additions & 32 deletions apis/core/v1beta1/featureflagsource_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
SidecarEnvVarPrefix string = "SIDECAR_ENV_VAR_PREFIX"
InputConfigurationEnvVarPrefix string = "SIDECAR"
SidecarMetricPortEnvVar string = "MANAGEMENT_PORT"
SidecarPortEnvVar string = "PORT"
SidecarSocketPathEnvVar string = "SOCKET_PATH"
SidecarEvaluatorEnvVar string = "EVALUATOR"
SidecarImageEnvVar string = "IMAGE"
SidecarVersionEnvVar string = "TAG"
SidecarProviderArgsEnvVar string = "PROVIDER_ARGS"
SidecarDefaultSyncProviderEnvVar string = "SYNC_PROVIDER"
SidecarLogFormatEnvVar string = "LOG_FORMAT"
SidecarProbesEnabledVar string = "PROBES_ENABLED"
defaultSidecarEnvVarPrefix string = "FLAGD"
DefaultManagementPort int32 = 8014
defaultPort int32 = 8013
defaultSocketPath string = ""
defaultEvaluator string = "json"
defaultLogFormat string = "json"
defaultProbesEnabled bool = true
)

// FeatureFlagSourceSpec defines the desired state of FeatureFlagSource
type FeatureFlagSourceSpec struct {
// ManagemetPort defines the port to serve management on, defaults to 8014
Expand Down Expand Up @@ -197,9 +175,11 @@ func (fc *FeatureFlagSourceSpec) Merge(new *FeatureFlagSourceSpec) {
}
if len(new.EnvVars) != 0 {
fc.EnvVars = append(fc.EnvVars, new.EnvVars...)
fc.EnvVars = common.RemoveDuplicateEnvVars(fc.EnvVars)
}
if len(new.SyncProviderArgs) != 0 {
fc.SyncProviderArgs = append(fc.SyncProviderArgs, new.SyncProviderArgs...)
fc.SyncProviderArgs = common.RemoveDuplicatesFromSlice[string](fc.SyncProviderArgs)
}
if new.EnvVarPrefix != "" {
fc.EnvVarPrefix = new.EnvVarPrefix
Expand Down Expand Up @@ -234,40 +214,46 @@ func (fc *FeatureFlagSourceSpec) ToEnvVars() []corev1.EnvVar {
})
}

if fc.ManagementPort != DefaultManagementPort {
if fc.ManagementPort != common.DefaultManagementPort {
envs = append(envs, corev1.EnvVar{
Name: common.EnvVarKey(fc.EnvVarPrefix, SidecarMetricPortEnvVar),
Name: common.EnvVarKey(fc.EnvVarPrefix, common.ManagementPortEnvVar),
Value: fmt.Sprintf("%d", fc.ManagementPort),
})
}

if fc.Port != defaultPort {
if fc.Port != common.DefaultRPCPort {
envs = append(envs, corev1.EnvVar{
Name: common.EnvVarKey(fc.EnvVarPrefix, SidecarPortEnvVar),
Name: common.EnvVarKey(fc.EnvVarPrefix, common.PortEnvVar),
Value: fmt.Sprintf("%d", fc.Port),
})
}

if fc.Evaluator != defaultEvaluator {
if fc.Evaluator != common.DefaultEvaluator {
envs = append(envs, corev1.EnvVar{
Name: common.EnvVarKey(fc.EnvVarPrefix, SidecarEvaluatorEnvVar),
Name: common.EnvVarKey(fc.EnvVarPrefix, common.EvaluatorEnvVar),
Value: fc.Evaluator,
})
}

if fc.SocketPath != defaultSocketPath {
if fc.SocketPath != "" {
envs = append(envs, corev1.EnvVar{
Name: common.EnvVarKey(fc.EnvVarPrefix, SidecarSocketPathEnvVar),
Name: common.EnvVarKey(fc.EnvVarPrefix, common.SocketPathEnvVar),
Value: fc.SocketPath,
})
}

if fc.LogFormat != defaultLogFormat {
if fc.LogFormat != common.DefaultLogFormat {
envs = append(envs, corev1.EnvVar{
Name: common.EnvVarKey(fc.EnvVarPrefix, SidecarLogFormatEnvVar),
Name: common.EnvVarKey(fc.EnvVarPrefix, common.LogFormatEnvVar),
Value: fc.LogFormat,
})
}

// sets the FLAGD_RESOLVER var to "rpc" to configure the provider for RPC evaluation mode
envs = append(envs, corev1.EnvVar{
Name: common.EnvVarKey(fc.EnvVarPrefix, common.ResolverEnvVar),
Value: common.RPCResolverType,
})

return envs
}
Loading

0 comments on commit 51db913

Please sign in to comment.