Coder Social home page Coder Social logo

open-feature / flagd Goto Github PK

View Code? Open in Web Editor NEW
492.0 15.0 60.0 11.5 MB

A feature flag daemon with a Unix philosophy

Home Page: https://openfeature.dev

License: Apache License 2.0

Dockerfile 0.90% Makefile 1.23% Go 90.76% JavaScript 0.28% Shell 0.79% HTML 0.03% TypeScript 6.00%
go golang openfeature

flagd's Introduction

A feature flag daemon with a Unix philosophy.

Github Actions

What's flagd?

flagd is a feature flag daemon with a Unix philosophy. Think of it as a ready-made, open source, OpenFeature-compliant feature flag backend system.

Features

โ–ถ๏ธ Quick Start

Experiment with flagd in your browser using the Killercoda tutorial or follow the instructions below to run on your own infrastructure.

  1. flagd can be run as a standalone binary or container. Download and install flagd or run it as a container

    Kubernetes-native? flagd can also be run as part of the Kubernetes Operator.

  2. Start flagd:

    flagd start \
      --port 8013 \
      --uri https://raw.githubusercontent.com/open-feature/flagd/main/samples/example_flags.flagd.json

    Or use docker: Note - In Windows, use WSL system for both the file location and Docker runtime. Mixed file systems don't work and this is a limitation of Docker

    docker run \
      --rm -it \
      --name flagd \
      -p 8013:8013 \
      ghcr.io/open-feature/flagd:latest start \
      --uri https://raw.githubusercontent.com/open-feature/flagd/main/samples/example_flags.flagd.json

    If you wish, download the file locally to make changes:

    wget https://raw.githubusercontent.com/open-feature/flagd/main/samples/example_flags.flagd.json

    In local mode, run flagd like this:

    flagd start \
      --port 8013 \
      --uri file:./example_flags.flagd.json

    Or use docker ( Note - In Windows, this requires WSL system for both the file location and Docker runtime):

    docker run \
      --rm -it \
      --name flagd \
      -p 8013:8013 \
      -v $(pwd):/etc/flagd \
      ghcr.io/open-feature/flagd:latest start \
      --uri file:./etc/flagd/example_flags.flagd.json

    --uri can be a local file or any remote endpoint. Use file: prefix for local files. eg. --uri file:/path/to/example_flags.flagd.json. gRPC and http have their own requirements. More information can be found here.

    Multiple --uri parameters can be specified. In other words, flagd can retrieve flags from multiple sources simultaneously.

  3. Flagd is now ready to perform flag evaluations over either HTTP(s) or gRPC. This example utilizes HTTP via cURL.

    Retrieve a String value:

    curl -X POST "http://localhost:8013/flagd.evaluation.v1.Service/ResolveString" \
      -d '{"flagKey":"myStringFlag","context":{}}' -H "Content-Type: application/json"

    For Windows we recommend using a WSL terminal. Otherwise, use the following with cmd:

    set json={"flagKey":"myStringFlag","context":{}}
    curl -i -X POST -H "Content-Type: application/json" -d %json:"=\"% "localhost:8013/flagd.evaluation.v1.Service/ResolveString"

    Result:

    {
      "value": "val1",
      "reason": "DEFAULT",
      "variant":"key1"
    }

    Updates to the underlying flag store (e.g. JSON file) are reflected by flagd in realtime. No restart is required.

    flagd also supports boolean, integer, float and object flag types.

  4. Now that flagd is running, it is time to integrate it into your application. Do this by using an OpenFeature provider in a language of your choice.

๐Ÿ“ High-level Architecture

logical architecture of flagd

๐Ÿ“ Further Documentation

Further documentation including flagd configuration options, fractional evaluation, targeting rules and flag configuration merging strategies can be found at flagd.dev or in this repository.

๐Ÿซถ Contributing

Interested in contributing? Great, we'd love your help! To get started, take a look at the CONTRIBUTING guide.

We also hold regular community meetings that are open to everyone. Check the OpenFeature community page for all the ways to get involved.

