Skip to content

Commit

Permalink
feat: support yaml evaluator (#206)
Browse files Browse the repository at this point in the history
Signed-off-by: Suraj Banakar <surajrbanakar@gmail.com>
  • Loading branch information
vadasambar committed Jan 4, 2023
1 parent 3ecfc65 commit 2dbace5
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 20 deletions.
6 changes: 4 additions & 2 deletions cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,17 @@ func init() {
flags.StringP(socketPathFlagName, "d", "", "Flagd socket path. "+
"With grpc the service will become available on this address. "+
"With http(s) the grpc-gateway proxy will use this address internally.")
flags.StringP(evaluatorFlagName, "e", "json", "Set an evaluator e.g. json")
flags.StringP(evaluatorFlagName, "e", "json", "Set an evaluator e.g. json, yaml/yml."+
"Please note that yaml/yml and json evaluations work the same (yaml/yml files are converted to json internally)")
flags.StringP(serverCertPathFlagName, "c", "", "Server side tls certificate path")
flags.StringP(serverKeyPathFlagName, "k", "", "Server side tls key path")
flags.StringToStringP(providerArgsFlagName,
"a", nil, "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 or FeatureFlagConfiguration. Using multiple providers is supported however if"+
"flag keys are duplicated across multiple sources it may lead to unexpected behavior ",
" flag keys are duplicated across multiple sources it may lead to unexpected behavior. "+
"Please note that if you are using filepath, flagd only supports files with `.yaml/.yml/.json` extension.",
)
flags.StringP(
bearerTokenFlagName, "b", "", "Set a bearer token to use for remote sync")
Expand Down
88 changes: 88 additions & 0 deletions config/samples/example_flags.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
flags:
myBoolFlag:
state: ENABLED
variants:
'on': true
'off': false
defaultVariant: 'on'
myStringFlag:
state: ENABLED
variants:
key1: val1
key2: val2
defaultVariant: key1
myFloatFlag:
state: ENABLED
variants:
one: 1.23
two: 2.34
defaultVariant: one
myIntFlag:
state: ENABLED
variants:
one: 1
two: 2
defaultVariant: one
myObjectFlag:
state: ENABLED
variants:
object1:
key: val
object2:
key: true
defaultVariant: object1
isColorYellow:
state: ENABLED
variants:
'on': true
'off': false
defaultVariant: 'off'
targeting:
if:
- "==":
- var:
- color
- yellow
- 'on'
- 'off'
fibAlgo:
variants:
recursive: recursive
memo: memo
loop: loop
binet: binet
defaultVariant: recursive
state: ENABLED
targeting:
if:
- "$ref": emailWithFaas
- binet
- null
headerColor:
variants:
red: "#FF0000"
blue: "#0000FF"
green: "#00FF00"
yellow: "#FFFF00"
defaultVariant: red
state: ENABLED
targeting:
if:
- "$ref": emailWithFaas
- fractionalEvaluation:
- email
- - red
- 25
- - blue
- 25
- - green
- 25
- - yellow
- 25
- null
"$evaluators":
emailWithFaas:
in:
- "@faas.com"
- var:
- email
23 changes: 12 additions & 11 deletions docs/configuration/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ highest priority, followed by environment variables and finally config file.
Supported flags are as follows (result of running `./flagd start --help`):

```
-b, --bearer-token string Set a bearer token to use for remote sync
-e, --evaluator string Set an evaluator e.g. json (default "json")
-h, --help help for start
-p, --port int32 Port to listen on (default 8013)
-m, --metrics-port int32 Port to set up metrics listener on (default 8014)
-c, --server-cert-path string Server side tls certificate path
-k, --server-key-path string Server side tls key path
-a, --sync-provider-args Sync provider arguments as key values separated by =
-d, --socket-path string Set the flagd socket path.
-f, --uri strings Set a sync provider uri to read data from this can be a filepath, url or reference to a kubernetes custom resource. Using multiple providers is supported, however if flag keys are duplicated across multiple sources it may lead to unexpected behavior.
-C, --cors-origin strings Set a CORS allow origin header, setting "*" will allow all origins (by default CORS headers are not set)
-b, --bearer-token string Set a bearer token to use for remote sync
-C, --cors-origin strings CORS allowed origins, * will allow all origins
-e, --evaluator string Set an evaluator e.g. json, yaml/yml. Please note that yaml/yml and json evaluations work the same (yaml/yml files are converted to json internally) (default "json")
-h, --help help for start
-m, --metrics-port int32 Port to serve metrics on (default 8014)
-p, --port int32 Port to listen on (default 8013)
-c, --server-cert-path string Server side tls certificate path
-k, --server-key-path string Server side tls key path
-d, --socket-path string Flagd socket path. With grpc the service will become available on this address. With http(s) the grpc-gateway proxy will use this address internally.
-y, --sync-provider string DEPRECATED: Set a sync provider e.g. filepath or remote
-a, --sync-provider-args stringToString 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 or FeatureFlagConfiguration. Using multiple providers is supported however if flag keys are duplicated across multiple sources it may lead to unexpected behavior. Please note that if you are using filepath, flagd only supports files with .yaml/.yml/.json extension.
```

Environment variable keys are uppercased, prefixed with `FLAGD_` and all `-` are replaced with `_`. For example,
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ require (
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0
google.golang.org/grpc v1.50.1
google.golang.org/protobuf v1.28.1
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.25.4
k8s.io/apimachinery v0.25.4
k8s.io/client-go v0.25.4
Expand Down Expand Up @@ -97,7 +98,6 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.25.4 // indirect
k8s.io/component-base v0.25.4 // indirect
k8s.io/klog/v2 v2.80.1 // indirect
Expand Down
4 changes: 3 additions & 1 deletion pkg/runtime/from_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func (r *Runtime) setService(logger *logger.Logger) {

func (r *Runtime) setEvaluatorFromConfig(logger *logger.Logger) error {
switch r.config.Evaluator {
case "json":
case "yaml", "yml", "json":
r.Evaluator = eval.NewJSONEvaluator(logger)
default:
return errors.New("no evaluator set")
Expand All @@ -84,6 +84,8 @@ func (r *Runtime) setSyncImplFromConfig(logger *logger.Logger) error {
zap.String("sync", "filepath"),
),
ProviderArgs: r.config.ProviderArgs,
// evaluator here is file type: `json`, `yaml` etc.,
FileType: r.config.Evaluator,
})
rtLogger.Debug(fmt.Sprintf("Using filepath sync-provider for %q", uri))
case regCrd.Match(uriB):
Expand Down
34 changes: 33 additions & 1 deletion pkg/sync/filepath_sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ package sync

import (
"context"
"encoding/json"
"errors"
"fmt"
"os"

"gopkg.in/yaml.v3"

"github.com/fsnotify/fsnotify"
"github.com/open-feature/flagd/pkg/logger"
)
Expand All @@ -14,6 +17,8 @@ type FilePathSync struct {
URI string
Logger *logger.Logger
ProviderArgs ProviderArgs
// FileType indicates the file type e.g., json, yaml/yml etc.,
FileType string
}

func (fs *FilePathSync) Source() string {
Expand All @@ -28,7 +33,15 @@ func (fs *FilePathSync) Fetch(_ context.Context) (string, error) {
if err != nil {
return "", err
}
return string(rawFile), nil

switch fs.FileType {
case "yaml", "yml":
return yamlToJSON(rawFile)
case "json":
return string(rawFile), nil
default:
return "", fmt.Errorf("filepath extension '%v' is not supported", fs.FileType)
}
}

func (fs *FilePathSync) Notify(ctx context.Context, w chan<- INotify) {
Expand Down Expand Up @@ -90,3 +103,22 @@ func (fs *FilePathSync) Notify(ctx context.Context, w chan<- INotify) {
w <- &Notifier{Event: Event[DefaultEventType]{DefaultEventTypeReady}} // signal readiness to the caller
<-ctx.Done()
}

// yamlToJSON is a generic helper function to convert
// yaml to json
func yamlToJSON(rawFile []byte) (string, error) {
var ms map[string]interface{}
// yaml.Unmarshal unmarshals to map[interface]interface{}
if err := yaml.Unmarshal(rawFile, &ms); err != nil {
return "", fmt.Errorf("unmarshal yaml: %w", err)
}

// Adding spaces here because our evaluator transposer function
// doesn't understand json without indentations quite well
r, err := json.MarshalIndent(ms, "", " ")
if err != nil {
return "", fmt.Errorf("convert yaml to json: %w", err)
}

return string(r), err
}
10 changes: 6 additions & 4 deletions pkg/sync/filepath_sync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,9 @@ func TestFilePathSync_Fetch(t *testing.T) {
}{
"success": {
fpSync: sync.FilePathSync{
URI: fmt.Sprintf("%s/%s", dirName, fetchFileName),
Logger: logger.NewLogger(nil, false),
URI: fmt.Sprintf("%s/%s", dirName, fetchFileName),
Logger: logger.NewLogger(nil, false),
FileType: "json", // this is the default
},
handleResponse: func(t *testing.T, fetched string, err error) {
if err != nil {
Expand All @@ -132,8 +133,9 @@ func TestFilePathSync_Fetch(t *testing.T) {
},
"not found": {
fpSync: sync.FilePathSync{
URI: fmt.Sprintf("%s/%s", dirName, "not_found"),
Logger: logger.NewLogger(nil, false),
URI: fmt.Sprintf("%s/%s", dirName, "not_found"),
Logger: logger.NewLogger(nil, false),
FileType: "json",
},
handleResponse: func(t *testing.T, fetched string, err error) {
if err == nil {
Expand Down

0 comments on commit 2dbace5

Please sign in to comment.