Skip to content

Commit 51db913

Browse files
odubajDTtoddbaertKavindu-Dodan
authored
feat: introduce new CRD for in-process evaluation (#632)
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>
1 parent a8b7ad4 commit 51db913

30 files changed

+1927
-111
lines changed

PROJECT

+8-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ resources:
5656
path: github.com/open-feature/open-feature-operator/apis/core/v1beta1
5757
version: v1beta1
5858
webhooks:
59-
defaulting: false
6059
validation: true
6160
webhookVersion: v1
6261
- api:
@@ -76,4 +75,12 @@ resources:
7675
kind: Flagd
7776
path: github.com/open-feature/open-feature-operator/apis/core/v1beta1
7877
version: v1beta1
78+
- api:
79+
crdVersion: v1
80+
namespaced: true
81+
domain: openfeature.dev
82+
group: core
83+
kind: InProcessConfiguration
84+
path: github.com/open-feature/open-feature-operator/apis/core/v1beta1
85+
version: v1beta1
7986
version: "3"

apis/core/v1beta1/common/common.go

+70-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package common
22

3-
import "fmt"
3+
import (
4+
"fmt"
5+
6+
corev1 "k8s.io/api/core/v1"
7+
)
48

59
type SyncProviderType string
610

@@ -12,6 +16,39 @@ const (
1216
SyncProviderFlagdProxy SyncProviderType = "flagd-proxy"
1317
)
1418

19+
const (
20+
ManagementPortEnvVar string = "MANAGEMENT_PORT"
21+
PortEnvVar string = "PORT"
22+
HostEnvVar string = "HOST"
23+
TLSEnvVar string = "TLS"
24+
SocketPathEnvVar string = "SOCKET_PATH"
25+
OfflineFlagSourcePathEnvVar string = "OFFLINE_FLAG_SOURCE_PATH"
26+
SelectorEnvVar string = "SOURCE_SELECTOR"
27+
CacheEnvVar string = "CACHE"
28+
CacheMaxSizeEnvVar string = "MAX_CACHE_SIZE"
29+
ResolverEnvVar string = "RESOLVER"
30+
EvaluatorEnvVar string = "EVALUATOR"
31+
ImageEnvVar string = "IMAGE"
32+
VersionEnvVar string = "TAG"
33+
ProviderArgsEnvVar string = "PROVIDER_ARGS"
34+
DefaultSyncProviderEnvVar string = "SYNC_PROVIDER"
35+
LogFormatEnvVar string = "LOG_FORMAT"
36+
ProbesEnabledVar string = "PROBES_ENABLED"
37+
DefaultEnvVarPrefix string = "FLAGD"
38+
DefaultManagementPort int32 = 8014
39+
DefaultRPCPort int32 = 8013
40+
DefaultInProcessPort int32 = 8015
41+
DefaultEvaluator string = "json"
42+
DefaultLogFormat string = "json"
43+
DefaultProbesEnabled bool = true
44+
DefaultTLS bool = false
45+
DefaultHost string = "localhost"
46+
DefaultCache string = "lru"
47+
DefaultCacheMaxSize int32 = 1000
48+
InProcessResolverType string = "in-process"
49+
RPCResolverType string = "rpc"
50+
)
51+
1552
func (s SyncProviderType) IsKubernetes() bool {
1653
return s == SyncProviderKubernetes
1754
}
@@ -55,3 +92,35 @@ func FeatureFlagConfigurationId(namespace, name string) string {
5592
func FeatureFlagConfigMapKey(namespace, name string) string {
5693
return fmt.Sprintf("%s.flagd.json", FeatureFlagConfigurationId(namespace, name))
5794
}
95+
96+
func RemoveDuplicateEnvVars(input []corev1.EnvVar) []corev1.EnvVar {
97+
out := make([]corev1.EnvVar, 0, len(input))
98+
for i := len(input) - 1; i >= 0; i-- {
99+
if !isEnvVarNamePresent(out, input[i]) {
100+
out = append(out, input[i])
101+
}
102+
}
103+
return out
104+
}
105+
106+
func isEnvVarNamePresent(slice []corev1.EnvVar, item corev1.EnvVar) bool {
107+
for _, i := range slice {
108+
if i.Name == item.Name {
109+
return true
110+
}
111+
}
112+
return false
113+
}
114+
115+
func RemoveDuplicatesFromSlice[T comparable](input []T) []T {
116+
seen := make(map[T]bool)
117+
result := []T{}
118+
119+
for _, item := range input {
120+
if _, ok := seen[item]; !ok {
121+
seen[item] = true
122+
result = append(result, item)
123+
}
124+
}
125+
return result
126+
}

apis/core/v1beta1/common/common_test.go

+112
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"testing"
55

66
"github.com/stretchr/testify/require"
7+
corev1 "k8s.io/api/core/v1"
78
)
89

910
func Test_FeatureFlagSource_SyncProvider(t *testing.T) {
@@ -34,3 +35,114 @@ func Test_FLagSourceConfiguration_FeatureFlagConfigurationId(t *testing.T) {
3435
func Test_FLagSourceConfiguration_FeatureFlagConfigMapKey(t *testing.T) {
3536
require.Equal(t, "pre_suf.flagd.json", FeatureFlagConfigMapKey("pre", "suf"))
3637
}
38+
39+
func Test_RemoveDuplicateEnvVars(t *testing.T) {
40+
input1 := []corev1.EnvVar{
41+
{
42+
Name: "key1",
43+
Value: "val1",
44+
},
45+
{
46+
Name: "key2",
47+
Value: "val2",
48+
},
49+
{
50+
Name: "key1",
51+
Value: "val3",
52+
},
53+
}
54+
input2 := []corev1.EnvVar{
55+
{
56+
Name: "key1",
57+
Value: "val1",
58+
},
59+
{
60+
Name: "key2",
61+
Value: "val2",
62+
},
63+
{
64+
Name: "key3",
65+
Value: "val3",
66+
},
67+
}
68+
input3 := []corev1.EnvVar{
69+
{
70+
Name: "key1",
71+
Value: "val1",
72+
},
73+
{
74+
Name: "key2",
75+
Value: "val2",
76+
},
77+
{
78+
Name: "key1",
79+
ValueFrom: &corev1.EnvVarSource{
80+
SecretKeyRef: &corev1.SecretKeySelector{
81+
LocalObjectReference: corev1.LocalObjectReference{
82+
Name: "secret",
83+
},
84+
},
85+
},
86+
},
87+
}
88+
89+
out1 := RemoveDuplicateEnvVars(input1)
90+
require.Len(t, out1, 2)
91+
require.Contains(t, out1, corev1.EnvVar{
92+
Name: "key1",
93+
Value: "val3",
94+
})
95+
require.Contains(t, out1, corev1.EnvVar{
96+
Name: "key2",
97+
Value: "val2",
98+
})
99+
100+
out2 := RemoveDuplicateEnvVars(input2)
101+
require.Len(t, out2, 3)
102+
require.Contains(t, out2, corev1.EnvVar{
103+
Name: "key1",
104+
Value: "val1",
105+
})
106+
require.Contains(t, out2, corev1.EnvVar{
107+
Name: "key2",
108+
Value: "val2",
109+
})
110+
require.Contains(t, out2, corev1.EnvVar{
111+
Name: "key3",
112+
Value: "val3",
113+
})
114+
115+
out3 := RemoveDuplicateEnvVars(input3)
116+
require.Len(t, out3, 2)
117+
require.Contains(t, out3, corev1.EnvVar{
118+
Name: "key1",
119+
ValueFrom: &corev1.EnvVarSource{
120+
SecretKeyRef: &corev1.SecretKeySelector{
121+
LocalObjectReference: corev1.LocalObjectReference{
122+
Name: "secret",
123+
},
124+
},
125+
},
126+
})
127+
require.Contains(t, out3, corev1.EnvVar{
128+
Name: "key2",
129+
Value: "val2",
130+
})
131+
}
132+
133+
func Test_RRemoveDuplicatesFromSlice(t *testing.T) {
134+
input1 := []string{
135+
"some", "input", "duplicate", "some",
136+
}
137+
input2 := []int{
138+
1, 2, 3, 4, 2,
139+
}
140+
141+
require.Equal(t, RemoveDuplicatesFromSlice(input1), []string{
142+
"some", "input", "duplicate",
143+
})
144+
145+
require.Equal(t, RemoveDuplicatesFromSlice(input2), []int{
146+
1, 2, 3, 4,
147+
})
148+
}

apis/core/v1beta1/featureflagsource_types.go

+18-32
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,6 @@ import (
2424
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2525
)
2626

27-
const (
28-
SidecarEnvVarPrefix string = "SIDECAR_ENV_VAR_PREFIX"
29-
InputConfigurationEnvVarPrefix string = "SIDECAR"
30-
SidecarMetricPortEnvVar string = "MANAGEMENT_PORT"
31-
SidecarPortEnvVar string = "PORT"
32-
SidecarSocketPathEnvVar string = "SOCKET_PATH"
33-
SidecarEvaluatorEnvVar string = "EVALUATOR"
34-
SidecarImageEnvVar string = "IMAGE"
35-
SidecarVersionEnvVar string = "TAG"
36-
SidecarProviderArgsEnvVar string = "PROVIDER_ARGS"
37-
SidecarDefaultSyncProviderEnvVar string = "SYNC_PROVIDER"
38-
SidecarLogFormatEnvVar string = "LOG_FORMAT"
39-
SidecarProbesEnabledVar string = "PROBES_ENABLED"
40-
defaultSidecarEnvVarPrefix string = "FLAGD"
41-
DefaultManagementPort int32 = 8014
42-
defaultPort int32 = 8013
43-
defaultSocketPath string = ""
44-
defaultEvaluator string = "json"
45-
defaultLogFormat string = "json"
46-
defaultProbesEnabled bool = true
47-
)
48-
4927
// FeatureFlagSourceSpec defines the desired state of FeatureFlagSource
5028
type FeatureFlagSourceSpec struct {
5129
// ManagemetPort defines the port to serve management on, defaults to 8014
@@ -197,9 +175,11 @@ func (fc *FeatureFlagSourceSpec) Merge(new *FeatureFlagSourceSpec) {
197175
}
198176
if len(new.EnvVars) != 0 {
199177
fc.EnvVars = append(fc.EnvVars, new.EnvVars...)
178+
fc.EnvVars = common.RemoveDuplicateEnvVars(fc.EnvVars)
200179
}
201180
if len(new.SyncProviderArgs) != 0 {
202181
fc.SyncProviderArgs = append(fc.SyncProviderArgs, new.SyncProviderArgs...)
182+
fc.SyncProviderArgs = common.RemoveDuplicatesFromSlice[string](fc.SyncProviderArgs)
203183
}
204184
if new.EnvVarPrefix != "" {
205185
fc.EnvVarPrefix = new.EnvVarPrefix
@@ -234,40 +214,46 @@ func (fc *FeatureFlagSourceSpec) ToEnvVars() []corev1.EnvVar {
234214
})
235215
}
236216

237-
if fc.ManagementPort != DefaultManagementPort {
217+
if fc.ManagementPort != common.DefaultManagementPort {
238218
envs = append(envs, corev1.EnvVar{
239-
Name: common.EnvVarKey(fc.EnvVarPrefix, SidecarMetricPortEnvVar),
219+
Name: common.EnvVarKey(fc.EnvVarPrefix, common.ManagementPortEnvVar),
240220
Value: fmt.Sprintf("%d", fc.ManagementPort),
241221
})
242222
}
243223

244-
if fc.Port != defaultPort {
224+
if fc.Port != common.DefaultRPCPort {
245225
envs = append(envs, corev1.EnvVar{
246-
Name: common.EnvVarKey(fc.EnvVarPrefix, SidecarPortEnvVar),
226+
Name: common.EnvVarKey(fc.EnvVarPrefix, common.PortEnvVar),
247227
Value: fmt.Sprintf("%d", fc.Port),
248228
})
249229
}
250230

251-
if fc.Evaluator != defaultEvaluator {
231+
if fc.Evaluator != common.DefaultEvaluator {
252232
envs = append(envs, corev1.EnvVar{
253-
Name: common.EnvVarKey(fc.EnvVarPrefix, SidecarEvaluatorEnvVar),
233+
Name: common.EnvVarKey(fc.EnvVarPrefix, common.EvaluatorEnvVar),
254234
Value: fc.Evaluator,
255235
})
256236
}
257237

258-
if fc.SocketPath != defaultSocketPath {
238+
if fc.SocketPath != "" {
259239
envs = append(envs, corev1.EnvVar{
260-
Name: common.EnvVarKey(fc.EnvVarPrefix, SidecarSocketPathEnvVar),
240+
Name: common.EnvVarKey(fc.EnvVarPrefix, common.SocketPathEnvVar),
261241
Value: fc.SocketPath,
262242
})
263243
}
264244

265-
if fc.LogFormat != defaultLogFormat {
245+
if fc.LogFormat != common.DefaultLogFormat {
266246
envs = append(envs, corev1.EnvVar{
267-
Name: common.EnvVarKey(fc.EnvVarPrefix, SidecarLogFormatEnvVar),
247+
Name: common.EnvVarKey(fc.EnvVarPrefix, common.LogFormatEnvVar),
268248
Value: fc.LogFormat,
269249
})
270250
}
271251

252+
// sets the FLAGD_RESOLVER var to "rpc" to configure the provider for RPC evaluation mode
253+
envs = append(envs, corev1.EnvVar{
254+
Name: common.EnvVarKey(fc.EnvVarPrefix, common.ResolverEnvVar),
255+
Value: common.RPCResolverType,
256+
})
257+
272258
return envs
273259
}

0 commit comments

Comments
 (0)