diff --git a/.github/workflows/markdown-checks.yaml b/.github/workflows/markdown-checks.yaml new file mode 100644 index 000000000..57c283a5a --- /dev/null +++ b/.github/workflows/markdown-checks.yaml @@ -0,0 +1,19 @@ +name: Markdown checks + +on: + push: + paths: + - '**.md' + pull_request: + paths: + - '**.md' + +jobs: + markdown-lint: + runs-on: ubuntu-22.04 + steps: + - name: Check out code + uses: actions/checkout@v3 + + - name: Lint Markdown files + run: make markdownlint diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml new file mode 100644 index 000000000..0ceebaaa6 --- /dev/null +++ b/.markdownlint-cli2.yaml @@ -0,0 +1,18 @@ +config: + line-length: + line_length: 500 + tables: false + code_blocks: false + no-inline-html: + allowed_elements: + - details + - summary + - img + github-admonition: true + max-one-sentence-per-line: true + +ignores: + - "**/CHANGELOG.md" + - "docs/specification" + - "node_modules" + - "tmp" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6afa1fa3f..71f26bc5e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,15 @@ -Welcome! +# Welcome There are a few things to consider before contributing to flagd. Firstly, there's [a code of conduct](https://github.com/open-feature/.github/blob/main/CODE_OF_CONDUCT.md). TLDR: be respectful. -Any contributions are expected to include unit tests. These can be validated with `make test` or the automated github workflow will run them on PR creation. +Any contributions are expected to include unit tests. +These can be validated with `make test` or the automated github workflow will run them on PR creation. This project uses a go workspace, to setup the project run + ```shell make workspace-init ``` @@ -15,8 +17,10 @@ make workspace-init The go version in the `go.work` is the currently supported version of go. The project uses remote buf packages, changing the remote generation source will require a one-time registry configuration: + ```shell export GOPRIVATE=buf.build/gen/go ``` -Thanks! Issues and pull requests following these guidelines are welcome. +Thanks! +Issues and pull requests following these guidelines are welcome. diff --git a/Makefile b/Makefile index 5b584a8cf..75891afea 100644 --- a/Makefile +++ b/Makefile @@ -60,3 +60,18 @@ mockgen: install-mockgen mockgen -source=core/pkg/eval/ievaluator.go -destination=core/pkg/eval/mock/ievaluator.go -package=evalmock generate-docs: cd flagd; go run ./cmd/doc/main.go + +# Markdown lint configuration +# +# - .markdownlintignore holds the configuration for files to be ignored +# - .markdownlint.yaml contains the rules for markdownfiles +MDL_DOCKER_VERSION := next +ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) +MDL_CMD := docker run -v $(ROOT_DIR):/workdir --rm + +.PHONY: markdownlint markdownlint-fix +markdownlint: + $(MDL_CMD) davidanson/markdownlint-cli2-rules:$(MDL_DOCKER_VERSION) "**/*.md" + +markdownlint-fix: + $(MDL_CMD) --entrypoint="markdownlint-cli2-fix" davidanson/markdownlint-cli2-rules:$(MDL_DOCKER_VERSION) "**/*.md" \ No newline at end of file diff --git a/README.md b/README.md index 9f2755c1b..7b5ab6cef 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +

@@ -13,6 +14,7 @@