Thanks so much to our contributors.

Made with contrib.rocks.

License

Apache License 2.0

flagd's People

Contributors

aepfli avatar agardnerit avatar alexsjones avatar bacherfl avatar beeme1mr avatar chenrui333 avatar craigpastro avatar dblanchard88 avatar github-actions[bot] avatar james-milligan avatar joaoepj avatar josecolella avatar jwatte avatar kavindu-dodan avatar mihirm21 avatar moshebe avatar odubajdt avatar oleg-nenashev avatar olunusib avatar osamanabih avatar pradeepbbl avatar realanna avatar renovate[bot] avatar skyerus avatar snosratiershad avatar thisthat avatar thomaspoignant avatar toddbaert avatar tranngoclam avatar vadasambar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flagd's Issues

[Spec] Feature flag schema

Overview

A feature flag schema needs to be defined for flagd. The schema should be easy to use while still being flexible. The playground repo has a json schema that could be leveraged.

Use cases

This is how a few use cases could be defined using the proposed schema.

Simple on/off
  {
       "newWelcomeMessage": {
            "state": "enabled"
       }
  }
Multi-variant string values
  {
      "hexColor": {
          "returnType": "string",
          "variants": {
              "red": "CC0000",
              "green": "00CC00",
              "blue": "0000CC"
          },
         "defaultVariant": "blue",
         "state": "enabled"
      }
  }
Multi-variant numer values
  {
      "newFeatureMultiplier": {
          "returnType": "number",
          "variants": {
              "low": 1,
              "medium": 10,
              "high": 50
          },
         "defaultVariant": "medium",
         "state": "enabled"
      }
  }
Multi-variant string values with rules
  {
      "fibAlgo": {
          "returnType": "string",
          "variants": {
              "recursive": "recursive",
              "memo": "memo",
              "loop": "loop",
              "binet": "binet"
          },
          "defaultVariant": "recursive",
          "state": "enabled",
          "rules": [{
              "action": {
                  "variant": "binet"
              },
              "conditions": [
              {
                  "context": "email",
                  "op": "ends_with",
                  "value": "@faas.com"
              }]
         }]
     }
}

Prereq for #17

[FEATURE] Add support for flag state

Requirements

The flag configuration contains a state property. This property can be either ENABLED or DISABLED according to the schema. This value is an enum and may contain additional states in the future.

If the state is ENABLED, flagD should behave like it does now. However, if the state is DISABLED, flagD should return an error stating that the flag is disabled. The OpenFeature SDK would catch this error and use the fallback value.

Bug: potential nil dereference when closing request body

The following piece of code might cause nil dereference which causes service panic.

	resp, err := fs.Client.Do(req)
	defer func() { _ = resp.Body.Close() }()
	if err != nil {
		return []byte(""), err
	}

The resp is accessed regardless the err value. But in case of error there is no guarantee that it won't be nil.

[Feature] Service tests

Services currently do not have tests which means that they are high risk.
We should build out a test suite to test services atomically.

Make test failures

Golang 1.18.4

โฏ make test
git submodule update --init --recursive
cp schemas/json/flagd-definitions.json pkg/eval/flagd-definitions.json
go install github.com/bufbuild/buf/cmd/buf@latest
cd schemas/protobuf && buf generate --template buf.gen.go-server.yaml
go test -cover ./...
go: downloading github.com/golang/mock v1.4.4
go: downloading github.com/stretchr/testify v1.7.4
go: downloading github.com/pmezard/go-difflib v1.0.0
pkg/generated/server.gen.go:11:2: no required module provides package github.com/deepmap/oapi-codegen/pkg/runtime; to add it:
	go get github.com/deepmap/oapi-codegen/pkg/runtime
pkg/generated/server.gen.go:12:2: no required module provides package github.com/go-chi/chi/v5; to add it:
	go get github.com/go-chi/chi/v5
make: *** [test] Error 1

Formalize flagD release process

Based on community feedback, the following items need to be configured to

