feat!: support OFO v1beta1 API (#997)
BREAKING CHANGE: OFO APIs were updated to version v1beta1, since they are more stable now. Resources of the alpha versions are no longer supported in flagd or flagd-proxy.
This commit is contained in:
parent
5b82d06eb7
commit
bb6f5bf0fc
|
|
@ -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
|
||||
|
|
|
|||
10
core/go.sum
10
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=
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
@ -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:
|
||||
|
|
@ -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 - <<EOF
|
||||
apiVersion: core.openfeature.dev/v1alpha2
|
||||
kind: FeatureFlagConfiguration
|
||||
apiVersion: core.openfeature.dev/v1beta1
|
||||
kind: FeatureFlag
|
||||
metadata:
|
||||
name: sample-flags
|
||||
spec:
|
||||
|
|
@ -52,7 +52,7 @@ EOF
|
|||
|
||||
Next, tell the OpenFeature operator where to find flags.
|
||||
|
||||
Do so by creating a [FlagSourceConfiguration](./crds/flagsourceconfiguration.md) CRD.
|
||||
Do so by creating a [FeatureFlagSource](./crds/featureflagsource.md) CRD.
|
||||
|
||||
This example specifies that the CRD called `sample-flags` (created above) can be found in the `flags` namespace and that the provider is `kubernetes`.
|
||||
|
||||
|
|
@ -60,10 +60,10 @@ The `port` parameter defines the port on which the flagd API will be made availa
|
|||
|
||||
```bash
|
||||
kubectl apply -n flags -f - <<EOF
|
||||
apiVersion: core.openfeature.dev/v1alpha3
|
||||
kind: FlagSourceConfiguration
|
||||
apiVersion: core.openfeature.dev/v1beta1
|
||||
kind: FeatureFlagSource
|
||||
metadata:
|
||||
name: flag-source-configuration
|
||||
name: feature-flag-source
|
||||
spec:
|
||||
sources:
|
||||
- source: flags/sample-flags
|
||||
|
|
@ -77,11 +77,11 @@ EOF
|
|||
The operator looks for `Deployment` objects annotated with particular annotations.
|
||||
|
||||
- `openfeature.dev/enabled: "true"` enables this deployment for flagd
|
||||
- `openfeature.dev/flagsourceconfiguration: "flags/flag-source-configuration"` makes the given feature flag sources available to this deployment
|
||||
- `openfeature.dev/featureflagsource: "flags/feature-flag-source"` makes the given feature flag sources available to this deployment
|
||||
|
||||
When these two annotations are added, the OpenFeature operator will inject a sidecar into your workload.
|
||||
|
||||
flagd will then be available via `http://localhost` the port specified in the `FlagSourceConfiguration` (e.g. `8080`)
|
||||
flagd will then be available via `http://localhost` the port specified in the `FeatureFlagSource` (e.g. `8080`)
|
||||
|
||||
Your Deployment YAML might look like this:
|
||||
|
||||
|
|
@ -102,7 +102,7 @@ spec:
|
|||
annotations:
|
||||
# here are the annotations for OpenFeature Operator
|
||||
openfeature.dev/enabled: "true"
|
||||
openfeature.dev/flagsourceconfiguration: "flags/flag-source-configuration"
|
||||
openfeature.dev/featureflagsource: "flags/feature-flag-source"
|
||||
spec:
|
||||
containers:
|
||||
- name: busybox
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ in-process flagd provider via a gRPC client connection to a sync server, such as
|
|||
|
||||
An implementation of an in-process flagd-provider must accept the following environment variables which determine the sync source:
|
||||
|
||||
- `FLAGD_SOURCE_URI`: The URI identifying the sync source. Depending on the sync provider type, this can be the URI of a gRPC service providing the `sync` API required by the in-process flagd provider, or the name of a [core.openfeature.dev/v1alpha2.FeatureFlagConfiguration](https://github.com/open-feature/open-feature-operator/blob/main/docs/crds.md#featureflagconfiguration-1) Custom Resource containing the flag definition.
|
||||
- `FLAGD_SOURCE_URI`: The URI identifying the sync source. Depending on the sync provider type, this can be the URI of a gRPC service providing the `sync` API required by the in-process flagd provider, or the name of a [core.openfeature.dev/v1beta1.FeatureFlag](https://github.com/open-feature/open-feature-operator/blob/main/docs/crds.md#featureflag-1) Custom Resource containing the flag definition.
|
||||
- `FLAGD_SOURCE_PROVIDER_TYPE`: The type of the provider. E.g. `grpc` or `kubernetes`.
|
||||
- `FLAGD_SOURCE_SELECTOR`: Optional selector for the feature flag definition of interest. This is used as a `selector` for the flagd-proxie's sync API to identify a flag definition within a collection of feature flag definitions.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[](http://github.com/badges/stability-badges)
|
||||
|
||||
The kube flagd proxy acts as a pub sub for deployed flagd sidecar containers to subscribe to change events in FeatureFlagConfiguration CRs.
|
||||
The kube flagd proxy acts as a pub sub for deployed flagd sidecar containers to subscribe to change events in FeatureFlag CRs.
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<p align="center">
|
||||
<img src="../images/flagd-proxy.png" width="650">
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.",
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
Loading…
Reference in New Issue