Skip to content

Commit

Permalink
feat: file extension detection (#257)
Browse files Browse the repository at this point in the history
Signed-off-by: James Milligan <james@omnant.co.uk>

<!-- Please use this template for your pull request. -->
<!-- Please use the sections that you need and delete other sections -->

## This PR
<!-- add the description of the PR here -->

- file type is detected via the file extension, it is now possible to
mix and match `json` and `yaml` files as flag configuration sources

```
start -f file:./config/samples/example_flags.yaml -f file:./config/samples/example_flags.json
```

### Related Issues
<!-- add here the GitHub issue that this PR resolves if applicable -->

#240

### Notes
<!-- any additional notes for this PR -->

### Follow-up Tasks
<!-- anything that is related to this PR but not done here should be
noted under this section -->
<!-- if there is a need for a new issue, please link it here -->
It is my opinion that the `-e` or `--evaluator` flag is now a little
misleading / confusing, as only the `json_evaluator` is used internally,
and changing the flag value has no effect internally. Open to opinions
on how this should be handled, if at all.
### How to test
<!-- if applicable, add testing instructions under this section -->

Signed-off-by: James Milligan <james@omnant.co.uk>
  • Loading branch information
james-milligan authored Jan 12, 2023
1 parent 712d7dd commit ca22541
Show file tree
Hide file tree
Showing 6 changed files with 26 additions and 36 deletions.
8 changes: 6 additions & 2 deletions cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ 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, yaml/yml."+
flags.StringP(evaluatorFlagName, "e", "json", "DEPRECATED: 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")
Expand Down Expand Up @@ -95,6 +95,11 @@ var startCmd = &cobra.Command{
"Docs: https://github.com/open-feature/flagd/blob/main/docs/configuration.md")
}

if viper.GetString(evaluatorFlagName) != "" {
rtLogger.Warn("DEPRECATED: The --evaluator flag has been deprecated. " +
"Docs: https://github.com/open-feature/flagd/blob/main/docs/configuration.md")
}

// Build Runtime -----------------------------------------------------------
rt, err := runtime.FromConfig(logger, runtime.Config{
ServicePort: viper.GetInt32(portFlagName),
Expand All @@ -105,7 +110,6 @@ var startCmd = &cobra.Command{
ProviderArgs: viper.GetStringMapString(providerArgsFlagName),
SyncURI: viper.GetStringSlice(uriFlagName),
SyncBearerToken: viper.GetString(bearerTokenFlagName),
Evaluator: viper.GetString(evaluatorFlagName),
CORS: viper.GetStringSlice(corsFlagName),
})
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions docs/configuration/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ Supported flags are as follows (result of running `./flagd start --help`):
```
-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.
-e, --evaluator string DEPRECATED: 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")
-y, --sync-provider string DEPRECATED: Set a sync provider e.g. filepath or remote
```

Environment variable keys are uppercased, prefixed with `FLAGD_` and all `-` are replaced with `_`. For example,
Expand Down
18 changes: 1 addition & 17 deletions pkg/runtime/from_config.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package runtime

import (
"errors"
"fmt"
"net/http"
"regexp"
Expand Down Expand Up @@ -33,9 +32,7 @@ func FromConfig(logger *logger.Logger, config Config) (*Runtime, error) {
config: config,
Logger: logger.WithFields(zap.String("component", "runtime")),
syncNotifier: make(chan sync.INotify),
}
if err := rt.setEvaluatorFromConfig(logger); err != nil {
return nil, err
Evaluator: eval.NewJSONEvaluator(logger),
}
if err := rt.setSyncImplFromConfig(logger); err != nil {
return nil, err
Expand All @@ -60,17 +57,6 @@ func (r *Runtime) setService(logger *logger.Logger) {
}
}

func (r *Runtime) setEvaluatorFromConfig(logger *logger.Logger) error {
switch r.config.Evaluator {
case "yaml", "yml", "json":
r.Evaluator = eval.NewJSONEvaluator(logger)
default:
return errors.New("no evaluator set")
}
logger.Debug(fmt.Sprintf("Using %s evaluator", r.config.Evaluator))
return nil
}

func (r *Runtime) setSyncImplFromConfig(logger *logger.Logger) error {
rtLogger := logger.WithFields(zap.String("component", "runtime"))
r.SyncImpl = make([]sync.ISync, 0, len(r.config.SyncURI))
Expand All @@ -84,8 +70,6 @@ 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
3 changes: 1 addition & 2 deletions pkg/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ type Config struct {
SyncURI []string
SyncBearerToken string

Evaluator string
CORS []string
CORS []string
}

func (r *Runtime) startSyncer(ctx context.Context, syncr sync.ISync) error {
Expand Down
11 changes: 8 additions & 3 deletions pkg/sync/filepath_sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"os"
"strings"

"gopkg.in/yaml.v3"

Expand All @@ -18,7 +19,7 @@ type FilePathSync struct {
Logger *logger.Logger
ProviderArgs ProviderArgs
// FileType indicates the file type e.g., json, yaml/yml etc.,
FileType string
fileType string
}

func (fs *FilePathSync) Source() string {
Expand All @@ -29,18 +30,22 @@ func (fs *FilePathSync) Fetch(_ context.Context) (string, error) {
if fs.URI == "" {
return "", errors.New("no filepath string set")
}
if fs.fileType == "" {
uriSplit := strings.Split(fs.URI, ".")
fs.fileType = uriSplit[len(uriSplit)-1]
}
rawFile, err := os.ReadFile(fs.URI)
if err != nil {
return "", err
}

switch fs.FileType {
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)
return "", fmt.Errorf("filepath extension for URI '%s' is not supported", fs.URI)
}
}

Expand Down
18 changes: 8 additions & 10 deletions pkg/sync/filepath_sync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import (

const (
dirName = "test"
createFileName = "to_create"
modifyFileName = "to_modify"
deleteFileName = "to_delete"
fetchFileName = "to_fetch"
createFileName = "to_create.json"
modifyFileName = "to_modify.json"
deleteFileName = "to_delete.json"
fetchFileName = "to_fetch.json"
fetchFileContents = "fetch me"
)

Expand Down Expand Up @@ -117,9 +117,8 @@ func TestFilePathSync_Fetch(t *testing.T) {
}{
"success": {
fpSync: sync.FilePathSync{
URI: fmt.Sprintf("%s/%s", dirName, fetchFileName),
Logger: logger.NewLogger(nil, false),
FileType: "json", // this is the default
URI: fmt.Sprintf("%s/%s", dirName, fetchFileName),
Logger: logger.NewLogger(nil, false),
},
handleResponse: func(t *testing.T, fetched string, err error) {
if err != nil {
Expand All @@ -133,9 +132,8 @@ func TestFilePathSync_Fetch(t *testing.T) {
},
"not found": {
fpSync: sync.FilePathSync{
URI: fmt.Sprintf("%s/%s", dirName, "not_found"),
Logger: logger.NewLogger(nil, false),
FileType: "json",
URI: fmt.Sprintf("%s/%s", dirName, "not_found"),
Logger: logger.NewLogger(nil, false),
},
handleResponse: func(t *testing.T, fetched string, err error) {
if err == nil {
Expand Down

0 comments on commit ca22541

Please sign in to comment.