Additional notes:

  • Breaking changes need to be included in the release notes but does NOT require a major version bump until v1.0
  • PRs that are not ready to be merged must be marked as a draft
  • When a PR is ready, the final approver should merge the PR.

[FEATURE] GRPCServer TLS implementation for standalone

Requirements

func (s *GRPCService) Serve(ctx context.Context, eval eval.IEvaluator) error {
	s.Eval = eval

	// TODO: Needs TLS implementation
	grpcServer := grpc.NewServer()
	gen.RegisterServiceServer(grpcServer, s)

	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", s.GRPCServiceConfiguration.Port))
	if err != nil {
		return err
	}
	return grpcServer.Serve(lis)
}

Remove FSNotify Events Stop File Watcher

When using a mounted ConfigMap, Kubernetes removes and readds the file. This causes Flagd to disconnect and no longer watch the file for changes.

Here is the output from Flagd after a ConfigMap update:

time="2022-06-20T14:48:50Z" level=info msg="Starting filepath sync notifier"
time="2022-06-20T14:48:50Z" level=info msg="Notifying filepath: /etc/flagd/config.json"
time="2022-06-20T14:49:57Z" level=info msg="Filepath notifier event: /etc/flagd/config.json CHMOD"
time="2022-06-20T14:49:57Z" level=info msg="Filepath notifier event sent"
time="2022-06-20T14:49:57Z" level=info msg="Filepath notifier event: /etc/flagd/config.json REMOVE"
time="2022-06-20T14:49:57Z" level=info msg="New configuration created"
time="2022-06-20T14:49:57Z" level=info msg="Configuration deleted"
time="2022-06-20T14:49:57Z" level=info msg="Filepath notifier event sent"

Bug: Graceful shutdown does not work as expected

There is a signal catch and call to the context cancel function with context propagation which is awesome.

But, the ISync interface:

type ISync interface {
	Fetch() (string, error)
	Notify(chan<- INotify)
}

Does not include the context on its Notify function so the work there won't be canceled although it should (cron/watch for the HTTP and the File syncers)

I suggest adding the context to the interface signature and use it instead the Notify implementations.

[Proposal] Initial scope

Flagd Initial Scope

Flagd is intended to be a lightweight feature flag control plane that's compliant with the OpenFeature specification. It's responsible for managing feature flag configurations and performing flag evaluations. Flagd is designed to support multiple deployment patterns (i.e. systemd, sysvinit, kubernetes).

Architecture

image

Requirements

Feature flag configuration management (Sync impl)

  • #18
  • #17
  • #22
  • Invalid flag configurations are rejected

Flag evaluation

  • Flag evaluations are compliant with the OpenFeature provider specification
  • #49

Communication (Service impl)

Future goals

  • Track flag configuration changes so change events can be emitted. Events are currently not defined in the spec but it's on the roadmap. open-feature/spec#77
  • Possible syncs implementing vendor communication

Configuration changes take affect immediately

Summary

One of the main benefits of feature flags is the ability to change values at runtime. Flagd needs to watch for feature flag configuration changes and update its internal state. This should happen without downtime to Flagd.

Considerations

  • File system watcher overhead should be minimal
  • Change events may be added to the OpenFeature spec in the future

Questions

  • How should invalid feature flag configuration changes be handled in a running process?

Resources

[Feature] [Question] GRPC service

In order to support more protocols, GRPC seems like a sensible schema to support.
This does also mean that we will need to be opinionated on the interfaces that the client side will need to consume and also think about where the proto files will live in terms of the repository as this has dependency implications.

[FEATURE] Reusable Targeting Operations

Requirements

Overview

Targeting rules in flagD allow users to define arbitrarily complex rulesets using JSON logic. However, this flexibility introduces administrative challenges when managing a large number of flags. Reusable targeting operations would allow common targeting rules to be shared between many flags.

The following is an example of a configuration that has multiple flags that use a similar rule.