+ ## Features @@ -26,19 +28,24 @@ ## Get started -Flagd is a simple command line tool for fetching and evaluating feature flags for services. It is designed to conform with the OpenFeature specification. To get started, follow the installation instructions in the [docs](https://github.com/open-feature/flagd/blob/main/docs/README.md). +Flagd is a simple command line tool for fetching and evaluating feature flags for services. +It is designed to conform with the OpenFeature specification. +To get started, follow the installation instructions in the [docs](https://github.com/open-feature/flagd/blob/main/docs/README.md). ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to the OpenFeature project. -Our community meetings are held regularly and open to everyone. Check the [OpenFeature community calendar](https://calendar.google.com/calendar/u/0?cid=MHVhN2kxaGl2NWRoMThiMjd0b2FoNjM2NDRAZ3JvdXAuY2FsZW5kYXIuZ29vZ2xlLmNvbQ) for specific dates and for the Zoom meeting links. +Our community meetings are held regularly and open to everyone. +Check the [OpenFeature community calendar](https://calendar.google.com/calendar/u/0?cid=MHVhN2kxaGl2NWRoMThiMjd0b2FoNjM2NDRAZ3JvdXAuY2FsZW5kYXIuZ29vZ2xlLmNvbQ) for specific dates and for the Zoom meeting links. Thanks so much to our contributors. + + Made with [contrib.rocks](https://contrib.rocks). diff --git a/docs/README.md b/docs/README.md index 14baba580..0465c1804 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,7 +4,8 @@ This directory contains all flagd documentation, see table of contents below: ## Usage -There are many ways to get started with flagd, the sections below run through some simple deployment options. Once the flagd service is running flag evaluation requests can either be made via one of the language specific flagd providers, or, directly via curl. +There are many ways to get started with flagd, the sections below run through some simple deployment options. +Once the flagd service is running flag evaluation requests can either be made via one of the language specific flagd providers, or, directly via curl. - [Getting started](./usage/getting_started.md) - [Flagd providers](./usage/flagd_providers.md) @@ -12,7 +13,8 @@ There are many ways to get started with flagd, the sections below run through so ## Flag Configuration -Flagd is configured via CLI arguments on startup, these configuration options can be found in the flagd configuration section. The remaining sections cover the flag configurations themselves, which are JSON representations of the flag variants and targeting rules. +Flagd is configured via CLI arguments on startup, these configuration options can be found in the flagd configuration section. +The remaining sections cover the flag configurations themselves, which are JSON representations of the flag variants and targeting rules. - [Flagd Configuration](./configuration/configuration.md) - [Flag configuration](./configuration/flag_configuration.md) @@ -34,5 +36,3 @@ This section documents any behavior of flagd which may seem unexpected, currentl - [Caching](./other_resources/caching.md) - [Snap](./other_resources/snap.md) - [Systemd service](./other_resources/systemd_service.md) - - diff --git a/docs/configuration/configuration.md b/docs/configuration/configuration.md index b745fc266..b6d46ec0f 100644 --- a/docs/configuration/configuration.md +++ b/docs/configuration/configuration.md @@ -1,32 +1,33 @@ # Configuration -`flagd` supports configuration via config file, environment variables and flags. In cases of conflict, flags have the +`flagd` supports configuration via config file, environment variables and flags. +In cases of conflict, flags have the highest priority, followed by environment variables and finally config file. Supported flags are documented (auto-generated) [here](./flagd_start.md). -Environment variable keys are uppercased, prefixed with `FLAGD_` and all `-` are replaced with `_`. For example, +Environment variable keys are uppercased, prefixed with `FLAGD_` and all `-` are replaced with `_`. +For example, `sync-provider-args` in environment variable form is `FLAGD_SYNC_PROVIDER_ARGS`. Config file expects the keys to have the exact naming as the flags. -### URI patterns +## URI patterns -Any URI passed to flagd via the `--uri` flag must follow one of the 4 following patterns to ensure that it is passed to the correct implementation: +Any URI passed to flagd via the `--uri` flag must follow one of the 4 following patterns to ensure that it is passed to the correct implementation: | Sync | Pattern | Example | |------------|---------------------------------------|---------------------------------------| | Kubernetes | `core.openfeature.dev/namespace/name` | `core.openfeature.dev/default/my-crd` | | Filepath | `file:path/to/my/flag` | `file:etc/flagd/my-flags.json` | | Remote | `http(s)://flag-source-url` | `https://my-flags.com/flags` | -| Grpc | `grpc(s)://flag-source-url` | `grpc://my-flags-server` | +| Grpc | `grpc(s)://flag-source-url` | `grpc://my-flags-server` | - -### Customising sync providers +## Customising sync providers Custom sync providers can be used to provide flag evaluation logic. -#### Kubernetes provider +### Kubernetes provider The Kubernetes 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) spec. @@ -36,22 +37,26 @@ To use an existing FeatureFlagConfiguration custom resource, start flagD with th flagd start --uri core.openfeature.dev/default/my_example ``` -### Source Configuration +## Source Configuration -While a URI may be passed to flagd via the `--uri` flag, some implementations may require further configurations. In these cases the `--sources` flag should be used. -The flag takes a string argument, which should be a JSON representation of an array of `SourceConfig` objects. Alternatively, these configurations should be passed to +While a URI may be passed to flagd via the `--uri` flag, some implementations may require further configurations. +In these cases the `--sources` flag should be used. +The flag takes a string argument, which should be a JSON representation of an array of `SourceConfig` objects. +Alternatively, these configurations should be passed to flagd via config file, specified using the `--config` flag. -| Field | Type | Note | +| Field | Type | Note | |-------------|------------------------------------------------------------|----------------------------------------------------| | uri | required `string` | | | provider | required `string` (`file`, `kubernetes`, `http` or `grpc`) | | | bearerToken | optional `string` | Used for http sync | | certPath | optional `string` | Used for grpcs sync when TLS certificate is needed | -The `uri` field values do not need to follow the [URI patterns](#uri-patterns), the provider type is instead derived from the provider field. If the prefix is supplied, it will be removed on startup without error. +The `uri` field values do not need to follow the [URI patterns](#uri-patterns), the provider type is instead derived from the provider field. +If the prefix is supplied, it will be removed on startup without error. Example start command using a filepath sync provider and the equivalent config file definition: + ```sh ./bin/flagd start --sources='[{"uri":"config/samples/example_flags.json","provider":"file"},{"uri":"http://my-flag-source.json","provider":"http","bearerToken":"bearer-dji34ld2l"}]{"uri":"default/my-flag-config","provider":"kubernetes"},{"uri":"grpc://my-flag-source:8080","provider":"grpc"}' ``` diff --git a/docs/configuration/flag_configuration.md b/docs/configuration/flag_configuration.md index adb9d0844..66b212b3e 100644 --- a/docs/configuration/flag_configuration.md +++ b/docs/configuration/flag_configuration.md @@ -1,28 +1,36 @@ # Flag Configuration -A flagd configuration is represented as a JSON object. Feature flag configurations can be found under `flags` and each item within `flags` represents a flag key (the unique identifier for a flag) and its corresponding configuration. +A flagd configuration is represented as a JSON object. +Feature flag configurations can be found under `flags` and each item within `flags` represents a flag key (the unique identifier for a flag) and its corresponding configuration. -Sample configurations can be found at https://github.com/open-feature/flagd/tree/main/config/samples. +Sample configurations can be found at . ## Flag configuration properties ### State -`state` is **required** property. Validate states are "ENABLED" or "DISABLED". When the state is set to "DISABLED", flagd will behave like the flag doesn't exist. +`state` is **required** property. +Validate states are "ENABLED" or "DISABLED". +When the state is set to "DISABLED", flagd will behave like the flag doesn't exist. Example: -``` +```json "state": "ENABLED" ``` ### Variants -`variants` is a **required** property. It is an object containing the possible variations supported by the flag. All the values of the object **must** be the same type (e.g. boolean, numbers, string, JSON). The type used as the variant value will correspond directly affects how the flag is accessed. For example, to use a flag configured with boolean values the `/schema.v1.Service/ResolveBoolean` path should be used. If another path such as `/schema.v1.Service/ResolveString` is called, a type mismatch occurred and an error is returned. +`variants` is a **required** property. +It is an object containing the possible variations supported by the flag. +All the values of the object **must** be the same type (e.g. boolean, numbers, string, JSON). +The type used as the variant value will correspond directly affects how the flag is accessed. +For example, to use a flag configured with boolean values the `/schema.v1.Service/ResolveBoolean` path should be used. +If another path such as `/schema.v1.Service/ResolveString` is called, a type mismatch occurred and an error is returned. Example: -``` +```json "variants": { "red": "c05543", "green": "2f5230", @@ -32,7 +40,7 @@ Example: Example: -``` +```json "variants": { "on": true, "off": false @@ -41,7 +49,7 @@ Example: Example of an invalid configuration: -``` +```json "variants": { "on": true, "off": "false" @@ -50,11 +58,13 @@ Example of an invalid configuration: ### Default Variant -`defaultVariant` is a **required** property. The value **must** match the name of one of the variants defined above. The default variant is always used unless a targeting rule explicitly overrides it. +`defaultVariant` is a **required** property. +The value **must** match the name of one of the variants defined above. +The default variant is always used unless a targeting rule explicitly overrides it. Example: -``` +```json "variants": { "on": true, "off": false @@ -64,7 +74,7 @@ Example: Example: -``` +```json "variants": { "red": "c05543", "green": "2f5230", @@ -75,7 +85,7 @@ Example: Example of an invalid configuration: -``` +```json "variants": { "red": "c05543", "green": "2f5230", @@ -86,9 +96,16 @@ Example of an invalid configuration: ### Targeting Rules -`targeting` is an **optional** property. A targeting rule **must** be valid JSON. Flagd uses a modified version of [JSON Logic](https://jsonlogic.com/), as well as some custom pre-processing, to evaluate these rules. The output of the targeting rule **must** match the name of one of the variants defined above. If an invalid or null value is is returned by the targeting rule, the `defaultVariant` value is used. If no targeting rules are defined, the response reason will always be `STATIC`, this allows for the client side caching of these flag values, this behavior is described [here](../other_resources/caching.md). +`targeting` is an **optional** property. +A targeting rule **must** be valid JSON. +Flagd uses a modified version of [JSON Logic](https://jsonlogic.com/), as well as some custom pre-processing, to evaluate these rules. +The output of the targeting rule **must** match the name of one of the variants defined above. +If an invalid or null value is is returned by the targeting rule, the `defaultVariant` value is used. +If no targeting rules are defined, the response reason will always be `STATIC`, this allows for the client side caching of these flag values, this behavior is described [here](../other_resources/caching.md). -The [JSON Logic playground](https://jsonlogic.com/play.html) is a great way to experiment with new targeting rules. The following example shows how a rule could be configured to return `binet` when the email (which comes from evaluation context) contains `@faas.com`. If the email wasn't included in the evaluation context or doesn't contain `@faas.com`, null is returned and the `defaultVariant` is used instead. +The [JSON Logic playground](https://jsonlogic.com/play.html) is a great way to experiment with new targeting rules. +The following example shows how a rule could be configured to return `binet` when the email (which comes from evaluation context) contains `@faas.com`. +If the email wasn't included in the evaluation context or doesn't contain `@faas.com`, null is returned and the `defaultVariant` is used instead.
Click here to see how this targeting rule would look in the JSON Logic playground. @@ -124,3 +141,5 @@ The [JSON Logic playground](https://jsonlogic.com/play.html) is a great way to e 1. Click `Compute` 1. confirm the output show `"binet"` 1. Optionally, experiment with different rules and data + +
diff --git a/docs/configuration/flag_configuration_merging.md b/docs/configuration/flag_configuration_merging.md index 8a1218666..c964c372a 100644 --- a/docs/configuration/flag_configuration_merging.md +++ b/docs/configuration/flag_configuration_merging.md @@ -1,7 +1,8 @@ # Flag Configuration Merging -Flagd can be configured to read from multiple sources at once, when this is the case flagd will merge all flag configurations into a single -merged state. For example: +Flagd can be configured to read from multiple sources at once, when this is the case flagd will merge all flag configurations into a single +merged state. +For example: ```mermaid flowchart LR @@ -9,8 +10,11 @@ flowchart LR source-B -->|config-B| store ``` -In this example, `source-A` and `source-B` provide a single flag configuration, `config-A` and `config-B` respectively. The merge logic for this configuration is simple, both flag configurations are added to the `store`. -In most scenarios, these flag sources will be supplying `n` number of configurations, using a unique flag key for each configuration. However, as multiple sources are being used, there is the opportunity for keys to be duplicated, intentionally or not, between flag sources. In these situations `flagd` uses a merge priority order to ensure that its behavior is consistent. +In this example, `source-A` and `source-B` provide a single flag configuration, `config-A` and `config-B` respectively. +The merge logic for this configuration is simple, both flag configurations are added to the `store`. +In most scenarios, these flag sources will be supplying `n` number of configurations, using a unique flag key for each configuration. +However, as multiple sources are being used, there is the opportunity for keys to be duplicated, intentionally or not, between flag sources. +In these situations `flagd` uses a merge priority order to ensure that its behavior is consistent. Merge order is dictated by the order that `sync-providers` and `uris` are defined, with the latest defined source taking precedence over those defined before it, as an example: @@ -18,7 +22,8 @@ Merge order is dictated by the order that `sync-providers` and `uris` are define ./bin/flagd start --uri file:source-A.json --uri file:source-B.json --uri file:source-C.json ``` -When `flagd` is started with the command defined above, `source-B` takes priority over `source-A`, whilst `source-C` takes priority over both `source-B` and `source-A`. Using the above example, if a flag key is duplicated across all 3 sources, then the configuration from `source-C` would be the only one stored in the merged state. +When `flagd` is started with the command defined above, `source-B` takes priority over `source-A`, whilst `source-C` takes priority over both `source-B` and `source-A`. +Using the above example, if a flag key is duplicated across all 3 sources, then the configuration from `source-C` would be the only one stored in the merged state. ```mermaid flowchart LR @@ -29,9 +34,12 @@ flowchart LR ## State Resync Events -Given the above example, the `source-A` and `source-B` 'versions' of flag configuration `config-A` have been discarded, so if a delete event in `source-C` results in the removal of `config-A`, there will no longer be any reference of` config-A` in flagd's store. As a result of this flagd will return `FLAG_NOT_FOUND` errors, and the OpenFeature SDK will always return the default value. +Given the above example, the `source-A` and `source-B` 'versions' of flag configuration `config-A` have been discarded, so if a delete event in `source-C` results in the removal of `config-A`, there will no longer be any reference of`config-A` in flagd's store. +As a result of this flagd will return `FLAG_NOT_FOUND` errors, and the OpenFeature SDK will always return the default value. -To prevent flagd falling out of sync with its flag sources during delete events, resync events are used. When a delete event results in a flag configuration being removed from the merged state, the full set of configurations is requested from all flag sources, and the merged state is rebuilt. As a result, the value of `config-A` from `source-B` will be stored in the merged state, preventing flagd from returning `FLAG_NOT_FOUND` errors. +To prevent flagd falling out of sync with its flag sources during delete events, resync events are used. +When a delete event results in a flag configuration being removed from the merged state, the full set of configurations is requested from all flag sources, and the merged state is rebuilt. +As a result, the value of `config-A` from `source-B` will be stored in the merged state, preventing flagd from returning `FLAG_NOT_FOUND` errors. ```mermaid flowchart LR @@ -41,6 +49,7 @@ flowchart LR source-C -->|delete config-A|source-C-config-A source-C-config-A --> resync-event ``` + In the example above, a delete event results in a resync event being fired, as `source-C` has deleted its 'version' of `config-A`, this results in a new merge state being formed from the remaining configurations. ```mermaid @@ -51,4 +60,4 @@ flowchart LR ``` -Resync events may lead to further resync events if the returned flag configurations result in further delete events, however the state will eventually be resolved correctly. \ No newline at end of file +Resync events may lead to further resync events if the returned flag configurations result in further delete events, however the state will eventually be resolved correctly. diff --git a/docs/configuration/flagd.md b/docs/configuration/flagd.md index 90cd032fd..97c4ae6a6 100644 --- a/docs/configuration/flagd.md +++ b/docs/configuration/flagd.md @@ -1,3 +1,4 @@ + ## flagd Flagd is a simple command line tool for fetching and presenting feature flags to services. It is designed to conform to Open Feature schema for flag definitions. @@ -12,6 +13,6 @@ Flagd is a simple command line tool for fetching and presenting feature flags to ### SEE ALSO -* [flagd start](flagd_start.md) - Start flagd -* [flagd version](flagd_version.md) - Print the version number of FlagD +* [flagd start](flagd_start) - Start flagd +* [flagd version](flagd_version) - Print the version number of FlagD diff --git a/docs/configuration/flagd_start.md b/docs/configuration/flagd_start.md index aef1d746b..e314ecd9d 100644 --- a/docs/configuration/flagd_start.md +++ b/docs/configuration/flagd_start.md @@ -1,3 +1,4 @@ + ## flagd start Start flagd @@ -34,5 +35,5 @@ flagd start [flags] ### SEE ALSO -* [flagd](flagd.md) - Flagd is a simple command line tool for fetching and presenting feature flags to services. It is designed to conform to Open Feature schema for flag definitions. +* [flagd](flagd) - Flagd is a simple command line tool for fetching and presenting feature flags to services. It is designed to conform to Open Feature schema for flag definitions. diff --git a/docs/configuration/flagd_version.md b/docs/configuration/flagd_version.md index df2d55bd5..305ecafb2 100644 --- a/docs/configuration/flagd_version.md +++ b/docs/configuration/flagd_version.md @@ -1,3 +1,4 @@ + ## flagd version Print the version number of FlagD @@ -21,5 +22,5 @@ flagd version [flags] ### SEE ALSO -* [flagd](flagd.md) - Flagd is a simple command line tool for fetching and presenting feature flags to services. It is designed to conform to Open Feature schema for flag definitions. +* [flagd](flagd) - Flagd is a simple command line tool for fetching and presenting feature flags to services. It is designed to conform to Open Feature schema for flag definitions. diff --git a/docs/configuration/fractional_evaluation.md b/docs/configuration/fractional_evaluation.md index 0ef77a23c..89fc44cc4 100644 --- a/docs/configuration/fractional_evaluation.md +++ b/docs/configuration/fractional_evaluation.md @@ -1,14 +1,20 @@ # Fractional Evaluation The `fractionalEvaluation` operation is a custom JsonLogic operation which deterministically selects a variant based on -the defined distribution of each variant (as a percentage). This works by hashing ([xxHash](https://cyan4973.github.io/xxHash/)) -the given data point, converting it into an int in the range [0, 99]. Whichever range this int falls in decides which variant -is selected. As hashing is deterministic we can be sure to get the same result every time for the same data point. +the defined distribution of each variant (as a percentage). +This works by hashing ([xxHash](https://cyan4973.github.io/xxHash/)) +the given data point, converting it into an int in the range [0, 99]. +Whichever range this int falls in decides which variant +is selected. +As hashing is deterministic we can be sure to get the same result every time for the same data point. ## Fractional evaluation configuration - -The `fractionalEvaluation` can be added as part of a targeting definition. The value is an array and the first element is the name of the property to use from the evaluation context. This value should typically be something that remains consistent for the duration of a users session (e.g. email or session ID). The other elements in the array are nested arrays with the first element representing a variant and the second being the percentage that this option is selected. There is no limit to the number of elements but the configured percentages must add up to 100. +The `fractionalEvaluation` can be added as part of a targeting definition. +The value is an array and the first element is the name of the property to use from the evaluation context. +This value should typically be something that remains consistent for the duration of a users session (e.g. email or session ID). +The other elements in the array are nested arrays with the first element representing a variant and the second being the percentage that this option is selected. +There is no limit to the number of elements but the configured percentages must add up to 100. ```js // Factional evaluation property name used in a targeting rule @@ -90,8 +96,10 @@ curl -X POST "localhost:8013/schema.v1.Service/ResolveString" -d '{"flagKey":"he ``` Result: -``` + +```json {"value":"#00FF00","reason":"TARGETING_MATCH","variant":"green"} ``` -Notice that rerunning either curl command will always return the same variant and value. The only way to get a different value is to change the email or update the `fractionalEvaluation` configuration. +Notice that rerunning either curl command will always return the same variant and value. +The only way to get a different value is to change the email or update the `fractionalEvaluation` configuration. diff --git a/docs/configuration/reusable_targeting_rules.md b/docs/configuration/reusable_targeting_rules.md index 417881633..ae0746ff2 100644 --- a/docs/configuration/reusable_targeting_rules.md +++ b/docs/configuration/reusable_targeting_rules.md @@ -1,10 +1,12 @@ # Reusable targeting rules -At the same level as the `flags` key one can define an `$evaluators` object. Each object defined under `$evaluators` is -a reusable targeting rule. In any targeting rule one can reference a defined reusable targeting rule, foo, like so: +At the same level as the `flags` key one can define an `$evaluators` object. +Each object defined under `$evaluators` is +a reusable targeting rule. +In any targeting rule one can reference a defined reusable targeting rule, foo, like so: `"$ref": "foo"` -Example +## Example Flags/evaluators defined as such: diff --git a/docs/help/http_int_response.md b/docs/help/http_int_response.md index 8a0e7d53a..35716ad1d 100644 --- a/docs/help/http_int_response.md +++ b/docs/help/http_int_response.md @@ -1,22 +1,30 @@ # HTTP(S) Service Integer Response Behavior - Why is my `int` response a `string`? Command: + ```sh curl -X POST "localhost:8013/schema.v1.Service/ResolveInt" -d '{"flagKey":"myIntFlag","context":{}}' -H "Content-Type: application/json" ``` + Result: + ```sh {"value":"1","reason":"DEFAULT","variant":"one"} ``` -When interacting directly with the flagD http(s) api and requesting an `int` the response type will be a `string`. This behaviour is introduced by [grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway), which uses [proto3 json mapping](https://developers.google.com/protocol-buffers/docs/proto3#json) to build the response object. If a number value is required, and none of the provided SDK's can be used, then it is recommended to use the `float64` endpoint instead: -
+ +When interacting directly with the flagD http(s) api and requesting an `int` the response type will be a `string`. +This behaviour is introduced by [grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway), which uses [proto3 json mapping](https://developers.google.com/protocol-buffers/docs/proto3#json) to build the response object. +If a number value is required, and none of the provided SDK's can be used, then it is recommended to use the `float64` endpoint instead: + Command: + ```sh curl -X POST "localhost:8013/schema.v1.Service/ResolveFloat" -d '{"flagKey":"myIntFlag","context":{}}' -H "Content-Type: application/json" ``` + Result: + ```sh {"value":1.23,"reason":"DEFAULT","variant":"one"} ``` diff --git a/docs/help/omitted_value_from_response.md b/docs/help/omitted_value_from_response.md index 304e8dd9d..66f9baeb2 100644 --- a/docs/help/omitted_value_from_response.md +++ b/docs/help/omitted_value_from_response.md @@ -15,4 +15,4 @@ Result: ```sh {"reason":"DEFAULT","variant":"off"} -``` \ No newline at end of file +``` diff --git a/docs/other_resources/caching.md b/docs/other_resources/caching.md index 0599db3b7..763bf09b8 100644 --- a/docs/other_resources/caching.md +++ b/docs/other_resources/caching.md @@ -2,23 +2,25 @@ `flagd` has a caching strategy implementable by providers that support server-to-client streaming. -#### Cacheable flags +## Cacheable flags -`flagd` sets the `reason` of a flag evaluation as `STATIC` when no targeting rules are configured for the flag. A client can safely store the result of a static evaluation in its cache indefinitely (until the configuration of the flag changes, see [cache invalidation](#cache-invalidation)). +`flagd` sets the `reason` of a flag evaluation as `STATIC` when no targeting rules are configured for the flag. +A client can safely store the result of a static evaluation in its cache indefinitely (until the configuration of the flag changes, see [cache invalidation](#cache-invalidation)). Put simply in pseudocode: -``` +```pseudo if reason == "STATIC" { isFlagCacheable = true } ``` -#### Cache invalidation +## Cache invalidation -`flagd` emits events to the server-to-client stream, among these is the `configuration_change` event. The structure of this event is as such: +`flagd` emits events to the server-to-client stream, among these is the `configuration_change` event. +The structure of this event is as such: -``` +```json { "type": "delete", // ENUM:["delete","write","update"] "source": "/flag-configuration.json", // the source of the flag configuration diff --git a/docs/other_resources/creating_providers.md b/docs/other_resources/creating_providers.md index 51a761900..ea3008d43 100644 --- a/docs/other_resources/creating_providers.md +++ b/docs/other_resources/creating_providers.md @@ -7,7 +7,8 @@ Prerequisites: - Understanding of [general provider concepts](https://docs.openfeature.dev/docs/reference/concepts/provider/) - Proficiency in the chosen programming language (check the language isn't already covered by the [existing providers](../usage/flagd_providers.md)) -Communication with `flagd` is possible via Protobuf or REST. Protobuf is the recommended approach (as such, that is the approach outlined in this document) when possible (not possible if no tooling exists for a given technology). +Communication with `flagd` is possible via Protobuf or REST. +Protobuf is the recommended approach (as such, that is the approach outlined in this document) when possible (not possible if no tooling exists for a given technology). ## Protobuf @@ -18,13 +19,16 @@ Protobuf schemas define the contract between a client (provider) and server (`fl Leverage the [buf CLI](https://docs.buf.build/installation) to generate a `flagd` client in the chosen technology: Add the [open-feature schema repository](https://github.com/open-feature/schemas) as a submodule -``` + +```shell git submodule add --force https://github.com/open-feature/schemas.git ``` + Create a `buf.gen.{chosen language}.yaml` for the chosen language in `schemas/protobuf` (if it doesn't already exist) using one of the other files as a template (find a plugin for the chosen language [here](https://buf.build/protocolbuffers/plugins)) and create a pull request with this file. Generate the code (this step ought to be automated in the build process for the chosen technology so that the generated code is never committed) -``` + +```shell cd schemas/protobuf buf generate --template buf.gen.{chosen language}.yaml ``` @@ -32,12 +36,15 @@ buf generate --template buf.gen.{chosen language}.yaml Move the generated code (following convention for the chosen language) and add its location to .gitignore ## Provider construction + (__using Go as an example__) +Create a provider struct/class/type (whichever is relevant to the chosen language) with an exported (public) constructor allowing configuration (e.g. `flagd` host). +Give the provider an unexported (private) client field, set this field as the client generated by the previous step. -Create a provider struct/class/type (whichever is relevant to the chosen language) with an exported (public) constructor allowing configuration (e.g. `flagd` host). Give the provider an unexported (private) client field, set this field as the client generated by the previous step. +Create methods for the provider to satisfy the chosen language SDK's provider interface. +These methods ought to wrap the built client's methods. -Create methods for the provider to satisfy the chosen language SDK's provider interface. These methods ought to wrap the built client's methods. ```go type Provider struct { client generated.Client @@ -51,9 +58,9 @@ func NewProvider(options ...ProviderOption) *Provider { for _, opt := range opts { opt(provider) } - + provider.client: generated.NewClient(provider.flagdHost), - + return provider } @@ -139,4 +146,5 @@ Create a pull request appending the provider to the list [here](../usage/flagd_p ### Open an issue to document the provider -Create an issue in docs.openfeature.dev [here](https://github.com/open-feature/docs.openfeature.dev/issues/new?assignees=&labels=provider&template=document-provider.yaml&title=%5BProvider%5D%3A+). This will ensure the provider is added to OpenFeature's website. +Create an issue in docs.openfeature.dev [here](https://github.com/open-feature/docs.openfeature.dev/issues/new?assignees=&labels=provider&template=document-provider.yaml&title=%5BProvider%5D%3A+). +This will ensure the provider is added to OpenFeature's website. diff --git a/docs/other_resources/high_level_architecture.md b/docs/other_resources/high_level_architecture.md index a6f2001b7..6645fce21 100644 --- a/docs/other_resources/high_level_architecture.md +++ b/docs/other_resources/high_level_architecture.md @@ -4,37 +4,44 @@ Flagd consists of four main components - Service, Evaluator engine, Runtime and Sync. -The service component exposes the evaluator engine for client libraries. It further exposes an interface +The service component exposes the evaluator engine for client libraries. +It further exposes an interface for flag configuration change notifications. -The sync component has implementations to update flag configurations from various sources. The current implementation +The sync component has implementations to update flag configurations from various sources. +The current implementation contain sync providers for files, K8s resources and HTTP endpoints. -The evaluation engine's role is twofold, it acts as an intermediary between configuration changes and the state store by interpreting change events and forwarding the necessary changes to the state store. It also performs the feature flag evaluations based on evaluation requests coming from feature flag libraries. +The evaluation engine's role is twofold, it acts as an intermediary between configuration changes and the state store by interpreting change events and forwarding the necessary changes to the state store. +It also performs the feature flag evaluations based on evaluation requests coming from feature flag libraries. The Runtime stays in between these components and coordinates operations. - ## Sync component -The Sync component contains implementations of the ISync interface. The interface contract simply allows updating -flag configurations watched by the respective implementation. For example, the file sync provider watches for a change +The Sync component contains implementations of the ISync interface. +The interface contract simply allows updating +flag configurations watched by the respective implementation. +For example, the file sync provider watches for a change (ex: - add, modify, remove) of a specific file in the file system. -The update provided by sync implementation is pushed to the evaluator engine, which interprets the event and forwards it to the state store. Change notifications generated in the +The update provided by sync implementation is pushed to the evaluator engine, which interprets the event and forwards it to the state store. +Change notifications generated in the process gets pushed to event subscribers. ## Readiness & Liveness probes -Flagd exposes HTTP liveness and readiness probes. These probes can be used for K8s deployments. With default +Flagd exposes HTTP liveness and readiness probes. +These probes can be used for K8s deployments. +With default start-up configurations, these probes are exposed at the following URLs, -- Liveness: http://localhost:8014/healthz -- Readiness: http://localhost:8014/readyz +- Liveness: +- Readiness: ### Definition of Liveness @@ -42,6 +49,9 @@ The liveness probe becomes active and HTTP 200 status is served as soon as Flagd ### Definition of Readiness -The readiness probe becomes active similar to the liveness probe as soon as Flagd service is up and running. However, -the probe emits HTTP 412 until all sync providers are ready. This status changes to HTTP 200 when all sync providers at -least have one successful data sync. The status does not change from there on. \ No newline at end of file +The readiness probe becomes active similar to the liveness probe as soon as Flagd service is up and running. +However, +the probe emits HTTP 412 until all sync providers are ready. +This status changes to HTTP 200 when all sync providers at +least have one successful data sync. +The status does not change from there on. diff --git a/docs/other_resources/snap.md b/docs/other_resources/snap.md index 79790b025..b1ab3afd0 100644 --- a/docs/other_resources/snap.md +++ b/docs/other_resources/snap.md @@ -3,24 +3,24 @@ flagD can be released on the snapstore as a snap package. The homepage for the snap is found [here](https://snapcraft.io/flagd/) -#### Login +## Login `snapcraft login` -#### Build +## Build `snapcraft` Run this command from `snap` directory. -#### Release +## Release -``` +```shell snapcraft upload flagd__amd64.snap --release=candidate ``` -#### Promotion +## Promotion -``` +```shell snapcraft promote flagd --from-channel=candidate --to-channel=stable -``` \ No newline at end of file +``` diff --git a/docs/other_resources/systemd_service.md b/docs/other_resources/systemd_service.md index fceda4bde..7d6e359c2 100644 --- a/docs/other_resources/systemd_service.md +++ b/docs/other_resources/systemd_service.md @@ -1,13 +1,14 @@ # Run flagD as a systemd Service -To install as a systemd service clone the repo and run `sudo make install`. This will place the binary by default in `/usr/local/bin`. +To install as a systemd service clone the repo and run `sudo make install`. +This will place the binary by default in `/usr/local/bin`. There will also be a default provider and sync enabled ( http / filepath ) both of which can be modified in the flagd.service. Validation can be run with `systemctl status flagd` And result similar to below will be seen -``` +```console flagd.service - "A generic feature flag daemon" Loaded: loaded (/etc/systemd/system/flagd.service; disabled; vendor preset: enabled) Active: active (running) since Mon 2022-05-30 12:19:55 BST; 5min ago diff --git a/docs/usage/evaluation_examples.md b/docs/usage/evaluation_examples.md index 2333cdce6..d6d0c9f3a 100644 --- a/docs/usage/evaluation_examples.md +++ b/docs/usage/evaluation_examples.md @@ -21,9 +21,11 @@ docker run -p 8013:8013 -v $(pwd)/:/etc/flagd/ -it --pull=always ghcr.io/open-feature/flagd:latest start --uri file:./etc/flagd/example_flags.flagd.json ``` -1. Changes made in `example_flags.flagd.json` will immediately take affect. Go ahead, give a shot! +1. Changes made in `example_flags.flagd.json` will immediately take affect. + Go ahead, give a shot! -Flagd is now ready to perform flag evaluations over either HTTP or gRPC. In this example, we'll utilize HTTP via cURL. +Flagd is now ready to perform flag evaluations over either HTTP or gRPC. +In this example, we'll utilize HTTP via cURL. ### Resolve a boolean value @@ -39,8 +41,6 @@ Result: {"value":true,"reason":"DEFAULT","variant":"on"} ``` -
- ### Resolve a string value Command: @@ -55,8 +55,6 @@ Result: {"value":"val1","reason":"DEFAULT","variant":"key1"} ``` -
- ### Resolve a integer value Command: @@ -70,9 +68,8 @@ Result: ```sh {"value":"1","reason":"DEFAULT","variant":"one"} ``` + [Why is this int response a string](https://github.com/open-feature/flagd/blob/main/docs/help/http_int_response.md) -
-
### Resolve a float value @@ -88,8 +85,6 @@ Result: {"value":1.23,"reason":"DEFAULT","variant":"one"} ``` -
- ### Resolve an object value Command: @@ -104,8 +99,6 @@ Result: {"value":{"key":"val"},"reason":"DEFAULT","variant":"object1"} ``` -
- ### Resolve a boolean value with evaluation context Command: @@ -120,11 +113,10 @@ Result: {"value":true,"reason":"TARGETING_MATCH","variant":"on"} ``` -
- ### Return value type mismatch error -A type mismatch error is returned when the resolved value of a flag does not match the type requested. In the example below, the resolved value of `myBoolFlag` is a `boolean` but the request expects a `string` to be returned. +A type mismatch error is returned when the resolved value of a flag does not match the type requested. +In the example below, the resolved value of `myBoolFlag` is a `boolean` but the request expects a `string` to be returned. Command: @@ -138,8 +130,6 @@ Result: {"code":"invalid_argument","message":"TYPE_MISMATCH"} ``` -
- ### Return flag not found error The flag not found error is returned when flag key in the request doesn't match any configured flags. diff --git a/docs/usage/flagd_providers.md b/docs/usage/flagd_providers.md index 54a0dd838..f6146889f 100644 --- a/docs/usage/flagd_providers.md +++ b/docs/usage/flagd_providers.md @@ -1,6 +1,7 @@ -# Flagd Providers +# Flagd Providers -Flagd providers are used for interacting with the `flagd` service via the OpenFeature SDK, they act as the translation layer between the evaluation API and the flag management system in use (in this case `flagd`). Documentation for each language specific provider can be found below: +Flagd providers are used for interacting with the `flagd` service via the OpenFeature SDK, they act as the translation layer between the evaluation API and the flag management system in use (in this case `flagd`). +Documentation for each language specific provider can be found below: | Language | Provider | | ----------- | ----------- | diff --git a/docs/usage/getting_started.md b/docs/usage/getting_started.md index 2a82921fa..45da7f51c 100644 --- a/docs/usage/getting_started.md +++ b/docs/usage/getting_started.md @@ -2,7 +2,8 @@ ## Installation -There are many ways to get started with flagd. Choose the method that best serves your requirements to get started. +There are many ways to get started with flagd. +Choose the method that best serves your requirements to get started. ### Docker @@ -19,15 +20,17 @@ There are many ways to get started with flagd. Choose the method that best serve ### Release binary -1. Download pre-built binaries from https://github.com/open-feature/flagd/releases +1. Download pre-built binaries from ### Systemd service Documentation for installing flagd as a systemd service can be found [here](../other_resources/systemd_service.md) ### Open Feature Operator -The OpenFeature Operator is a Kubernetes native operator that allows you to expose feature flags to your applications. It injects a flagD sidecar into your pod and allows you to poll the flagD server for feature flags in a variety of ways. -To get started with the operator, view the project here: https://github.com/open-feature/open-feature-operator + +The OpenFeature Operator is a Kubernetes native operator that allows you to expose feature flags to your applications. +It injects a flagD sidecar into your pod and allows you to poll the flagD server for feature flags in a variety of ways. +To get started with the operator, view the project here: ## Next Steps diff --git a/flagd/cmd/doc.go b/flagd/cmd/doc.go index d261d862c..b28052a8a 100644 --- a/flagd/cmd/doc.go +++ b/flagd/cmd/doc.go @@ -1,8 +1,19 @@ package cmd -import "github.com/spf13/cobra/doc" +import ( + "strings" + "github.com/spf13/cobra/doc" +) // GenerateDoc generates cobra docs of the cmd func GenerateDoc(path string) error { - return doc.GenMarkdownTree(rootCmd, path) + linkHandler := func(name string) string { + return strings.ReplaceAll(name, ".md", "") + } + + filePrepender := func(filename string) string { + return "\n" + } + return doc.GenMarkdownTreeCustom(rootCmd, path, filePrepender, linkHandler) } + diff --git a/flagd/tests/integration/README.md b/flagd/tests/integration/README.md index 17cac2ff7..7aef7d769 100644 --- a/flagd/tests/integration/README.md +++ b/flagd/tests/integration/README.md @@ -2,19 +2,26 @@ The continuous integration runs a set of [gherkin integration tests](https://github.com/open-feature/test-harness/blob/main/features). If you'd like to run them locally, first pull the `test-harness` git submodule -``` + +```shell git submodule update --init --recursive ``` + then build the `flagd` binary -``` + +```shell make build ``` + then run the `flagd` binary -``` + +```shell ./bin/flagd start -f file:test-harness/symlink_testing-flags.json ``` + and finally run -``` + +```shell make integration-test ``` @@ -23,25 +30,34 @@ make integration-test To run the integration tests against a `flagd` instance configured to use TLS, do the following: Generate a cert and key in the repository root -``` + +```shell openssl req -x509 -out localhost.crt -keyout localhost.key \ -newkey rsa:2048 -nodes -sha256 \ -subj '/CN=localhost' -extensions EXT -config <( \ printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth") ``` + build the `flagd` binary -``` + +```shell make build ``` + then run the `flagd` binary with tls configuration -``` + +```shell ./bin/flagd start -f file:test-harness/symlink_testing-flags.json -c ./localhost.crt -k ./localhost.key ``` + finally, either run the tests with an explicit path to the certificate: -``` + +```shell make ARGS="-tls true -cert-path ./../../localhost.crt" integration-test ``` + or, run without the path, defaulting to the host's root certificate authorities set (for this to work, the certificate must be registered and trusted in the host's system certificates) -``` + +```shell make ARGS="-tls true" integration-test ```