diff --git a/core/go.mod b/core/go.mod index 16952d76c..3ea8777f3 100644 --- a/core/go.mod +++ b/core/go.mod @@ -11,7 +11,7 @@ require ( github.com/diegoholiveira/jsonlogic/v3 v3.3.2 github.com/fsnotify/fsnotify v1.7.0 github.com/golang/mock v1.6.0 - github.com/open-feature/open-feature-operator v0.2.36 + github.com/open-feature/open-feature-operator v0.2.37-0.20231108054703-a97d336468d5 github.com/open-feature/schemas v0.2.8 github.com/prometheus/client_golang v1.17.0 github.com/robfig/cron v1.2.0 @@ -61,7 +61,6 @@ require ( github.com/go-openapi/swag v0.22.3 // indirect github.com/go-task/slim-sprig v2.20.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect @@ -95,15 +94,12 @@ require ( golang.org/x/term v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/api v0.28.3 // indirect - k8s.io/apiextensions-apiserver v0.28.3 // indirect - k8s.io/component-base v0.28.3 // indirect k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect diff --git a/core/go.sum b/core/go.sum index 63205ab86..1db6db607 100644 --- a/core/go.sum +++ b/core/go.sum @@ -488,8 +488,6 @@ github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -638,8 +636,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/open-feature/open-feature-operator v0.2.36 h1:dzyZh9JSIRvXkfpM9ynYplNk7vjQFLs9sd5aHhF48z4= -github.com/open-feature/open-feature-operator v0.2.36/go.mod h1:nM7T4oGQukeGmcAFkQm0uwt8WFdDb5hYPjXkm7pHhX4= +github.com/open-feature/open-feature-operator v0.2.37-0.20231108054703-a97d336468d5 h1:ONgFdsDH0uAS3qrmZEQjYqFtqKw/MCkbbQSE33bGBsU= +github.com/open-feature/open-feature-operator v0.2.37-0.20231108054703-a97d336468d5/go.mod h1:nM7T4oGQukeGmcAFkQm0uwt8WFdDb5hYPjXkm7pHhX4= github.com/open-feature/schemas v0.2.8 h1:oA75hJXpOd9SFgmNI2IAxWZkwzQPUDm7Jyyh3q489wM= github.com/open-feature/schemas v0.2.8/go.mod h1:vj+rfTsOLlh5PtGGkAbitnJmFPYuTHXTjOy13kzNgKQ= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -1054,7 +1052,6 @@ golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNq golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= -gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1315,13 +1312,10 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM= k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc= k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08= -k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc= k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A= k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8= k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4= k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo= -k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI= -k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8= k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= diff --git a/core/pkg/sync/kubernetes/kubernetes_sync.go b/core/pkg/sync/kubernetes/kubernetes_sync.go index 4f2354d18..730771854 100644 --- a/core/pkg/sync/kubernetes/kubernetes_sync.go +++ b/core/pkg/sync/kubernetes/kubernetes_sync.go @@ -2,6 +2,7 @@ package kubernetes import ( "context" + "encoding/json" "fmt" "os" "strings" @@ -10,7 +11,7 @@ import ( "github.com/open-feature/flagd/core/pkg/logger" "github.com/open-feature/flagd/core/pkg/sync" - "github.com/open-feature/open-feature-operator/apis/core/v1alpha1" + "github.com/open-feature/open-feature-operator/apis/core/v1beta1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/dynamic" @@ -23,9 +24,9 @@ import ( ) var ( - resyncPeriod = 1 * time.Minute - apiVersion = fmt.Sprintf("%s/%s", v1alpha1.GroupVersion.Group, v1alpha1.GroupVersion.Version) - featureFlagConfigurationResource = v1alpha1.GroupVersion.WithResource("featureflagconfigurations") + resyncPeriod = 1 * time.Minute + apiVersion = fmt.Sprintf("%s/%s", v1beta1.GroupVersion.Group, v1beta1.GroupVersion.Version) + featureFlagResource = v1beta1.GroupVersion.WithResource("featureflags") ) type Sync struct { @@ -89,15 +90,15 @@ func (k *Sync) Init(_ context.Context) error { return fmt.Errorf("unable to parse uri %s: %w", k.URI, err) } - if err := v1alpha1.AddToScheme(scheme.Scheme); err != nil { - return fmt.Errorf("unable to v1alpha1 types to scheme: %w", err) + if err := v1beta1.AddToScheme(scheme.Scheme); err != nil { + return fmt.Errorf("unable to v1beta1 types to scheme: %w", err) } // The created informer will not do resyncs if the given defaultEventHandlerResyncPeriod is zero. // For more details on resync implications refer to tools/cache/shared_informer.go factory := dynamicinformer.NewFilteredDynamicSharedInformerFactory(k.dynamicClient, resyncPeriod, k.namespace, nil) - k.informer = factory.ForResource(featureFlagConfigurationResource).Informer() + k.informer = factory.ForResource(featureFlagResource).Informer() return nil } @@ -191,21 +192,21 @@ func (k *Sync) fetch(ctx context.Context) (string, error) { } k.logger.Debug(fmt.Sprintf("resource %s served from the informer cache", k.URI)) - return configuration.Spec.FeatureFlagSpec, nil + return marshallFeatureFlagSpec(configuration) } // fallback to API access - this is an informer cache miss. Could happen at the startup where cache is not filled - var ff v1alpha1.FeatureFlagConfiguration + var ff v1beta1.FeatureFlag err = k.readClient.Get(ctx, client.ObjectKey{ Name: k.crdName, Namespace: k.namespace, }, &ff) if err != nil { - return "", fmt.Errorf("unable to fetch FeatureFlagConfiguration %s/%s: %w", k.namespace, k.crdName, err) + return "", fmt.Errorf("unable to fetch FeatureFlag %s/%s: %w", k.namespace, k.crdName, err) } k.logger.Debug(fmt.Sprintf("resource %s served from API server", k.URI)) - return ff.Spec.FeatureFlagSpec, nil + return marshallFeatureFlagSpec(&ff) } func (k *Sync) notify(ctx context.Context, c chan<- INotify) { @@ -299,16 +300,16 @@ func updateFuncHandler(oldObj interface{}, newObj interface{}, object client.Obj } // toFFCfg attempts to covert unstructured payload to configurations -func toFFCfg(object interface{}) (*v1alpha1.FeatureFlagConfiguration, error) { +func toFFCfg(object interface{}) (*v1beta1.FeatureFlag, error) { u, ok := object.(*unstructured.Unstructured) if !ok { return nil, fmt.Errorf("provided value is not of type *unstructured.Unstructured") } - var ffObj v1alpha1.FeatureFlagConfiguration + var ffObj v1beta1.FeatureFlag err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &ffObj) if err != nil { - return nil, fmt.Errorf("unable to convert unstructured to v1alpha1.FeatureFlagConfiguration: %w", err) + return nil, fmt.Errorf("unable to convert unstructured to v1beta1.FeatureFlag: %w", err) } return &ffObj, nil @@ -349,3 +350,11 @@ func k8sClusterConfig() (*rest.Config, error) { return clusterConfig, nil } + +func marshallFeatureFlagSpec(ff *v1beta1.FeatureFlag) (string, error) { + b, err := json.Marshal(ff.Spec.FlagSpec) + if err != nil { + return "", fmt.Errorf("failed to marshall FlagSpec: %s", err.Error()) + } + return string(b), nil +} diff --git a/core/pkg/sync/kubernetes/kubernetes_sync_test.go b/core/pkg/sync/kubernetes/kubernetes_sync_test.go index 725e04a6b..b81f44074 100644 --- a/core/pkg/sync/kubernetes/kubernetes_sync_test.go +++ b/core/pkg/sync/kubernetes/kubernetes_sync_test.go @@ -12,7 +12,8 @@ import ( "github.com/open-feature/flagd/core/pkg/logger" "github.com/open-feature/flagd/core/pkg/sync" - "github.com/open-feature/open-feature-operator/apis/core/v1alpha1" + "github.com/open-feature/open-feature-operator/apis/core/v1beta1" + "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -26,7 +27,7 @@ import ( ) var Metadata = v1.TypeMeta{ - Kind: "FeatureFlagConfiguration", + Kind: "FeatureFlag", APIVersion: apiVersion, } @@ -74,14 +75,14 @@ func Test_parseURI(t *testing.T) { } func Test_toFFCfg(t *testing.T) { - validFFCfg := v1alpha1.FeatureFlagConfiguration{ + validFFCfg := v1beta1.FeatureFlag{ TypeMeta: Metadata, } tests := []struct { name string input interface{} - want *v1alpha1.FeatureFlagConfiguration + want *v1beta1.FeatureFlag wantErr bool }{ { @@ -120,7 +121,7 @@ func Test_commonHandler(t *testing.T) { cfgNs := "resourceNS" cfgName := "resourceName" - validFFCfg := v1alpha1.FeatureFlagConfiguration{ + validFFCfg := v1beta1.FeatureFlag{ TypeMeta: Metadata, ObjectMeta: v1.ObjectMeta{ Namespace: cfgNs, @@ -166,9 +167,9 @@ func Test_commonHandler(t *testing.T) { { name: "simple error - API mismatch", args: args{ - obj: toUnstructured(t, v1alpha1.FeatureFlagConfiguration{ + obj: toUnstructured(t, v1beta1.FeatureFlag{ TypeMeta: v1.TypeMeta{ - Kind: "FeatureFlagConfiguration", + Kind: "FeatureFlag", APIVersion: "someAPIVersion", }, }), @@ -227,7 +228,7 @@ func Test_updateFuncHandler(t *testing.T) { cfgNs := "resourceNS" cfgName := "resourceName" - validFFCfgOld := v1alpha1.FeatureFlagConfiguration{ + validFFCfgOld := v1beta1.FeatureFlag{ TypeMeta: Metadata, ObjectMeta: v1.ObjectMeta{ Namespace: cfgNs, @@ -293,9 +294,9 @@ func Test_updateFuncHandler(t *testing.T) { name: "Simple error - API version mismatch new object", args: args{ oldObj: toUnstructured(t, validFFCfgOld), - newObj: toUnstructured(t, v1alpha1.FeatureFlagConfiguration{ + newObj: toUnstructured(t, v1beta1.FeatureFlag{ TypeMeta: v1.TypeMeta{ - Kind: "FeatureFlagConfiguration", + Kind: "FeatureFlag", APIVersion: "someAPIVersion", }, }), @@ -310,9 +311,9 @@ func Test_updateFuncHandler(t *testing.T) { { name: "Simple error - API version mismatch old object", args: args{ - oldObj: toUnstructured(t, v1alpha1.FeatureFlagConfiguration{ + oldObj: toUnstructured(t, v1beta1.FeatureFlag{ TypeMeta: v1.TypeMeta{ - Kind: "FeatureFlagConfiguration", + Kind: "FeatureFlag", APIVersion: "someAPIVersion", }, }), @@ -369,23 +370,25 @@ func Test_updateFuncHandler(t *testing.T) { } func TestSync_fetch(t *testing.T) { - flagSpec := "fakeFlagSpec" + flagSpec := v1beta1.FlagSpec{ + Flags: map[string]v1beta1.Flag{}, + } - validCfg := v1alpha1.FeatureFlagConfiguration{ + validCfg := v1beta1.FeatureFlag{ TypeMeta: Metadata, ObjectMeta: v1.ObjectMeta{ Namespace: "resourceNS", Name: "resourceName", ResourceVersion: "v1", }, - Spec: v1alpha1.FeatureFlagConfigurationSpec{ - FeatureFlagSpec: flagSpec, + Spec: v1beta1.FeatureFlagSpec{ + FlagSpec: flagSpec, }, } type args struct { InformerGetFunc func(key string) (item interface{}, exists bool, err error) - ClientResponse v1alpha1.FeatureFlagConfiguration + ClientResponse v1beta1.FeatureFlag ClientError error } @@ -403,7 +406,7 @@ func TestSync_fetch(t *testing.T) { }, }, wantErr: false, - want: flagSpec, + want: `{"flags":{}}`, }, { name: "Scenario - get from API if informer cache miss", @@ -414,7 +417,7 @@ func TestSync_fetch(t *testing.T) { ClientResponse: validCfg, }, wantErr: false, - want: flagSpec, + want: `{"flags":{}}`, }, { name: "Scenario - error for informer cache read error", @@ -468,18 +471,20 @@ func TestSync_fetch(t *testing.T) { } func TestSync_watcher(t *testing.T) { - flagSpec := "fakeFlagSpec" + flagSpec := v1beta1.FeatureFlagSpec{ + FlagSpec: v1beta1.FlagSpec{ + Flags: map[string]v1beta1.Flag{}, + }, + } - validCfg := v1alpha1.FeatureFlagConfiguration{ + validCfg := v1beta1.FeatureFlag{ TypeMeta: Metadata, ObjectMeta: v1.ObjectMeta{ Namespace: "resourceNS", Name: "resourceName", ResourceVersion: "v1", }, - Spec: v1alpha1.FeatureFlagConfigurationSpec{ - FeatureFlagSpec: flagSpec, - }, + Spec: flagSpec, } type args struct { @@ -494,7 +499,7 @@ func TestSync_watcher(t *testing.T) { }{ { name: "scenario - create event", - want: flagSpec, + want: `{"flags":{}}`, args: args{ InformerGetFunc: func(key string) (item interface{}, exists bool, err error) { return toUnstructured(t, validCfg), true, nil @@ -508,7 +513,7 @@ func TestSync_watcher(t *testing.T) { }, { name: "scenario - modify event", - want: flagSpec, + want: `{"flags":{}}`, args: args{ InformerGetFunc: func(key string) (item interface{}, exists bool, err error) { return toUnstructured(t, validCfg), true, nil @@ -602,7 +607,7 @@ func TestSync_ReSync(t *testing.T) { ff := &unstructured.Unstructured{} ff.SetUnstructuredContent(getCFG(name, ns)) fakeDynamicClient := fake.NewSimpleDynamicClient(s, ff) - validFFCfg := &v1alpha1.FeatureFlagConfiguration{ + validFFCfg := &v1beta1.FeatureFlag{ TypeMeta: Metadata, ObjectMeta: v1.ObjectMeta{ Name: name, @@ -727,7 +732,7 @@ func TestNotify(t *testing.T) { "empty": "", } ff.SetUnstructuredContent(cfg) - _, err = fc.Resource(featureFlagConfigurationResource).Namespace(ns).UpdateStatus(context.TODO(), ff, v1.UpdateOptions{}) + _, err = fc.Resource(featureFlagResource).Namespace(ns).UpdateStatus(context.TODO(), ff, v1.UpdateOptions{}) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -740,7 +745,7 @@ func TestNotify(t *testing.T) { old["resourceVersion"] = "newVersion" cfg["metadata"] = old ff.SetUnstructuredContent(cfg) - _, err = fc.Resource(featureFlagConfigurationResource).Namespace(ns).UpdateStatus(context.TODO(), ff, v1.UpdateOptions{}) + _, err = fc.Resource(featureFlagResource).Namespace(ns).UpdateStatus(context.TODO(), ff, v1.UpdateOptions{}) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -749,7 +754,7 @@ func TestNotify(t *testing.T) { t.Errorf("Expected message %v, got %v", DefaultEventTypeModify, msg) } // delete - err = fc.Resource(featureFlagConfigurationResource).Namespace(ns).Delete(context.TODO(), name, v1.DeleteOptions{}) + err = fc.Resource(featureFlagResource).Namespace(ns).Delete(context.TODO(), name, v1.DeleteOptions{}) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -763,7 +768,7 @@ func TestNotify(t *testing.T) { "featureFlagSpec": int64(12), // we expect string here } ff.SetUnstructuredContent(cfg) - _, err = fc.Resource(featureFlagConfigurationResource).Namespace(ns).Create(context.TODO(), ff, v1.CreateOptions{}) + _, err = fc.Resource(featureFlagResource).Namespace(ns).Create(context.TODO(), ff, v1.CreateOptions{}) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -771,11 +776,11 @@ func TestNotify(t *testing.T) { "bump": "1", } ff.SetUnstructuredContent(cfg) - _, err = fc.Resource(featureFlagConfigurationResource).Namespace(ns).UpdateStatus(context.TODO(), ff, v1.UpdateOptions{}) + _, err = fc.Resource(featureFlagResource).Namespace(ns).UpdateStatus(context.TODO(), ff, v1.UpdateOptions{}) if err != nil { t.Errorf("Unexpected error: %v", err) } - err = fc.Resource(featureFlagConfigurationResource).Namespace(ns).Delete(context.TODO(), name, v1.DeleteOptions{}) + err = fc.Resource(featureFlagResource).Namespace(ns).Delete(context.TODO(), name, v1.DeleteOptions{}) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -839,14 +844,14 @@ func Test_NewK8sSync(t *testing.T) { } func newFakeReadClient(objs ...client.Object) client.Client { - _ = v1alpha1.AddToScheme(scheme.Scheme) + _ = v1beta1.AddToScheme(scheme.Scheme) return fakeClient.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(objs...).Build() } func getCFG(name, namespace string) map[string]interface{} { return map[string]interface{}{ - "apiVersion": "core.openfeature.dev/v1alpha1", - "kind": "FeatureFlagConfiguration", + "apiVersion": "core.openfeature.dev/v1beta1", + "kind": "FeatureFlag", "metadata": map[string]interface{}{ "name": name, "namespace": namespace, @@ -879,7 +884,7 @@ type MockClient struct { client.Reader clientErr error - getResponse v1alpha1.FeatureFlagConfiguration + getResponse v1beta1.FeatureFlag } func (m MockClient) Get(_ context.Context, _ client.ObjectKey, obj client.Object, _ ...client.GetOption) error { @@ -889,9 +894,9 @@ func (m MockClient) Get(_ context.Context, _ client.ObjectKey, obj client.Object } // else try returning response - cfg, ok := obj.(*v1alpha1.FeatureFlagConfiguration) + cfg, ok := obj.(*v1beta1.FeatureFlag) if !ok { - return errors.New("must contain a pointer typed v1alpha1.FeatureFlagConfiguration") + return errors.New("must contain a pointer typed v1beta1.FeatureFlag") } *cfg = m.getResponse @@ -908,3 +913,20 @@ type MockInformer struct { func (m MockInformer) GetStore() cache.Store { return &m.fakeStore } + +func TestMeasure(t *testing.T) { + res, err := marshallFeatureFlagSpec(&v1beta1.FeatureFlag{ + Spec: v1beta1.FeatureFlagSpec{ + FlagSpec: v1beta1.FlagSpec{ + Flags: map[string]v1beta1.Flag{ + "flag": { + DefaultVariant: "kubernetes", + }, + }, + }, + }, + }) + + require.Nil(t, err) + require.Equal(t, "{\"flags\":{\"flag\":{\"state\":\"\",\"variants\":null,\"defaultVariant\":\"kubernetes\"}}}", res) +} diff --git a/docs/concepts/syncs.md b/docs/concepts/syncs.md index 10f1c8d18..861f7c2ba 100644 --- a/docs/concepts/syncs.md +++ b/docs/concepts/syncs.md @@ -50,18 +50,18 @@ See [sync source](../reference/sync-configuration.md#source-configuration) confi ### Kubernetes sync The Kubernetes sync provider allows flagd to connect to a Kubernetes cluster and evaluate flags against a specified -FeatureFlagConfiguration resource as defined within -the [open-feature-operator](https://github.com/open-feature/open-feature-operator/blob/main/apis/core/v1alpha1/featureflagconfiguration_types.go) +FeatureFlag resource as defined within +the [open-feature-operator](https://github.com/open-feature/open-feature-operator/blob/main/apis/core/v1beta1/featureflag_types.go) spec. This configuration is best used in conjunction with the [OpenFeature Operator](https://github.com/open-feature/open-feature-operator). -To use an existing FeatureFlagConfiguration custom resource, start flagd with the following command: +To use an existing FeatureFlag custom resource, start flagd with the following command: ```shell flagd start --uri core.openfeature.dev/default/my_example ``` -In this example, `default/my_example` expected to be a valid FeatureFlagConfiguration resource, where `default` is the +In this example, `default/my_example` expected to be a valid FeatureFlag resource, where `default` is the namespace and `my_example` being the resource name. See [sync source](../reference/sync-configuration.md#source-configuration) configuration for details. diff --git a/docs/reference/flagd-cli/flagd_start.md b/docs/reference/flagd-cli/flagd_start.md index 4359c29bf..c867c22e2 100644 --- a/docs/reference/flagd-cli/flagd_start.md +++ b/docs/reference/flagd-cli/flagd_start.md @@ -26,7 +26,7 @@ flagd start [flags] -s, --sources string JSON representation of an array of SourceConfig objects. This object contains 2 required fields, uri (string) and provider (string). Documentation for this object: https://github.com/open-feature/flagd/blob/main/docs/configuration/configuration.md#sync-provider-customisation -y, --sync-provider string DEPRECATED: Set a sync provider e.g. filepath or remote -a, --sync-provider-args stringToString DEPRECATED: Sync provider arguments as key values separated by = (default []) - -f, --uri .yaml/.yml/.json Set a sync provider uri to read data from, this can be a filepath,url (http and grpc) or FeatureFlagConfiguration. When flag keys are duplicated across multiple providers the merge priority follows the index of the flag arguments, as such flags from the uri at index 0 take the lowest precedence, with duplicated keys being overwritten by those from the uri at index 1. Please note that if you are using filepath, flagd only supports files with .yaml/.yml/.json extension. + -f, --uri .yaml/.yml/.json Set a sync provider uri to read data from, this can be a filepath, URL (HTTP and gRPC) or FeatureFlag custom resource. When flag keys are duplicated across multiple providers the merge priority follows the index of the flag arguments, as such flags from the uri at index 0 take the lowest precedence, with duplicated keys being overwritten by those from the uri at index 1. Please note that if you are using filepath, flagd only supports files with .yaml/.yml/.json extension. ``` ### Options inherited from parent commands diff --git a/docs/reference/openfeature-operator/crds/featureflagconfiguration.md b/docs/reference/openfeature-operator/crds/featureflag.md similarity index 76% rename from docs/reference/openfeature-operator/crds/featureflagconfiguration.md rename to docs/reference/openfeature-operator/crds/featureflag.md index 43e21b983..a4cb27a8d 100644 --- a/docs/reference/openfeature-operator/crds/featureflagconfiguration.md +++ b/docs/reference/openfeature-operator/crds/featureflag.md @@ -1,12 +1,12 @@ # Feature Flag Configuration -The `FeatureFlagConfiguration` version `v1alpha2` CRD defines a CR with the following example structure: +The `FeatureFlag` version `v1beta1` CRD defines a CR with the following example structure: ```yaml -apiVersion: core.openfeature.dev/v1alpha2 -kind: FeatureFlagConfiguration +apiVersion: core.openfeature.dev/v1beta1 +kind: FeatureFlag metadata: - name: featureflagconfiguration-sample + name: featureflag-sample spec: featureFlagSpec: flags: diff --git a/docs/reference/openfeature-operator/crds/flagsourceconfiguration.md b/docs/reference/openfeature-operator/crds/featureflagsource.md similarity index 65% rename from docs/reference/openfeature-operator/crds/flagsourceconfiguration.md rename to docs/reference/openfeature-operator/crds/featureflagsource.md index dd7c89d74..63ce52067 100644 --- a/docs/reference/openfeature-operator/crds/flagsourceconfiguration.md +++ b/docs/reference/openfeature-operator/crds/featureflagsource.md @@ -1,23 +1,23 @@ -# FlagSourceConfigurations +# FeatureFlagSources -`FlagSourceConfiguration` support multiple flag sources. Sources are configured as a list and given below are supported sources and their configurations, +`FeatureFlagSource` support multiple flag sources. Sources are configured as a list and given below are supported sources and their configurations, -## kubernetes aka `FeatureFlagConfiguration` +## kubernetes aka `FeatureFlag` -This is `FeatureFlagConfiguration` custom resource backed flagd feature flag definition. -Read more on the custom resource at the dedicated documentation of [FeatureFlagConfiguration](https://github.com/open-feature/open-feature-operator/blob/main/docs/feature_flag_configuration.md) +This is `FeatureFlag` custom resource backed flagd feature flag definition. +Read more on the custom resource at the dedicated documentation of [FeatureFlag](https://github.com/open-feature/open-feature-operator/blob/main/docs/feature_flag_configuration.md) -To refer this custom resource in `FlagSourceConfiguration`, provider type `kubernetes` is used as below example, +To refer this custom resource in `FlagSource`, provider type `kubernetes` is used as below example, ```yaml sources: - - source: flags/sample-flags # FeatureFlagConfiguration - namespace/custom_resource_name - provider: kubernetes # kubernetes flag source backed by FeatureFlagConfiguration custom resource + - source: flags/sample-flags # FeatureFlag - namespace/custom_resource_name + provider: kubernetes # kubernetes flag source backed by FeatureFlag custom resource ``` ## flagd-proxy -`flagd-proxy` is an alternative to direct resource access on `FeatureFlagConfiguration` custom resources. +`flagd-proxy` is an alternative to direct resource access on `FeatureFlag` custom resources. This source type is useful when there is a need for restricting workload permissions and/or to reduce k8s API load. Read more about proxy approach to access kubernetes resources: [flagd-proxy](https://github.com/open-feature/open-feature-operator/blob/main/docs/flagd_proxy.md) @@ -60,7 +60,7 @@ sources: ## Sidecar configurations -`FlagSourceConfiguration` further allows to provide configurations to the injected flagd sidecar. +`FeatureFlagSource` further allows to provide configurations to the injected flagd sidecar. Table given below is non-exhaustive list of overriding options, | Configuration | Explanation | Default | @@ -82,16 +82,16 @@ If no namespace is provided, it is assumed that the CR is within the same namesp namespace: test-ns annotations: openfeature.dev/enabled: "true" - openfeature.dev/flagsourceconfiguration: "config-A, test-ns-2/config-B" + openfeature.dev/featureflagsource: "config-A, test-ns-2/config-B" ``` In this example, 2 CRs are being used to configure the injected container (by default the operator uses the `flagd:main` image), `config-A` (which is assumed to be in the namespace `test-ns`) and `config-B` from the `test-ns-2` namespace, with `config-B` taking precedence in the configuration merge. -The `FlagSourceConfiguration` version `v1alpha3` CRD defines a CR with the following example structure, the documentation for this CRD can be found [here](https://github.com/open-feature/open-feature-operator/blob/main/docs/crds.md#flagsourceconfiguration): +The `FeatureFlagSource` version `v1beta1` CRD defines a CR with the following example structure, the documentation for this CRD can be found [here](https://github.com/open-feature/open-feature-operator/blob/main/docs/crds.md#featureflagsource): ```yaml -apiVersion: core.openfeature.dev/v1alpha3 -kind: FlagSourceConfiguration +apiVersion: core.openfeature.dev/v1beta1 +kind: FeatureFlagSource metadata: name: flag-source-sample spec: @@ -114,15 +114,15 @@ spec: debugLogging: false ``` -The relevant `FlagSourceConfigurations` are passed to the operator by setting the `openfeature.dev/flagsourceconfiguration` annotation, and is responsible for providing the full configuration of the injected sidecar. +The relevant `FeatureFlagSources` are passed to the operator by setting the `openfeature.dev/featureflagsource` annotation, and is responsible for providing the full configuration of the injected sidecar. ## Configuration Merging -When multiple `FlagSourceConfigurations` are provided, the configurations are merged. The last `CR` takes precedence over the first, with any configuration from the deprecated `FlagDSpec` field of the `FeatureFlagConfiguration` CRD taking the lowest priority. +When multiple `FeatureFlagSources` are provided, the configurations are merged. The last `CR` takes precedence over the first, with any configuration from the deprecated `FlagDSpec` field of the `FeatureFlag` CRD taking the lowest priority. ```mermaid flowchart LR - FlagSourceConfiguration-values -->|highest priority| environment-variables -->|lowest priority| defaults + FeatureFlagSource-values -->|highest priority| environment-variables -->|lowest priority| defaults ``` An example of this behavior: @@ -131,14 +131,14 @@ An example of this behavior: metadata: annotations: openfeature.dev/enabled: "true" - openfeature.dev/flagsourceconfiguration:"config-A, config-B" + openfeature.dev/featureflagsource:"config-A, config-B" ``` Config-A: ```yaml -apiVersion: core.openfeature.dev/v1alpha2 -kind: FlagSourceConfiguration +apiVersion: core.openfeature.dev/v1beta1 +kind: FeatureFlagSource metadata: name: config-A spec: @@ -149,8 +149,8 @@ spec: Config-B: ```yaml -apiVersion: core.openfeature.dev/v1alpha2 -kind: FlagSourceConfiguration +apiVersion: core.openfeature.dev/v1beta1 +kind: FeatureFlagSource metadata: name: config-B spec: diff --git a/docs/reference/openfeature-operator/installation.md b/docs/reference/openfeature-operator/installation.md index 19d44b830..0ed553185 100644 --- a/docs/reference/openfeature-operator/installation.md +++ b/docs/reference/openfeature-operator/installation.md @@ -27,14 +27,14 @@ Create a namespace to house your flags: kubectl create namespace flags ``` -Next, define your feature flag(s) using the [FeatureFlagConfiguration](./crds/featureflagconfiguration.md) custom resource definition (CRD). +Next, define your feature flag(s) using the [FeatureFlag](./crds/featureflag.md) custom resource definition (CRD). This example specifies one flag called `foo` which has two variants `bar` and `baz`. The `defaultVariant` is `bar`. ```bash kubectl apply -n flags -f - <

@@ -40,12 +40,12 @@ spec: - '[{"uri":"grpc://flagd-proxy-svc.flagd-proxy.svc.cluster.local:8015","provider":"grpc","selector":"core.openfeature.dev/NAMESPACE/NAME"}]' - --debug --- -apiVersion: core.openfeature.dev/v1alpha2 -kind: FeatureFlagConfiguration +apiVersion: core.openfeature.dev/v1beta1 +kind: FeatureFlag metadata: name: end-to-end spec: - featureFlagSpec: + flagSpec: flags: color: state: ENABLED diff --git a/flagd/cmd/start.go b/flagd/cmd/start.go index 44d52f9c6..c248388ce 100644 --- a/flagd/cmd/start.go +++ b/flagd/cmd/start.go @@ -54,7 +54,7 @@ func init() { "a", nil, "DEPRECATED: Sync provider arguments as key values separated by =") flags.StringSliceP( uriFlagName, "f", []string{}, "Set a sync provider uri to read data from, this can be a filepath,"+ - "url (http and grpc) or FeatureFlagConfiguration. When flag keys are duplicated across multiple providers the "+ + " URL (HTTP and gRPC) or FeatureFlag custom resource. When flag keys are duplicated across multiple providers the "+ "merge priority follows the index of the flag arguments, as such flags from the uri at index 0 take the "+ "lowest precedence, with duplicated keys being overwritten by those from the uri at index 1. "+ "Please note that if you are using filepath, flagd only supports files with `.yaml/.yml/.json` extension.", diff --git a/mkdocs.yml b/mkdocs.yml index dcc1032ee..fee865456 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -92,8 +92,8 @@ nav: - 'String Comparison Specification': 'reference/specifications/custom-operations/string-comparison-operation-spec.md' - 'OpenFeature Operator': - 'Installation': 'reference/openfeature-operator/installation.md' - - 'FeatureFlagConfiguration': 'reference/openfeature-operator/crds/featureflagconfiguration.md' - - 'FlagSourceConfiguration': 'reference/openfeature-operator/crds/flagsourceconfiguration.md' + - 'FeatureFlag': 'reference/openfeature-operator/crds/featureflag.md' + - 'FeatureFlagSource': 'reference/openfeature-operator/crds/featureflagsource.md' - 'Naming': 'reference/naming.md' - 'FAQ': 'faq.md' - 'Troubleshooting': 'troubleshooting.md'