Current flag configurations
{
   "flags": {
        "hex-color": {
          "variants": {
            "red": "CC0000",
            "green": "00CC00",
            "blue": "0000CC",
            "yellow": "yellow"
          },
          "defaultVariant": "red",
          "state": "ENABLED",
          "targeting": {
            "if": [
              {
                "in": ["@faas.com", {
                  "var": ["email"]
                }]
              }, "green", null
            ]
          }
        },
        "fib-algo": {
          "variants": {
            "recursive": "recursive",
            "memo": "memo",
            "loop": "loop",
            "binet": "binet"
          },
          "defaultVariant": "recursive",
          "state": "ENABLED",
          "targeting": {
            "if": [
              {
                "in": ["@faas.com", {
                  "var": ["email"]
                }]
              }, "binet", null
            ]
          }
        }
    }
}

A shared operation would allow the core logic to be moved to a shared location and be referenced within a targeting rule.

Proposed flag configurations
{
   "flags": {
        "hex-color": {
          "variants": {
            "red": "CC0000",
            "green": "00CC00",
            "blue": "0000CC",
            "yellow": "yellow"
          },
          "defaultVariant": "red",
          "state": "ENABLED",
          "targeting": {
            "if": [
               {
                "sharedOperations":  "email-with-faas"
              }, "green", null
            ]
          }
        },
        "fib-algo": {
          "variants": {
            "recursive": "recursive",
            "memo": "memo",
            "loop": "loop",
            "binet": "binet"
          },
          "defaultVariant": "recursive",
          "state": "ENABLED",
          "targeting": {
            "if": [
              {
                "sharedOperations":  "email-with-faas"
              }, "binet", null
            ]
          }
        }
    }
    "sharedOperations": {
        "email-with-faas":  {
                "in": ["@faas.com", {
                  "var": ["email"]
                }]
              }
    }
}

Requirements

  • Shared operations are defined in the schema
  • If there are collisions between shared operations, the priority goes to the later (e.g. example_operation < example_operation_secondary).
  • Targeting rules are compliant with the JSON Logic spec
  • An example config is added under config/samples
  • Readme includes an example configuration

Questions

  • Is shared operations a good property name? If not, what would be better?
  • What's the expected behavior if a targeting rule contains an invalid shared operation?

Resources

[FEATURE] Fractional Evaluation

Requirements

Overview

Fraction evaluation enable percentage based rollouts of a feature. It allows features to be tested pseudorandomly using a value from the evaluation context such as a targeting key.

The following example is how a configuration may look

{
   "fib-algo":{
      "state":"ENABLED",
      "variants":{
         "recursive":"recursive",
         "memo":"memo",
         "loop":"loop",
         "binet":"binet"
      },
      "defaultVariant":"recursive",
      "targeting":{
         "if":[
            {
               "in":[
                  "@faas.com"
               ],
               "var":[
                  "email"
               ]
            },
            {
               "factionalEvaluation":[
                  {
                     "bucketBy":"email"
                  },
                  {
                     "distribution":[
                        [
                           "recursive",
                           25
                        ],
                        [
                           "memo",
                           25
                        ],
                        [
                           "loop",
                           25
                        ],
                        [
                           "binet",
                           25
                        ]
                     ]
                  }
               ]
            },
            null
         ]
      }
   }
}

The factionalEvaluation operation would be a custom operation in JSON logic that would return the variant name. The variant name would be selected based on the percentage (or fraction) defined in the distribution using a hash of the flag key and bucketBy value.

Requirements:

  • Factional evaluation is deterministic
  • Targeting rules are compliant with the JSON logic spec
  • An example config is added under config/samples
  • Readme includes an example configuration

Questions:

  • Can bucketBy be optional and default to targetingKey?
  • What should happen if the bucketBy value is invalid?
  • What should happen if the percentage doesn't add up to 100?
  • Should we use percentages or fractions?

Links:

How should invalid configurations changes be handled?

Summary

An invalid flag configuration during start up will result in no flags being loaded into flagD. There's also an error message that says "set state: invalid JSON file". However, if an invalid flag configuration is made after flagD initially loads, the previously valid flag value is retained. How should this be handled?

