Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deploy unleash-edge alongside local featureFlags #1115

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apis/cloud.redhat.com/v1alpha1/clowdenvironment_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,8 @@ type ObjectStoreConfig struct {
}

type FeatureFlagsImages struct {
Unleash string `json:"unleash,omitempty"`
Unleash string `json:"unleash,omitempty"`
UnleashEdge string `json:"unleashEdge,omitempty"`
}

// FeatureFlagsMode details the mode of operation of the Clowder FeatureFlags
Expand Down
21 changes: 11 additions & 10 deletions controllers/cloud.redhat.com/clowderconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@ import (

type ClowderConfig struct {
Images struct {
MBOP string `json:"mbop"`
Caddy string `json:"caddy"`
CaddyGateway string `json:"caddyGateway"`
Keycloak string `json:"Keycloak"`
Mocktitlements string `json:"mocktitlements"`
CaddyReverseProxy string `json:"caddyReverseProxy"`
ObjectStoreMinio string `json:"objectStoreMinio"`
FeatureFlagsUnleash string `json:"featureFlagsUnleash"`
TokenRefresher string `json:"tokenRefresher"`
OtelCollector string `json:"otelCollector"`
MBOP string `json:"mbop"`
Caddy string `json:"caddy"`
CaddyGateway string `json:"caddyGateway"`
Keycloak string `json:"Keycloak"`
Mocktitlements string `json:"mocktitlements"`
CaddyReverseProxy string `json:"caddyReverseProxy"`
ObjectStoreMinio string `json:"objectStoreMinio"`
FeatureFlagsUnleash string `json:"featureFlagsUnleash"`
FeatureFlagsUnleashEdge string `json:"featureFlagsUnleashEdge"`
TokenRefresher string `json:"tokenRefresher"`
OtelCollector string `json:"otelCollector"`
} `json:"images"`
DebugOptions struct {
Logging struct {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,20 @@ import (
"github.com/RedHatInsights/rhc-osdk-utils/utils"
)

const featureFlagsPort = 4242

// LocalFFDeployment is the ident referring to the local Feature Flags deployment object.
var LocalFFDeployment = rc.NewSingleResourceIdent(ProvName, "ff_deployment", &apps.Deployment{})

// LocalFFService is the ident referring to the local Feature Flags service object.
var LocalFFService = rc.NewSingleResourceIdent(ProvName, "ff_service", &core.Service{})

// LocalFFEdgeDeployment is the ident referring to the local Unleash edge deployment object.
var LocalFFEdgeDeployment = rc.NewSingleResourceIdent(ProvName, "ff_edge_deployment", &apps.Deployment{})

// LocalFFEdgeService is the ident referring to the local Unleash edge service object.
var LocalFFEdgeService = rc.NewSingleResourceIdent(ProvName, "ff_service", &core.Service{})

// LocalFFSecret is the ident referring to the local Feature Flags secret object.
var LocalFFSecret = rc.NewSingleResourceIdent(ProvName, "ff_secret", &core.Secret{})

Expand All @@ -53,6 +61,8 @@ func NewLocalFeatureFlagsProvider(p *providers.Provider) (providers.ClowderProvi
p.Cache.AddPossibleGVKFromIdent(
LocalFFDeployment,
LocalFFService,
LocalFFEdgeDeployment,
LocalFFEdgeService,
LocalFFSecret,
LocalFFDBDeployment,
LocalFFDBService,
Expand Down Expand Up @@ -84,6 +94,15 @@ func (ff *localFeatureFlagsProvider) EnvProvide() error {
return err
}

objList2 := []rc.ResourceIdent{
LocalFFEdgeDeployment,
LocalFFEdgeService,
}

if err := providers.CachedMakeComponent(ff.Cache, objList2, ff.Env, "featureflags-edge", makeLocalFeatureFlagsEdge, false, ff.Env.IsNodePort()); err != nil {
return err
}

namespacedNameDb := types.NamespacedName{
Name: "featureflags-db",
Namespace: ff.Env.Status.TargetNamespace,
Expand Down Expand Up @@ -229,7 +248,7 @@ func makeLocalFeatureFlags(o obj.ClowdObject, objMap providers.ObjectMap, _ bool

dd.Spec.Template.ObjectMeta.Labels = labels

port := int32(4242)
port := int32(featureFlagsPort)

envVars := []core.EnvVar{
{
Expand Down Expand Up @@ -257,7 +276,7 @@ func makeLocalFeatureFlags(o obj.ClowdObject, objMap providers.ObjectMap, _ bool
Path: "/health",
Port: intstr.IntOrString{
Type: intstr.Int,
IntVal: 4242,
IntVal: featureFlagsPort,
},
},
}
Expand Down Expand Up @@ -319,3 +338,106 @@ func makeLocalFeatureFlags(o obj.ClowdObject, objMap providers.ObjectMap, _ bool
utils.MakeService(svc, nn, labels, servicePorts, o, nodePort)
return nil
}

func makeLocalFeatureFlagsEdge(o obj.ClowdObject, objMap providers.ObjectMap, _ bool, nodePort bool) error {

nnFF := providers.GetNamespacedName(o, "featureflags")
nn := providers.GetNamespacedName(o, "featureflags-edge")

dd := objMap[LocalFFEdgeDeployment].(*apps.Deployment)
svc := objMap[LocalFFEdgeService].(*core.Service)

labels := o.GetLabels()
labels["env-app"] = nn.Name
labels["service"] = "featureflags-edge"
labeler := utils.MakeLabeler(nn, labels, o)

labeler(dd)

replicas := int32(1)

dd.Spec.Replicas = &replicas
dd.Spec.Selector = &metav1.LabelSelector{MatchLabels: labels}

dd.Spec.Template.ObjectMeta.Labels = labels

portEdge := int32(3063)

envVarsEdge := []core.EnvVar{
{
// communication with the main featureflags service on localhost
Name: "UPSTREAM_URL",
Value: fmt.Sprintf("http://%s:%d", nnFF.Name, featureFlagsPort),
},
}
envVarsEdge = provutils.AppendEnvVarsFromSecret(envVarsEdge, nnFF.Name,
provutils.NewSecretEnvVar("TOKENS", "clientAccessToken"))

portsEdge := []core.ContainerPort{{
Name: "service",
ContainerPort: portEdge,
Protocol: "TCP",
}}

readinessProbeEdge := core.Probe{
ProbeHandler: core.ProbeHandler{
Exec: &core.ExecAction{
Command: []string{"/unleash-edge", "ready"},
},
},
InitialDelaySeconds: 1,
TimeoutSeconds: 5,
PeriodSeconds: 30,
SuccessThreshold: 1,
FailureThreshold: 3,
}

livenessProbeEdge := core.Probe{
ProbeHandler: core.ProbeHandler{
Exec: &core.ExecAction{
Command: []string{"/unleash-edge", "health"},
},
},
InitialDelaySeconds: 30,
TimeoutSeconds: 5,
PeriodSeconds: 30,
SuccessThreshold: 1,
FailureThreshold: 3,
}

env, ok := o.(*crd.ClowdEnvironment)
if !ok {
return fmt.Errorf("could not get env")
}

ce := core.Container{
Name: nn.Name,
Image: GetFeatureFlagsUnleashEdgeImage(env),
Env: envVarsEdge,
Ports: portsEdge,
Args: []string{"edge"},
ReadinessProbe: &readinessProbeEdge,
LivenessProbe: &livenessProbeEdge,
ImagePullPolicy: core.PullIfNotPresent,
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
"memory": resource.MustParse("100Mi"),
"cpu": resource.MustParse("100m"),
},
},
}

dd.Spec.Template.Spec.Containers = []core.Container{ce}
dd.Spec.Template.SetLabels(labels)

servicePorts := []core.ServicePort{{
Name: "featureflags-edge",
Port: portEdge,
Protocol: "TCP",
TargetPort: intstr.FromInt(int(portEdge)),
}}

utils.MakeService(svc, nn, labels, servicePorts, o, nodePort)

return nil
}
11 changes: 11 additions & 0 deletions controllers/cloud.redhat.com/providers/featureflags/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
)

var DefaultImageFeatureFlagsUnleash = "unleashorg/unleash-server:5.6.9"
var DefaultImageFeatureFlagsUnleashEdge = "unleashorg/unleash-edge:v19.6.3"

func GetFeatureFlagsUnleashImage(env *crd.ClowdEnvironment) string {
if env.Spec.Providers.FeatureFlags.Images.Unleash != "" {
Expand All @@ -21,6 +22,16 @@ func GetFeatureFlagsUnleashImage(env *crd.ClowdEnvironment) string {
return DefaultImageFeatureFlagsUnleash
}

func GetFeatureFlagsUnleashEdgeImage(env *crd.ClowdEnvironment) string {
if env.Spec.Providers.FeatureFlags.Images.UnleashEdge != "" {
return env.Spec.Providers.FeatureFlags.Images.UnleashEdge
}
if clowderconfig.LoadedConfig.Images.FeatureFlagsUnleashEdge != "" {
return clowderconfig.LoadedConfig.Images.FeatureFlagsUnleashEdge
}
return DefaultImageFeatureFlagsUnleashEdge
}

// ProvName identifies the featureflags provider.
var ProvName = "featureflags"

Expand Down
23 changes: 23 additions & 0 deletions tests/kuttl/test-ff-local/test_feature_flags.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ ADMIN_TOKEN=$(kubectl -n test-ff-local get secret test-ff-local-featureflags -o
CLIENT_TOKEN=$(kubectl -n test-ff-local get secret test-ff-local-featureflags -o json | jq -r '.data.clientAccessToken | @base64d')
FEATURE_TOGGLE_NAME='my-feature-toggle-1'

get_request_edge() {

local TOKEN="$1"
local ENDPOINT="$2"

kubectl exec -n test-ff-local "$FEATURE_FLAGS_POD" -- wget -q -O- \
--header "Authorization: $TOKEN" "test-ff-local-featureflags-edge:3063${ENDPOINT}"
}

get_request() {

local TOKEN="$1"
Expand Down Expand Up @@ -59,3 +68,17 @@ if [ 'true' != "$(get_request "$CLIENT_TOKEN" "/api/client/features/$FEATURE_TOG
echo "Feature toggle '$FEATURE_TOGGLE_NAME' should be enabled"
exit 1
fi

# Unleash seems to refresh the cache every 5 seconds, didn't find a way to force it
# see https://github.com/Unleash/unleash-edge/blob/12cf9e3f87099d3c0dce1884bcd305604c1e68ff/server/src/http/refresher/feature_refresher.rs#L443
echo "Waiting for edge to sync"
sleep 5

if ! get_request_edge "$CLIENT_TOKEN" "/api/client/features/$FEATURE_TOGGLE_NAME"; then
echo "Feature toggle '$FEATURE_TOGGLE_NAME' should be available through edge"
fi

if [ 'true' != "$(get_request_edge "$CLIENT_TOKEN" "/api/client/features/$FEATURE_TOGGLE_NAME" | jq '.enabled==true')" ]; then
echo "Feature toggle '$FEATURE_TOGGLE_NAME' should be enabled"
exit 1
fi
Loading