Reproduce startup behavior

  1. Change line 7 in the example_flags.json file to a string value
  2. Run flagD ./flagd start -f config/samples/example_flags.json --service-provider http --sync-provider filepath
  3. cURL the flag curl -X POST "localhost:8080/flags/myBoolFlag/resolve/boolean"

You should see {"error_code":"FLAG_NOT_FOUND","reason":"ERROR"}

Reproduce runtime behavior

  1. Revert the example_flags.json file if you modified it above
  2. Run flagD ./flagd start -f config/samples/example_flags.json --service-provider http --sync-provider filepath
  3. Change line 7 in the example_flags.json file to a string value
  4. cURL the flag curl -X POST "localhost:8080/flags/myBoolFlag/resolve/boolean"

You should see {"value":true,"reason":"STATIC","variant":"on"}

[BUG] Go install fails due to generated dependencies missing

Observed behavior

 go install github.com/open-feature/flagd@latest
go: finding module for package github.com/open-feature/flagd/schemas/proto/go-server/schema/v1
.gvm/pkgsets/go1.18.3/global/pkg/mod/github.com/open-feature/[email protected]/pkg/eval/json_evaluator.go:18:12: pattern flagd-definitions.json: no matching files found
.gvm/pkgsets/go1.18.3/global/pkg/mod/github.com/open-feature/[email protected]/pkg/service/grpc_service.go:10:2: module github.com/open-feature/flagd@latest found (v0.0.6), but does not contain package github.com/open-feature/flagd/schemas/proto/go-server/schema/v1

Expected Behavior

No response

Steps to reproduce

go install github.com/open-feature/flagd@latest

Using golang >1.18

Remove required query-param "default-value"

There's no need to pass this value. The provider calling flagd already has the default, and can default the value whenever appropriate. It only over-complicates the API and the validation.

Unix socket support

Overview

In some situations (i.e. Kubernetes pod) it would be useful to communicate over a Unix socket instead of a TCP.

Requirements

  • Configurable via an argument
  • TCP is used by default but can be disabled
  • Socket is properly closed when FlagD is stopped

[Feature] JSON merge function for multiple files

In order to combine multiple feature flag json files ( in the case of aggregation etc )
Then we should have a function to merge multiple json files....

e.g.

MergeJSON(jsonPayload [][]byte) ([]byte, error)

Build push is failing

error: failed to solve: unexpected status: 403 Forbidden
Error: buildx failed with: error: failed to solve: unexpected status: 403 Forbidden

I am out some of this week, but could you take a look at this before we approve any more PR's?

[FEATURE] Unix socket support

Requirements

OFEP

Requirements:

  • Create a socket path parameter within flagD
  • Implement uniform ctx cancellation for all services
  • grpc_service.go to support unix sockets through a new method that returns a net connection
  • Modifiy the existing http_service code to leverage a similar net connection start
  • Write tests

Update readme

The readme has become out of date. The example json should be updated to match the new schema.

flag type in definition

i think it will be better to define the flag type in a definition like this

"myBoolFlag": {
      "state": "ENABLED",
      "type": "BOOLEAN",
      "variants": {
        "on": true,
        "off": false
      },
      "defaultVariant": "on"
    },

it will let us abstract the endpoints

"/flags/{flag-key}/resolve/boolean"
"/flags/{flag-key}/resolve/numbe"
"/flags/{flag-key}/resolve/object"
"/flags/{flag-key}/resolve/string"

to one endpoint

"/flags/{flag-key}/resolve/"

Invalid Default Variant

Overview

A flag configuration with an invalidate default variant returns the error code TYPE_MISMATCH.

Steps to reproduce

  1. Start flagd ./flagd start -f config/samples/example_flags.json --service-provider http --sync-provider filepath
  2. Change the defaultVariant to other.
  3. cURL flagd curl -X POST "localhost:8080/flags/myBoolFlag/resolve/boolean"

Expectation

Invalid default variants should be caught during configuration validation.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.