diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 6c7292ac1..4650baef7 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -8,14 +8,12 @@ on: paths-ignore: - "README.md" - "docs/**" - - "web-docs/**" pull_request: branches: - main paths-ignore: - "README.md" - "docs/**" - - "web-docs/**" env: GO_VERSION: '1.20' diff --git a/.gitignore b/.gitignore index bcfc00e18..25d338527 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,5 @@ go.work.sum bin/ # built documentation -site \ No newline at end of file +site +.cache/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 643f35ec4..89959ef2f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "spec"] path = spec url = https://github.com/open-feature/spec.git +[submodule "schemas"] + path = schemas + url = https://github.com/open-feature/schemas.git diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml index fec47c686..9da958b1e 100644 --- a/.markdownlint-cli2.yaml +++ b/.markdownlint-cli2.yaml @@ -11,9 +11,14 @@ config: - br github-admonition: true max-one-sentence-per-line: true + code-block-style: false # not compatible with mkdocs "details" panes ignores: - "**/CHANGELOG.md" - "docs/specification" - "node_modules" - "tmp" + - "**/protos.md" # auto-generated + - "schemas" # submodule + - "spec" # submodule + - "test-harness" # submodule diff --git a/Makefile b/Makefile index d640b6082..8c411502d 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,8 @@ ZD_CLIENT_IMG ?= zd-client:latest FLAGD_PROXY_IMG ?= flagd-proxy:latest FLAGD_PROXY_IMG_ZD ?= flagd-proxy:zd +DOCS_DIR ?= docs + workspace-init: workspace-clean go work init $(foreach module, $(ALL_GO_MOD_DIRS), go work use $(module);) @@ -105,4 +107,19 @@ 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 + $(MDL_CMD) --entrypoint="markdownlint-cli2-fix" davidanson/markdownlint-cli2-rules:$(MDL_DOCKER_VERSION) "**/*.md" + +.PHONY: pull-schemas-submodule +pull-schemas-submodule: + git submodule update schemas + +.PHONY: generate-proto-docs +generate-proto-docs: pull-schemas-submodule + docker run --rm -v ${PWD}/$(DOCS_DIR)/reference/specifications:/out -v ${PWD}/schemas/protobuf:/protos pseudomuto/protoc-gen-doc --doc_opt=markdown,protos-with-toc.md schema/v1/schema.proto sync/v1/sync_service.proto \ + && echo '' > ${PWD}/$(DOCS_DIR)/reference/specifications/protos.md \ + && sed '/^## Table of Contents/,/#top/d' ${PWD}/$(DOCS_DIR)/reference/specifications/protos-with-toc.md >> ${PWD}/$(DOCS_DIR)/reference/specifications/protos.md \ + && rm -f ${PWD}/$(DOCS_DIR)/reference/specifications/protos-with-toc.md + +.PHONY: run-web-docs +run-web-docs: generate-docs generate-proto-docs + docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material \ No newline at end of file diff --git a/README.md b/README.md index 62a8f132c..36bb7324c 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ ## 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. +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 @@ -87,7 +87,7 @@ Experiment with flagd in your browser using [the Killercoda tutorial](https://ki --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](docs/configuration/configuration.md#uri-patterns). + `--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](./docs/reference/sync-configuration.md#uri-patterns). Multiple `--uri` parameters can be specified. In other words, flagd can retrieve flags from multiple sources simultaneously. @@ -120,7 +120,7 @@ Experiment with flagd in your browser using [the Killercoda tutorial](https://ki Updates to the underlying flag store (e.g. JSON file) are reflected by flagd in realtime. No restarts required. - flagd also supports boolean, integer, float and object flag types. Read more on the [evaluation examples page](docs/usage/evaluation_examples.md) + flagd also supports boolean, integer, float and object flag types. 4. Now that flagd is running, it is time to integrate into your application. Do this by using [an OpenFeature provider in a language of your choice](https://github.com/open-feature/flagd/blob/main/docs/usage/flagd_providers.md). @@ -130,7 +130,7 @@ Experiment with flagd in your browser using [the Killercoda tutorial](https://ki ## 📝 Further Documentation -Further documentation including flagd configuration options, fractional evaluation, targeting rules and flag configuration merging strategies can be found [on this page](docs/README.md). +Further documentation including flagd configuration options, fractional evaluation, targeting rules and flag configuration merging strategies can be found at [flagd.dev](https://flagd.dev/), or [in this repository](./docs/index.md). ## 🫶 Contributing diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 6a68277ec..000000000 --- a/docs/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# Docs - -This directory contains all flagd documentation, see table of contents below: - -## Quick Start / Basic Usage - -See the [main page](../README.md) for the quick start guide. - -## Installation Options - -[See all flagd installation options](usage/installation_options.md) - -## Copy and Paste Evaluation Options - -[This page](usage/evaluation_examples.md) provides copy and paste evaluation examples. - -## Flagd Configuration - -[Flagd is configured via CLI arguments on startup](configuration/configuration.md), this page describes all available options. - -## Flag Configuration - -This document describes the syntax for feature flag JSON configurations: [Flag configuration](configuration/flag_configuration.md). - -## Application Integration - -Once flagd is running, your next step is to integrate it into you application. [This page](usage/flagd_providers.md) shows all available integration options (called providers) in a variety of languages. - -## Targeting Rules - -flagd offers a functionality called targeting rules which rely on the incoming context sent by the client during flag evaluation. - -[This page](configuration/reusable_targeting_rules.md) describes how to define targeting rules. - -## Fractional Evaluation - -flagd supports [fractional evaluation](configuration/fractional_evaluation.md) meaning an incoming property in the context can be sub-divided at "evaluation time" into "buckets". - -[This page](configuration/fractional_evaluation.md) explains the concept and describes the technical implementation in detail. - -## Starts/Ends With Evaluation - -flagd supports [starts/ends_with evaluation](configuration/string_comparison_evaluation.md) meaning an incoming property in the context can be used -to determine whether a certain variant should be returned based on if its value starts or ends with a certain string. - -[This page](configuration/string_comparison_evaluation.md) explains the concept and describes the technical implementation in detail. - -## SemVer Evaluation - -flagd supports [sem_ver evaluation](configuration/sem_ver_evaluation.md) meaning an incoming property -representing a semantic version in the context can be used to determine whether a certain variant should be returned -based on if the version meets a certain criteria. - -[This page](configuration/sem_ver_evaluation.md) explains the concept and describes the technical implementation in detail. - -## Flag Merging - -flagd can retrieve flags from multiple sources simultaneously. [This page](configuration/flag_configuration_merging.md) describes the de-duplication and merging rules that occur if multiple identical flags are found from different flag sources. - -## Help - -This section documents any behavior of flagd which may seem unexpected: - -- [HTTP int response](./help/http_int_response.md) - -## Other Resources - -- [High level architecture](./other_resources/high_level_architecture.md) -- [Creating providers](./other_resources/creating_providers.md) -- [Caching](./other_resources/caching.md) -- [Snap](./other_resources/snap.md) -- [Systemd service](./other_resources/systemd_service.md) - -## Still Stuck? - -[Speak to the OpenFeature community](https://openfeature.dev/community) and someone will help. diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 000000000..852cb3f6a --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,45 @@ +# Architecture + +flagd architectures fall into two broad categories: those where the evaluation engine is deployed in a standalone process to which the client application connects ([RPC](#rpc-evaluation)), and those where the evaluation engine is embedded into the client application ([in-process](#in-process-evaluation)). + +## RPC vs In-Process Evaluation + +### RPC evaluation + +In RPC-based deployments one or more flagd instances deployed and exposed to client applications in your infrastructure. +flagd RPC providers use HTTP or gRPC to request flag evaluations from flagd. +The request payload contains the [flag key](https://openfeature.dev/specification/glossary#flag-key) identifying the flag to be evaluated, as well as the relevant [evaluation context](https://openfeature.dev/specification/glossary#evaluation-context). +The flagd instance is configured to watch one or more [syncs](./concepts/syncs.md), and merges them to build its set of flags (see [here](./concepts/syncs.md#merging) for more details on flag definition merging). +When sync sources are updated, flagd will send notifications to clients that flags have changed, enabling applications to react to changes by re-evaluating flags. + +This architecture is can be leveraged by very simple clients, since no in-process engine is needed; in fact, you can evaluate flags directly from a terminal console using the `cURL` utility. +One disadvantage of this pattern is the latency involved in the remote request (though flagd typically takes <10ms for an evaluation, and can evaluate thousands of flags per second). + +```mermaid +--- +title: RPC Evaluation +--- +erDiagram + flagd ||--o{ "sync (file)" : watches + flagd ||--o{ "sync (http)" : polls + flagd ||--o{ "sync (grpc)" : "sync.proto (gRPC/stream)" + flagd ||--o{ "sync (kubernetes)" : watches + "client app (+ flagd RPC provider)" ||--|| flagd : "evaluation.proto (gRPC/stream) / HTTP" +``` + +### In-Process evaluation + +In-process deployments embed the flagd evaluation engine directly into the client application through the use of an [in-process provider](./deployment.md#in-process). +The in-process provider is connected via the sync protocol to an implementing [gRPC service](./concepts/syncs.md#grpc-sync) that provides the flag definitions. +This pattern requires an in-process implementation of the flagd evaluation engine, but has the benefit of no I/O overhead, since no inter-process communication is required. + +```mermaid +--- +title: In-Process Evaluation +--- +erDiagram + "client app (+ flagd in-process provider)" ||--|| "sync (grpc)" : "sync.proto (gRPC/stream)" +``` + + + \ No newline at end of file diff --git a/docs/assets/demo.flagd.json b/docs/assets/demo.flagd.json new file mode 100644 index 000000000..e61619133 --- /dev/null +++ b/docs/assets/demo.flagd.json @@ -0,0 +1,22 @@ +{ + "flags": { + "show-welcome-banner": { + "state": "ENABLED", + "variants": { + "on": true, + "off": false + }, + "defaultVariant": "off" + }, + "background-color": { + "state": "ENABLED", + "variants": { + "red": "#FF0000", + "blue": "#0000FF", + "green": "#00FF00", + "yellow": "#FFFF00" + }, + "defaultVariant": "red" + } + } +} \ No newline at end of file diff --git a/docs/assets/logo-white.svg b/docs/assets/logo-white.svg new file mode 100644 index 000000000..dbbf98e32 --- /dev/null +++ b/docs/assets/logo-white.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/docs/concepts/feature-flagging.md b/docs/concepts/feature-flagging.md new file mode 100644 index 000000000..60437395e --- /dev/null +++ b/docs/concepts/feature-flagging.md @@ -0,0 +1,24 @@ +# Feature Flagging + +Feature flags are a software development technique that allows teams to enable, disable or change the behavior of certain features or code paths in a product or service, without modifying the source code. + +## OpenFeature Compliance + +[OpenFeature](https://openfeature.dev/) is an open standard that provides a vendor-agnostic, community-driven API for feature flagging. +The flagd project is fully OpenFeature-compliant. +In fact, flagd was initially conceived as a reference implementation for an OpenFeature backend, but has become a powerful tool in its own right. +For this reason, you'll find flagd's concepts and terminology align with that of the OpenFeature project. +Within the context of an OpenFeature-compliant feature flag solution, flagd artifacts and libraries comprise the [flag management system](https://openfeature.dev/specification/glossary#flag-management-system) and [providers](https://openfeature.dev/specification/glossary#provider). +These artifacts and libraries alone won't allow you to evaluate flags in your application - you'll also need the [OpenFeature SDK](https://openfeature.dev/specification/glossary#feature-flag-sdk) for your language as well, which provides the evaluation API for application developers to use. + +## Supported Feature Flagging Use-Cases + +Below is a non-exhaustive table of common feature flag use-cases, and how flagd supports them: + +| Use case | flagd Feature | +| ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| flag evaluation | Returns the value of a particular feature flag, if the flag is enabled. Supports flags of various types including boolean, numeric, string, and JSON. | +| dynamic configuration | Flag definitions from any sync source are monitored for changes, with some syncs supporting near real time updates. | +| dynamic (context-sensitive) evaluation | flagd evaluations are context sensitive. Rules can use arbitrary context attributes as inputs for flag evaluation logic. | +| fractional evaluation / random assignment | flagd's [fractional](../reference/custom-operations/fractional-operation.md) custom operation supports pseudorandom assignment of flag values. | +| progressive roll-outs | Progressive roll-outs of new features can be accomplished by leveraging the [fractional](../reference/custom-operations/fractional-operation.md) custom operation as well as automation in your build pipeline, SCM, or infrastructure which updates the distribution over time. | diff --git a/docs/concepts/syncs.md b/docs/concepts/syncs.md new file mode 100644 index 000000000..5ef3b0993 --- /dev/null +++ b/docs/concepts/syncs.md @@ -0,0 +1,113 @@ +# Syncs + +Syncs are a core part of flagd; they are the abstraction that enables different sources for feature flag definitions. +flagd can connect to one or more sync sources to + +## Available syncs + +### Filepath sync + +The file path sync provider reads and watch the source file for updates(ex: changes and deletions). + +```shell +flagd start --uri file:etc/featureflags.json +``` + +In this example, `etc/featureflags.json` is a valid feature flag definition file accessible by the flagd process. +See [sync source](../reference/sync-configuration.md#source-configuration) configuration for details. + +--- + +### HTTP sync + +The HTTP sync provider fetch flags from a remote source and periodically poll the source for flag definition updates. + +```shell +flagd start --uri https://my-flag-source.json +``` + +In this example, `https://my-flag-source.json` is a remote endpoint responding valid feature flag definition when +invoked with **HTTP GET** request. +The polling interval, port, TLS settings, and authentication information can be configured. +See [sync source](../reference/sync-configuration.md#source-configuration) configuration for details. + +--- + +### gRPC sync + +The gRPC sync provider streams flag definitions from a gRPC sync provider implementation. +This stream connection is defined by the [sync service protobuf definition](https://github.com/open-feature/schemas/blob/main/protobuf/sync/v1/sync_service.proto). + +```shell +flagd start --uri grpc://grpc-sync-source +``` + +In this example, `grpc-sync-source` is a grpc target implementing [sync.proto](../reference/flag-sync-protocol.md) definition. +See [sync source](../reference/sync-configuration.md#source-configuration) configuration for details. + +--- + +### Kubernetes sync + +The Kubernetes sync provider allows flagd to connect to a Kubernetes cluster and evaluate flags against a specified +FeatureFlagConfiguration resource as defined within +the [open-feature-operator](https://github.com/open-feature/open-feature-operator/blob/main/apis/core/v1alpha1/featureflagconfiguration_types.go) +spec. +This configuration is best used in conjunction with the [OpenFeature Operator](https://github.com/open-feature/open-feature-operator). + +To use an existing FeatureFlagConfiguration custom resource, start flagd with the following command: + +```shell +flagd start --uri core.openfeature.dev/default/my_example +``` + +In this example, `default/my_example` expected to be a valid FeatureFlagConfiguration resource, where `default` is the +namespace and `my_example` being the resource name. +See [sync source](../reference/sync-configuration.md#source-configuration) configuration for details. + +## Merging + +Flagd can be configured to read from multiple sources at once, when this is the case flagd will merge all flag definition into a single +merged state. + +For example: + +![flag merge 1](../images/flag-merge-1.svg) + +In this example, `source-A` and `source-B` provide a single flag definition, the `foo` flag and the `bar` flag respectively. +The merge logic for this definition is simple, both flag definition are added to the `store`. + +In most scenarios, these flag sources will be supplying `n` number of definition, using a unique flag key for each definition. + +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: + +```sh +./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 definition from `source-C` would be the only one stored in the merged state. + +![flag merge 2](../images/flag-merge-2.svg) + +### State Resync Events + +Given the above example, the `source-A` and `source-B` 'versions' of flag definition the `foo` have been discarded, so if a delete event in `source-C` results in the removal of the `foo`flag, there will no longer be any reference of `foo` 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 definition being removed from the merged state, the full set of definition is requested from all flag sources, and the merged state is rebuilt. +As a result, the value of the `foo` flag from `source-B` will be stored in the merged state, preventing flagd from returning `FLAG_NOT_FOUND` errors. + +![flag merge 3](../images/flag-merge-3.svg) + +In the example above, a delete event results in a resync event being fired, as `source-C` has deleted its 'version' of the `foo`, this results in a new merge state being formed from the remaining definition. + +![flag merge 4](../images/flag-merge-4.svg) + +Resync events may lead to further resync events if the returned flag definition result in further delete events, however the state will eventually be resolved correctly. diff --git a/docs/configuration/configuration.md b/docs/configuration/configuration.md deleted file mode 100644 index 5b1f070ee..000000000 --- a/docs/configuration/configuration.md +++ /dev/null @@ -1,172 +0,0 @@ -# Configuration - - -- [Configuration](#configuration) - - [Sync providers](#sync-providers) - - [Kubernetes provider](#kubernetes-provider) - - [Filepath provider](#filepath-provider) - - [Remote provider](#remote-provider) - - [GRPC provider](#grpc-provider) - - [Sync provider configurations](#sync-provider-configurations) - - [URI patterns](#uri-patterns) - - [Source Configuration](#source-configuration) - - -`flagd` supports configuration via config file, environment variables and start-up flags. In cases of a conflict, -start-up flags have the highest priority, followed by environment variables and config file. - -Supported start-up flags are documented (auto-generated) [here](./flagd_start.md). - -Environment variable keys are uppercase, prefixed with `FLAGD_` and all `-` are replaced with `_`. For example, -`metrics-port` flag in environment variable form is `FLAGD_METRICS_PORT`. - -The config file expects the keys to have the exact naming as startup-flags flags. - -## Sync providers - -Sync providers are a core part of flagd; they are the abstraction that enables different sources for feature flag configurations. -flagd currently support the following sync providers: - -- [Configuration](#configuration) - - [Sync providers](#sync-providers) - - [Kubernetes provider](#kubernetes-provider) - - [Filepath provider](#filepath-provider) - - [Remote provider](#remote-provider) - - [GRPC provider](#grpc-provider) - - [Sync provider configurations](#sync-provider-configurations) - - [URI patterns](#uri-patterns) - - [Source Configuration](#source-configuration) - -### Kubernetes provider - -The Kubernetes sync provider allows flagd to connect to a Kubernetes cluster and evaluate flags against a specified -FeatureFlagConfiguration resource as defined within -the [open-feature-operator](https://github.com/open-feature/open-feature-operator/blob/main/apis/core/v1alpha1/featureflagconfiguration_types.go) -spec. - -To use an existing FeatureFlagConfiguration custom resource, start flagD with the following command: - -```shell -flagd start --uri core.openfeature.dev/default/my_example -``` - -In this example, `default/my_example` expected to be a valid FeatureFlagConfiguration resource, where `default` is the -namespace and `my_example` being the resource name. - -### Filepath provider - -The file path sync provider reads and watch the source file for updates(ex:- changes and deletions). - -```shell -flagd start --uri file:etc/featureflags.json -``` - -In this example, `etc/featureflags.json` is a valid feature flag configuration file accessible by the flagd runtime. - -### Remote provider - -The HTTP sync provider fetch flags from a remote source and periodically poll the source for flag configuration updates. - -```shell -flagd start --uri https://my-flag-source.json -``` - -In this example, `https://my-flag-source.json` is a remote endpoint responding valid feature flag configurations when -invoked with **HTTP GET** request. - -### GRPC provider - -The GRPC sync provider streams flag configurations from a grpc sync provider implementation. This stream connection is ruled -by -the [sync service protobuf definition](https://github.com/open-feature/schemas/blob/main/protobuf/sync/v1/sync_service.proto). - -```shell -flagd start --uri grpc://grpc-sync-source -``` - -In this example, `grpc-sync-source` is a grpc target implementing flagd protobuf definition. - -There are two mechanisms to provide configurations of sync providers, - -- [URI patterns](#uri-patterns) -- [Source Configuration](#source-configuration) - -## Sync provider configurations - -### URI patterns - -Any URI passed to flagd via the `--uri` flag must follow one of the 4 following patterns with prefixes to ensure that -it is passed to the correct implementation: - -| Sync | Prefix | Example | -| ---------- | ---------------------- | ------------------------------------- | -| Kubernetes | `core.openfeature.dev` | `core.openfeature.dev/default/my-crd` | -| Filepath | `file:` | `file:etc/flagd/my-flags.json` | -| Remote | `http(s)://` | `https://my-flags.com/flags` | -| Grpc | `grpc(s)://` | `grpc://my-flags-server` | - -### 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 flagd accepts a string argument, which should be a JSON representation of an array of `SourceConfig` objects. - -Alternatively, these configurations can be passed to flagd via config file, specified using the `--config` flag. - -| Field | Type | Note | -| ----------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | -| uri | required `string` | Flag configuration source of the provider | -| provider | required `string` | Provider type - `file`, `kubernetes`, `http` or `grpc` | -| bearerToken | optional `string` | Used for http sync; token gets appended to `Authorization` header with [bearer schema](https://www.rfc-editor.org/rfc/rfc6750#section-2.1) | -| interval | optional `uint32` | Used for http sync; requests will be made at this interval. Defaults to 5 seconds. | -| tls | optional `boolean` | Enable/Disable secure TLS connectivity. Currently used only by GRPC sync. Default(ex:- if unset) is false, which will use an insecure connection | -| providerID | optional `string` | Value binds to grpc connection's providerID field. GRPC server implementations may use this to identify connecting flagd instance | -| selector | optional `string` | Value binds to grpc connection's selector field. GRPC server implementations may use this to filter flag configurations | -| certPath | optional `string` | Used for grpcs sync when TLS certificate is needed. If not provided, system certificates will be used for TLS connection | - -The `uri` field values **do not** follow the [URI patterns](#uri-patterns). The provider type is instead derived -from the `provider` field. Only exception is the remote provider where `http(s)://` is expected by default. Incorrect -URIs will result in a flagd start-up failure with errors from the respective sync provider implementation. - -Given below are example sync providers, startup command and equivalent config file definition: - -Sync providers, - -- `file` - config/samples/example_flags.json -- `http` - -- `kubernetes` - default/my-flag-config -- `grpc`(insecure) - grpc-source:8080 -- `grpc`(secure) - my-flag-source:8080 - -Startup command, - -```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-source:8080","provider":"grpc"}, - {"uri":"my-flag-source:8080","provider":"grpc", "certPath": "/certs/ca.cert", "tls": true, "providerID": "flagd-weatherapp-sidecar", "selector": "source=database,app=weatherapp"}]' -``` - -Configuration file, - -```yaml -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: my-flag-source:8080 - provider: grpc - - uri: my-flag-source:8080 - provider: grpc - certPath: /certs/ca.cert - tls: true - providerID: flagd-weatherapp-sidecar - selector: 'source=database,app=weatherapp' -``` diff --git a/docs/configuration/flag_configuration_merging.md b/docs/configuration/flag_configuration_merging.md deleted file mode 100644 index 8f94c6741..000000000 --- a/docs/configuration/flag_configuration_merging.md +++ /dev/null @@ -1,3 +0,0 @@ -# Flag Configuration Merging - -Flagd merging logic has moved. It can now be found here: [flagd merging](https://flagd.dev/nonk8s/flagmerging/) diff --git a/docs/configuration/flagd_telemetry.md b/docs/configuration/flagd_telemetry.md deleted file mode 100644 index 66c6a26cb..000000000 --- a/docs/configuration/flagd_telemetry.md +++ /dev/null @@ -1,3 +0,0 @@ -# Telemetry - -This content has moved. See [flagd OpenTelemetry](https://flagd.dev/concepts/opentelemetry/) diff --git a/docs/configuration/reusable_targeting_rules.md b/docs/configuration/reusable_targeting_rules.md deleted file mode 100644 index ae0746ff2..000000000 --- a/docs/configuration/reusable_targeting_rules.md +++ /dev/null @@ -1,70 +0,0 @@ -# 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: -`"$ref": "foo"` - -## Example - -Flags/evaluators defined as such: - -```json -{ - "flags": { - "fibAlgo": { - "variants": { - "recursive": "recursive", - "memo": "memo", - "loop": "loop", - "binet": "binet" - }, - "defaultVariant": "recursive", - "state": "ENABLED", - "targeting": { - "if": [ - { - "$ref": "emailWithFaas" - }, "binet", null - ] - } - } - }, - "$evaluators": { - "emailWithFaas": { - "in": ["@faas.com", { - "var": ["email"] - }] - } - } -} -``` - -becomes (once the `$evaluators` have been substituted): - -```json -{ - "flags": { - "fibAlgo": { - "variants": { - "recursive": "recursive", - "memo": "memo", - "loop": "loop", - "binet": "binet" - }, - "defaultVariant": "recursive", - "state": "ENABLED", - "targeting": { - "if": [ - { - "in": ["@faas.com", { - "var": ["email"] - }] - }, "binet", null - ] - } - } - } -} -``` diff --git a/docs/deployment.md b/docs/deployment.md new file mode 100644 index 000000000..0beb65d99 --- /dev/null +++ b/docs/deployment.md @@ -0,0 +1,82 @@ +# Deployment + +## Docker + +:octicons-terminal-24: Install from the command line: + +```shell +docker pull ghcr.io/open-feature/flagd:latest +``` + +:octicons-code-square-24: Use as base image in Dockerfile: + +```dockerfile +FROM ghcr.io/open-feature/flagd:latest +``` + +### Kubernetes + +flagd was designed with cloud-native paradigms in mind. +You can run it as a sidecar, or as a central service in your cluster. +If you're interested in a full-featured solution for using flagd in Kubernetes, consider the [OpenFeature operator](https://github.com/open-feature/open-feature-operator). + +For more information, see [OpenFeature Operator](./reference/openfeature-operator/installation.md). + +--- + +## Binary + +:fontawesome-brands-linux::fontawesome-brands-windows::fontawesome-brands-apple: Binaries are available in x86/ARM. + +[Release](https://github.com/open-feature/flagd/releases) + +### systemd + +A systemd wrapper is available [here](https://github.com/open-feature/flagd/blob/main/systemd/flagd.service). + +### Homebrew + +```shell +brew install flagd +``` + +### Snap + +```shell +sudo snap install flagd +``` + +### Go binary + +```shell +go install github.com/open-feature/flagd/flagd@latest +``` + +--- + +## In-Process + +### :fontawesome-brands-golang: Go in-process provider + +[flagd-in-process/pkg](https://pkg.go.dev/github.com/open-feature/go-sdk-contrib/providers/flagd-in-process/pkg) + +```shell +go get github.com/open-feature/go-sdk-contrib/providers/flagd-in-process/pkg@latest +``` + +### :fontawesome-brands-java: Java in-process provider + +#### Maven + +```xml + + dev.openfeature.contrib.providers + flagd + +``` + +#### Gradle + +```gradle +implementation 'dev.openfeature.contrib.providers:flagd' +``` diff --git a/docs/faq.md b/docs/faq.md new file mode 100644 index 000000000..1afd48f35 --- /dev/null +++ b/docs/faq.md @@ -0,0 +1,37 @@ +# Frequently Asked Questions + +> Why do I need this? Can't I just use environment variables? + +Feature flags are not environment variables. +If you need to update your flag values without restarting your application, target specific users, randomly assign values for experimentation, or perform scheduled roll-outs, you should consider using feature flags. +If the values are always static, an environment variable or static configuration may be sufficient. + +For more information on feature-flagging concepts, see [feature-flagging](./concepts/feature-flagging.md). + +--- + +> Why is it called "flagd"? + +Please see [naming](./reference/naming.md). + +--- + +> What is flagd's relationship to OpenFeature? + +flagd is sub-project of OpenFeature and aims to be fully [OpenFeature-compliant](./concepts/feature-flagging.md#openfeature-compliance). + +--- + +> How do I run flagd? + +You can run flagd as a standalone application, accessible over HTTP or gRPC, or you can embed it into your application. +Please see [architecture](./architecture.md) and [deployment](./deployment.md) for more information. + +--- + +> Why doesn't flagd support {_my desired feature_}? + +Because you haven't opened a PR or created an issue! + +We're always adding new functionality to flagd, and welcome additions and ideas from new contributors. +Don't hesitate to [open an issue](https://github.com/open-feature/flagd/issues)! diff --git a/docs/help/http_int_response.md b/docs/help/http_int_response.md deleted file mode 100644 index f316afd87..000000000 --- a/docs/help/http_int_response.md +++ /dev/null @@ -1,3 +0,0 @@ -# HTTP(S) Service Integer Response Behavior - -This page has moved and is now located here: [flagd troubleshooting](https://flagd.dev/troubleshooting) diff --git a/web-docs/images/flag-merge-1.svg b/docs/images/flag-merge-1.svg similarity index 100% rename from web-docs/images/flag-merge-1.svg rename to docs/images/flag-merge-1.svg diff --git a/web-docs/images/flag-merge-2.svg b/docs/images/flag-merge-2.svg similarity index 100% rename from web-docs/images/flag-merge-2.svg rename to docs/images/flag-merge-2.svg diff --git a/web-docs/images/flag-merge-3.svg b/docs/images/flag-merge-3.svg similarity index 100% rename from web-docs/images/flag-merge-3.svg rename to docs/images/flag-merge-3.svg diff --git a/web-docs/images/flag-merge-4.svg b/docs/images/flag-merge-4.svg similarity index 100% rename from web-docs/images/flag-merge-4.svg rename to docs/images/flag-merge-4.svg diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..9d2cfb655 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,38 @@ +# Introduction + +## What is flagd? + +_flagd_ is a _feature flag evaluation engine_. +Think of it as a ready-made, open source, OpenFeature-compliant feature flag backend system. + +With flagd you can: + +* modify flags in real time +* define flags of various types (boolean, string, number, JSON) +* use context-sensitive rules to target specific users or user-traits +* perform pseudorandom assignments for experimentation +* perform progressive roll-outs of new features +* aggregate flag definitions from multiple sources + +It doesn't include a UI, management console or a persistence layer. +It's configurable entirely via a POSIX-style CLI. +Thanks to it's minimalism, it's _extremely flexible_; you can leverage flagd as a sidecar alongside your application, an engine running in your application process, or as a central service evaluating thousands of flags per second. + +## How do I deploy flagd? + +flagd is designed to fit well into a variety of infrastructures, and can run on various architectures. +It run as a separate process or directly in your application (see [architecture](./architecture.md)). +It's distributed as a binary, container image, and various libraries (see [deployment](./deployment.md)). +If you're already leveraging containers in your infrastructure, you can extend the docker image with your required configuration. +You can also run flagd as a service on a VM or a "bare-metal" host. +If you'd prefer not to run an additional process at all, you can run the flagd evaluation engine directly in your application. +No matter how you run flagd, you will need to supply it with feature flags. +The [flag definitions](./reference/flag-definitions.md) supplied to flagd are monitored for changes which will be immediately reflected in flagd's evaluations. +Currently supported sources include files, HTTP endpoints, Kubernetes custom resources, and proto-compliant gRPC services (see [syncs](./concepts/syncs.md), [sync configuration](./reference/sync-configuration.md)). + +## How do I use flagd? + +flagd is fully [OpenFeature compliant](./concepts/feature-flagging.md#openfeature-compliance). +To leverage it in your application you must use the OpenFeature SDK and flagd provider for your language. +You can configure the provider to connect to a flagd instance you deployed earlier (evaluating flags over gRPC) or use the in-process evaluation engine to do flag evaluations directly in your application. +Once you've configured the OpenFeature SDK, you can start evaluating the feature flags configured in your flagd definitions. diff --git a/docs/other_resources/caching.md b/docs/other_resources/caching.md deleted file mode 100644 index 763bf09b8..000000000 --- a/docs/other_resources/caching.md +++ /dev/null @@ -1,32 +0,0 @@ -# Caching - -`flagd` has a caching strategy implementable by providers that support server-to-client streaming. - -## 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)). - -Put simply in pseudocode: - -```pseudo -if reason == "STATIC" { - isFlagCacheable = true -} -``` - -## 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: - -```json -{ - "type": "delete", // ENUM:["delete","write","update"] - "source": "/flag-configuration.json", // the source of the flag configuration - "flagKey": "foo" -} -``` - -A client should invalidate the cache of any flag found in a `configuration_change` event to prevent stale data. -If the connection drops all cache values must be cleared (any number of events may have been missed). diff --git a/docs/other_resources/creating_providers.md b/docs/other_resources/creating_providers.md deleted file mode 100644 index 4e987f9d3..000000000 --- a/docs/other_resources/creating_providers.md +++ /dev/null @@ -1,183 +0,0 @@ -# Creating a flagd provider - -The provider is responsible for creating an abstraction between `flagd` and the OpenFeature SDK (for the [chosen technology](https://openfeature.dev/docs/reference/technologies/)). - -Prerequisites: - -- Understanding of [general provider concepts](https://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). - -## Protobuf - -Protobuf schemas define the contract between a client (provider) and server (`flagd`). `flagd`'s schemas are defined [here](https://github.com/open-feature/schemas/tree/main/protobuf). - -### Code generation - -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 -``` - -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 un-exported (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. - -```go -type Provider struct { - client generated.Client - flagdHost string -} - -type ProviderOption func(*Provider) - -func NewProvider(options ...ProviderOption) *Provider { - provider := &Provider{} - for _, opt := range opts { - opt(provider) - } - - provider.client: generated.NewClient(provider.flagdHost), - - return provider -} - -func WithHost(host string) ProviderOption { - return func(p *Provider) { - p.flagdHost = host - } -} - -func (p *Provider) BooleanEvaluation( - ctx context.Context, flagKey string, defaultValue bool, evalCtx of.FlattenedContext, -) of.BoolResolutionDetail { - res, err := p.client.ResolveBoolean(ctx, connect.NewRequest(&schemaV1.ResolveBooleanRequest{ - FlagKey: flagKey, - // omitted Context for simplicity, see Go's provider for completeness - })) - if err != nil { - return of.BoolResolutionDetail{ - Value: defaultValue, - ProviderResolutionDetail: of.ProviderResolutionDetail{ - ResolutionError: of.NewGeneralResolutionError(err.Error()), - Reason: of.Reason(res.Reason), - Variant: res.Variant, - }, - } - } - - return of.BoolResolutionDetail{ - Value: defaultValue, - ProviderResolutionDetail: of.ProviderResolutionDetail{ - Reason: of.Reason(res.Reason), - Variant: res.Variant, - }, - } -} - -// ... -``` - -## Caching - -Follow the guidance [here](./caching.md) to implement caching. - -## Provider lifecycle, initialization and shutdown - -With the release of the v0.6.0 spec, OpenFeature now outlines a lifecycle for provider initialization and shutdown. - -flagd providers should do the following to make use of OpenFeature v0.6.0 features: - -- start in a `NOT_READY` state -- initialize the streaming connection and set `state` to `READY` or `ERROR` in the `initialization` function - - note that the SDK will automatically emit `PROVIDER_READY`/`PROVIDER_ERROR` according to the termination of the `initialization` function -- throw an exception or terminate abnormally if a connection cannot be established during `initialization` -- attempt to restore the streaming connection (if the connection cannot be established or is broken): - - cache should be flushed after disconnection - - reconnection should be attempted with an exponential back-off, with a max-delay of `maxEventStreamRetryInterval` (see [configuration](#configuration)) - - reconnection should be attempted up to `maxEventStreamRetryDelay` times (see [configuration](#configuration)) - - `PROVIDER_READY` and `PROVIDER_CONFIGURATION_CHANGED` should be emitted, in that order, after successful reconnection -- emit `PROVIDER_CONFIGURATION_CHANGED` event and flush cache when a `configuration_change` message is received on the streaming connection -- close the streaming connection in the`shutdown` function - -```mermaid -stateDiagram-v2 - [*] --> NOT_READY - NOT_READY --> READY: initialize(), stream connected - NOT_READY --> ERROR: initialize(), unable to connect (retry) - READY --> ERROR: stream disconnected (flush cache, emit error*) - READY --> READY: configuration_change (flush cache, emit changed*) - ERROR --> READY: reconnect successful (emit ready*, changed*) - ERROR --> ERROR: maxEventStreamRetries reached - ERROR --> [*]: shutdown(), stream disconnected -``` - -\* ready=`PROVIDER_READY`, changed=`PROVIDER_CONFIGURATION_CHANGED`, error=`PROVIDER_ERROR` - -## Configuration - -Expose means to configure the provider aligned with the following priority system (highest to lowest). - -```mermaid -flowchart LR - constructor-parameters -->|highest priority| environment-variables -->|lowest priority| defaults -``` - -### Explicit declaration - -This takes the form of parameters to the provider's constructor, it has the highest priority. - -### Environment variables - -Read environment variables with sensible defaults (before applying the values explicitly declared to the constructor). - -| Option name | Environment variable name | Type | Options | Default | -| --------------------------- | ------------------------------------- | ------- | ------------ | -------------------------------------- | -| host | FLAGD_HOST | string | | localhost | -| port | FLAGD_PORT | number | | 8013 | -| tls | FLAGD_TLS | boolean | | false | -| socketPath | FLAGD_SOCKET_PATH | string | | | -| certPath | FLAGD_SERVER_CERT_PATH | string | | | -| cache | FLAGD_CACHE | string | lru,disabled | lru (if possible in chosen technology) | -| maxCacheSize | FLAGD_MAX_CACHE_SIZE | int | | 1000 | -| maxEventStreamRetries | FLAGD_MAX_EVENT_STREAM_RETRIES | int | | 0 (0 means unlimited) | -| maxEventStreamRetryInterval | FLAGD_MAX_EVENT_STREAM_RETRY_INTERVAL | int | | 60s | - -## Error handling - -Handle flag evaluation errors by using the error constructors exported by the SDK (e.g. `openfeature.NewProviderNotReadyResolutionError(ConnectionError)`), thereby allowing the SDK to parse and handle the error appropriately. - -## Post creation - -The following steps will extend the reach of the newly created provider to other developers of the chosen technology. - -### Add to flagd's list of providers - -Create a pull request appending the provider to the list [here](../usage/flagd_providers.md). - -### Open an issue to document the provider - -Create an issue in openfeature.dev [here](https://github.com/open-feature/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 deleted file mode 100644 index d0074b324..000000000 --- a/docs/other_resources/high_level_architecture.md +++ /dev/null @@ -1,62 +0,0 @@ -# High level architecture - -## Component overview - -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 -for flag configuration change notifications. - -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 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 -(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 -process gets pushed to event subscribers. - - - -## Readiness & Liveness probes - -### HTTP - -Flagd exposes HTTP liveness and readiness probes. -These probes can be used for K8s deployments. -With default start-up configurations, these probes are exposed on the metrics port (default: 8014) at the following URLs, - -- Liveness: -- Readiness: - -### gRPC - -Flagd exposes a [standard gRPC liveness check](https://github.com/grpc/grpc/blob/master/doc/health-checking.md) on the metrics port (default: 8014). - -### Definition of Liveness - -The liveness probe becomes active and HTTP 200 status is served as soon as Flagd service is up and running. - -### 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. diff --git a/docs/other_resources/snap.md b/docs/other_resources/snap.md deleted file mode 100644 index b1ab3afd0..000000000 --- a/docs/other_resources/snap.md +++ /dev/null @@ -1,26 +0,0 @@ -# Snap - -flagD can be released on the snapstore as a snap package. -The homepage for the snap is found [here](https://snapcraft.io/flagd/) - -## Login - -`snapcraft login` - -## Build - -`snapcraft` - -Run this command from `snap` directory. - -## Release - -```shell -snapcraft upload flagd__amd64.snap --release=candidate -``` - -## Promotion - -```shell -snapcraft promote flagd --from-channel=candidate --to-channel=stable -``` diff --git a/docs/other_resources/systemd_service.md b/docs/other_resources/systemd_service.md deleted file mode 100644 index 7d6e359c2..000000000 --- a/docs/other_resources/systemd_service.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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`. - -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 - Main PID: 64610 (flagd) - Tasks: 7 (limit: 4572) - Memory: 1.4M - CGroup: /system.slice/flagd.service - └─64610 /usr/local/bin/flagd start -f=/etc/flagd/flags.json - -May 30 12:19:55 foo systemd[1]: Started "A generic feature flag daemon". -``` diff --git a/docs/quick-start.md b/docs/quick-start.md new file mode 100644 index 000000000..1ccf0ebb4 --- /dev/null +++ b/docs/quick-start.md @@ -0,0 +1,212 @@ +# Quick Start + +Learn the basics of flagd from the comfort of your terminal. + +## What you'll need + +- Docker +- cURL + +## Let's get started + +### Download the flag definition + +```shell +wget https://raw.githubusercontent.com/open-feature/flagd/main/docs/assets/demo.flagd.json +``` + +The flag definition file includes two feature flags. +The first one has the flag key `show-welcome-banner` and is a boolean type. +These types of feature flags are commonly used to gate access to a new feature using a conditional in code. +The second flag has the key `background-color` and is a multi-variant string. +These are commonly used for A/B/(n) testing and experimentation. + +### Start flagd + +Run the following command to start flagd using docker. This will expose flagd on port `8013` and read from the `demo.flagd.json` file we downloaded in the previous step. + +```shell +docker run \ + --rm -it \ + --name flagd \ + -p 8013:8013 \ + -v $(pwd):/etc/flagd \ + ghcr.io/open-feature/flagd:latest start \ + --uri file:./etc/flagd/demo.flagd.json +``` + +??? "Tips for Windows users" + In Windows, use WSL system for both the file location and Docker runtime. + Mixed file systems does not work and this is a [limitation of Docker](https://github.com/docker/for-win/issues/8479). + +### Evaluating a feature flag + +Test it out by running the following cURL command in a separate terminal: + +```shell +curl -X POST "http://localhost:8013/schema.v1.Service/ResolveBoolean" \ + -d '{"flagKey":"show-welcome-banner","context":{}}' -H "Content-Type: application/json" +``` + +You should see the following result: + +```json +{ + "value": false, + "reason": "STATIC", + "variant": "off", + "metadata": {} +} +``` + +### Enable the welcome banner + +Open the `demo.flagd.json` file in a text editor and change the `defaultVariant` value from `off` to `on`. + +Save and rerun the following cURL command: + +```shell +curl -X POST "http://localhost:8013/schema.v1.Service/ResolveBoolean" \ + -d '{"flagKey":"show-welcome-banner","context":{}}' -H "Content-Type: application/json" +``` + +You should see the updated results: + +```json +{ + "value": true, + "reason": "STATIC", + "variant": "on", + "metadata": {} +} +``` + +!!! note "" + + Notice that flagd picked up the new flag definition without requiring a restart. + +### Multi-variant feature flags + +In some situations, a boolean value may not be enough. +That's where a multi-variant feature flag comes in handy. +In this section, we'll talk about a multi-variant feature flag can be used to control the background color of an application. + +Save and rerun the following cURL command: + +```shell +curl -X POST "http://localhost:8013/schema.v1.Service/ResolveString" \ + -d '{"flagKey":"background-color","context":{}}' -H "Content-Type: application/json" +``` + +You should see the updated results: + +```json +{ + "value": "#FF0000", + "reason": "STATIC", + "variant": "red", + "metadata": {} +} +``` + +### Add a targeting rule + +Imagine that we're testing out a new color scheme internally. +Employees should see the green background color while customers should continue seeing red. +This can be accomplished in flagd using targeting rules. + +Open the `demo.flagd.json` file in a text editor and extend the `background-color` to include a targeting rule. + +``` json hl_lines="19-32" +{ + "flags": { + "show-welcome-banner": { + "state": "ENABLED", + "variants": { + "on": true, + "off": false + }, + "defaultVariant": "off" + }, + "background-color": { + "state": "ENABLED", + "variants": { + "red": "#FF0000", + "blue": "#0000FF", + "green": "#00FF00", + "yellow": "#FFFF00" + }, + "defaultVariant": "red", + "targeting": { + "if": [ + { + "===": [ + { + "var": "company" + }, + "initech" + ] + }, + "green" + ] + } + } + } +} +``` + +The evaluation context contains arbitrary attributes that targeting rules can operate on, and can be included in each feature flag evaluation. +This rule will return the `green` variant if the `company` included in the _evaluation context_ matches `initech`. +If there isn't a match, the `defaultVariant` is returned. + +#### Test as a customer + +Let's confirm that customers are still seeing the `red` variant by running the following command: + +```shell +curl -X POST "http://localhost:8013/schema.v1.Service/ResolveString" \ + -d '{"flagKey":"background-color","context":{"company": "stark industries"}}' -H "Content-Type: application/json" +``` + +You should see the updated results: + +```json +{ + "value": "#FF0000", + "reason": "DEFAULT", + "variant": "red", + "metadata": {} +} +``` + +#### Test as an employee + +Let's confirm that employees of Initech are seeing the updated variant. + +Run the following cURL command in the terminal: + +```shell +curl -X POST "http://localhost:8013/schema.v1.Service/ResolveString" \ + -d '{"flagKey":"background-color","context":{"company": "initech"}}' -H "Content-Type: application/json" +``` + +You should see the updated results: + +```json +{ + "value": "#00FF00", + "reason": "TARGETING_MATCH", + "variant": "green", + "metadata": {} +} +``` + +Notice that the `green` variant is returned and the reason is `TARGETING_MATCH`. + +## Summary + +In this guide, we configured flagd to use a local flag configuration. +We then performed flag evaluation using cURL to see how updating the flag definition affects the output. +We also explored how evaluation context can be used within a targeting rule to personalize the output. +This is just scratching the surface of flagd's capabilities. +Check out the [concepts section](./concepts//feature-flagging.md) to learn about the use cases enabled by flagd. diff --git a/docs/configuration/fractional_evaluation.md b/docs/reference/custom-operations/fractional-operation.md similarity index 89% rename from docs/configuration/fractional_evaluation.md rename to docs/reference/custom-operations/fractional-operation.md index 2080df582..f148a4e3e 100644 --- a/docs/configuration/fractional_evaluation.md +++ b/docs/reference/custom-operations/fractional-operation.md @@ -1,8 +1,28 @@ -# Fractional Evaluation +# Fractional Operation OpenFeature allows clients to pass contextual information which can then be used during a flag evaluation. For example, a client could pass the email address of the user. -In some scenarios, it is desirable to use that contextual information to segment the user population further and thus return dynamic values. +```js +// Factional evaluation property name used in a targeting rule +"fractional": [ + // Evaluation context property used to determine the split + { "var": "email" }, + // Split definitions contain an array with a variant and percentage + // Percentages must add up to 100 + [ + // Must match a variant defined in the flag definition + "red", + // The probability this variant is selected + 50 + ], + [ + // Must match a variant defined in the flag definition + "green", + // The probability this variant is selected + 50 + ] +] +``` See the [headerColor](https://github.com/open-feature/flagd/blob/main/samples/example_flags.flagd.json#L88-#L133) flag. The `defaultVariant` is `red`, but it contains a [targeting rule](reusable_targeting_rules.md), meaning a fractional evaluation occurs for flag evaluation with a `context` object containing `email` and where that `email` value contains `@faas.com`. @@ -13,8 +33,6 @@ Assignment is deterministic (sticky) based on the expression supplied as the fir The value retrieved by this expression is referred to as the "bucketing value". The bucketing value expression can be omitted, in which case a concatenation of the `targetingKey` and the `flagKey` will be used. -## Fractional Evaluation: Technical Description - The `fractional` 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 ([murmur3](https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp)) @@ -23,36 +41,12 @@ 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 `fractional` operation 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 -"fractional": [ - // Evaluation context property used to determine the split - { "var": "email" }, - // Split definitions contain an array with a variant and percentage - // Percentages must add up to 100 - [ - // Must match a variant defined in the flag configuration - "red", - // The probability this configuration is selected - 50 - ], - [ - // Must match a variant defined in the flag configuration - "green", - // The probability this configuration is selected - 50 - ] -] -``` - ## Example Flags defined as such: @@ -119,7 +113,7 @@ Result: 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 `fractional` configuration. -### Migrating from legacy fractionalEvaluation +### Migrating from legacy "fractionalEvaluation" If you are using a legacy fractional evaluation (`fractionalEvaluation`), it's recommended you migrate to `fractional`. The new `fractional` evaluator supports nested properties and JsonLogic expressions. diff --git a/docs/configuration/sem_ver_evaluation.md b/docs/reference/custom-operations/semver-operation.md similarity index 92% rename from docs/configuration/sem_ver_evaluation.md rename to docs/reference/custom-operations/semver-operation.md index 038720d1b..6c821c51b 100644 --- a/docs/configuration/sem_ver_evaluation.md +++ b/docs/reference/custom-operations/semver-operation.md @@ -1,17 +1,11 @@ -# SemVer Evaluation +# Semantic Version Operation OpenFeature allows clients to pass contextual information which can then be used during a flag evaluation. For example, a client could pass the email address of the user. In some scenarios, it is desirable to use that contextual information to segment the user population further and thus return dynamic values. -## SemVer Evaluation: Technical Description - The `sem_ver` evaluation checks if the given property matches a semantic versioning condition. It returns 'true', if the value of the given property meets the condition, 'false' if not. - -## SemVer Evaluation Configuration - -The `sem_ver` evaluation can be added as part of a targeting definition. Note that the 'sem_ver' evaluation rule must contain exactly three items: 1. Target property: this needs which both resolve to a semantic versioning string diff --git a/docs/configuration/string_comparison_evaluation.md b/docs/reference/custom-operations/string-comparison-operation.md similarity index 92% rename from docs/configuration/string_comparison_evaluation.md rename to docs/reference/custom-operations/string-comparison-operation.md index a02abc4d0..6e6eb9fdc 100644 --- a/docs/configuration/string_comparison_evaluation.md +++ b/docs/reference/custom-operations/string-comparison-operation.md @@ -1,17 +1,11 @@ -# StartsWith/EndsWith Evaluation +# Starts-With / Ends-With Operation OpenFeature allows clients to pass contextual information which can then be used during a flag evaluation. For example, a client could pass the email address of the user. In some scenarios, it is desirable to use that contextual information to segment the user population further and thus return dynamic values. -## StartsWith/EndsWith Evaluation: Technical Description - The `starts_with`/`ends_with` operation is a custom JsonLogic operation which selects a variant based on whether the specified property starts/ends with a certain value. - -## StartsWith Evaluation Configuration - -The `starts_with` evaluation can be added as part of a targeting definition. The value is an array consisting of exactly two items, which both need to resolve to a string value. The first entry of the array represents the property to be considered, while the second entry represents the target value, i.e. the prefix that needs to be present in the value of the referenced property. @@ -28,7 +22,7 @@ The `starts_with` evaluation returns a boolean, indicating whether the condition ] ``` -## Example for 'starts_with' Evaluation +## Example for 'starts_with' Operation Flags defined as such: @@ -82,7 +76,7 @@ Result: {"value":"#0000FF","reason":"TARGETING_MATCH","variant":"green"} ``` -## EndsWith Evaluation Configuration +## EndsWith Operation Configuration The `ends_with` evaluation can be added as part of a targeting definition. The value is an array consisting of exactly two items, which both need to resolve to a string value. @@ -101,7 +95,7 @@ The `ends_with` evaluation returns a boolean, indicating whether the condition h ] ``` -## Example for 'ends_with' Evaluation +## Example for 'ends_with' Operation Flags defined as such: diff --git a/docs/configuration/flag_configuration.md b/docs/reference/flag-definitions.md similarity index 54% rename from docs/configuration/flag_configuration.md rename to docs/reference/flag-definitions.md index c30867bea..550c311fc 100644 --- a/docs/configuration/flag_configuration.md +++ b/docs/reference/flag-definitions.md @@ -1,19 +1,4 @@ -# Flag Configuration - -- [Flag Configuration](#flag-configuration) - - [Flags](#flags) - - [Flag configuration](#flag-configuration-1) - - [Flag properties](#flag-properties) - - [State](#state) - - [Variants](#variants) - - [Default Variant](#default-variant) - - [Targeting Rules](#targeting-rules) - - [Evaluation Context](#evaluation-context) - - [Conditions](#conditions) - - [Operators](#operators) - - [Functions](#functions) - - [Shared evaluators](#shared-evaluators) - - [Examples](#examples) +# Flag Definitions ## Flags @@ -28,7 +13,7 @@ The flags property is a top level property that contains a collection of individ } ``` -## Flag configuration +## Flag Definition `flag key` is a **required** property. The flag key **must** uniquely identify a flag so that it can be used during flag evaluation. @@ -180,9 +165,9 @@ The evaluation context can be accessed in targeting rules using the `var` operat | Description | Example | | -------------------------------------------------------------- | --------------------------------------------- | -| Retrieve property from the evaluation context | `{ "var": "email" }` | -| Retrieve property from the evaluation context or use a default | `{ "var": ["email", "noreply@example.com"] }` | -| Retrieve a nested property from the evaluation context | `{ "var": "user.email" }` | +| Retrieve property from the evaluation context | `#!json { "var": "email" }` | +| Retrieve property from the evaluation context or use a default | `#!json { "var": ["email", "noreply@example.com"] }` | +| Retrieve a nested property from the evaluation context | `#!json { "var": "user.email" }` | > For more information, see the `var` section in the [JSON Logic documentation](https://jsonlogic.com/operations.html#var). @@ -192,45 +177,46 @@ Conditions can be used to control the logical flow and grouping of targeting rul | Conditional | Example | | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| If | Logic: `{"if" : [ true, "yes", "no" ]}`
Result: `"yes"`

Logic: `{"if" : [ false, "yes", "no" ]}`
Result: `"no"` | -| If else | Logic: `{"if" : [ false, "yes", false, "no", "maybe" ]}`
Result: `"maybe"`

Logic: `{"if" : [ false, "yes", false, "no", false, "maybe", "who knows" ]}`
Result: `"who knows"` | -| Or | Logic: `{"or" : [ true, false ]}`
Result: `true`

Logic: `{"or" : [ false, false ]}`
Result: `false` | -| And | Logic: `{"and" : [ true, false ]}`
Result: `false`

Logic: `{"and" : [ true, true ]}`
Result: `true` | +| If | Logic: `#!json {"if" : [ true, "yes", "no" ]}`
Result: `"yes"`

Logic: `#!json {"if" : [ false, "yes", "no" ]}`
Result: `"no"` | +| If else | Logic: `#!json {"if" : [ false, "yes", false, "no", "maybe" ]}`
Result: `"maybe"`

Logic: `#!json {"if" : [ false, "yes", false, "no", false, "maybe", "who knows" ]}`
Result: `"who knows"` | +| Or | Logic: `#!json {"or" : [ true, false ]}`
Result: `true`

Logic: `#!json {"or" : [ false, false ]}`
Result: `false` | +| And | Logic: `#!json {"and" : [ true, false ]}`
Result: `false`

Logic: `#!json {"and" : [ true, true ]}`
Result: `true` | -#### Operators +#### Operations -Operators are used to take action on, or compare properties retrieved from the context. +Operations are used to take action on, or compare properties retrieved from the context. +These are provided out-of-the-box by JsonLogic. | Operator | Description | Context type | Example | | ---------------------- | -------------------------------------------------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Equals | Attribute equals the specified value, with type coercion. | any | Logic: `{ "==" : [1, 1] }`
Result: `true`

Logic: `{ "==" : [1, "1"] }`
Result: `true` | -| Strict equals | Attribute equals the specified value, with strict comparison. | any | Logic: `{ "===" : [1, 1] }`
Result: `true`

Logic: `{ "===" : [1, "1"] }`
Result: `false` | -| Not equals | Attribute doesn't equal the specified value, with type coercion. | any | Logic: `{ "!=" : [1, 2] }`
Result: `true`

Logic: `{ "!=" : [1, "1"] }`
Result: `false` | -| Strict not equal | Attribute doesn't equal the specified value, with strict comparison. | any | Logic: `{ "!==" : [1, 2] }`
Result: `true`

Logic: `{ "!==" : [1, "1"] }`
Result: `true` | -| Exists | Attribute is defined | any | Logic: `{ "!!": [ "mike" ] }`
Result: `true`

Logic: `{ "!!": [ "" ] }`
Result: `false` | -| Not exists | Attribute is not defined | any | Logic: `{"!": [ "mike" ] }`
Result: `false`

Logic: `{"!": [ "" ] }`
Result: `true` | -| Greater than | Attribute is greater than the specified value | number | Logic: `{ ">" : [2, 1] }`
Result: `true`

Logic: `{ ">" : [1, 2] }`
Result: `false` | -| Greater than or equals | Attribute is greater or equal to the specified value | number | Logic: `{ ">=" : [2, 1] }`
Result: `true`

Logic: `{ ">=" : [1, 1] }`
Result: `true` | -| Less than | Attribute is less than the specified value | number | Logic: `{ "<" : [1, 2] }`
Result: `true`

Logic: `{ "<" : [2, 1] }`
Result: `false` | -| Less than or equals | Attribute is less or equal to the specified value | number | Logic: `{ "<=" : [1, 1] }`
Result: `true`

Logic: `{ "<=" : [2, 1] }`
Result: `false` | -| Between | Attribute between the specified values | number | Logic: `{ "<" : [1, 5, 10]}`
Result: `true`

Logic: `{ "<" : [1, 11, 10] }`
Result: `false` | -| Between inclusive | Attribute between or equal to the specified values | number | Logic: `{"<=" : [1, 1, 10] }`
Result: `true`

Logic: `{"<=" : [1, 11, 10] }`
Result: `false` | -| Contains | Contains string | string | Logic: `{ "in": ["Spring", "Springfield"] }`
Result: `true`

Logic: `{ "in":["Illinois", "Springfield"] }`
Result: `false` | -| Not contains | Does not contain a string | string | Logic: `{ "!": { "in":["Spring", "Springfield"] } }`
Result: `false`

Logic: `{ "!": { "in":["Illinois", "Springfield"] } }`
Result: `true` | -| In | Attribute is in an array of strings | string | Logic: `{ "in" : [ "Mike", ["Bob", "Mike"]] }`
Result: `true`

Logic: `{ "in":["Todd", ["Bob", "Mike"]] }`
Result: `false` | -| Not it | Attribute is not in an array of strings | string | Logic: `{ "!": { "in" : [ "Mike", ["Bob", "Mike"]] } }`
Result: `false`

Logic: `{ "!": { "in":["Todd", ["Bob", "Mike"]] } }`
Result: `true` | - -#### Functions - -Functions can be used to create more advanced targeting rules. -They are purpose built extensions to JSON logic in order to support popular feature flag use cases. - -| Function | Description | Example | -| -------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `fractionalEvaluation` | Deterministic percentage-based rollout | Logic: `{ "fractionalEvaluation" : [ "email", [ "red" , 50], [ "green" , 50 ] ] }`
Result: Pseudo randomly `red` or `green` based on the evaluation context property `email`.

Additional documentation can be found [here](./fractional_evaluation.md) | -| `starts_with` | Attribute starts with the specified value | Logic: `{ "starts_with" : [ "192.168.0.1", "192.168"] }`
Result: `true`

Logic: `{ "starts_with" : [ "10.0.0.1", "192.168"] }`
Result: `false` | -| `ends_with` | Attribute ends with the specified value | Logic: `{ "ends_with" : [ "noreply@example.com", "@example.com"] }`
Result: `true`

Logic: `{ ends_with" : [ "noreply@example.com", "@test.com"] }`
Result: `false` | -| `sem_ver` | Attribute matches a semantic versioning condition | Logic: `{"sem_ver": ["1.1.2", ">=", "1.0.0"]}`
Result: `true`

Additional documentation can be found [here](./sem_ver_evaluation.md) | +| Equals | Attribute equals the specified value, with type coercion. | any | Logic: `#!json { "==" : [1, 1] }`
Result: `true`

Logic: `#!json { "==" : [1, "1"] }`
Result: `true` | +| Strict equals | Attribute equals the specified value, with strict comparison. | any | Logic: `#!json { "===" : [1, 1] }`
Result: `true`

Logic: `#!json { "===" : [1, "1"] }`
Result: `false` | +| Not equals | Attribute doesn't equal the specified value, with type coercion. | any | Logic: `#!json { "!=" : [1, 2] }`
Result: `true`

Logic: `#!json { "!=" : [1, "1"] }`
Result: `false` | +| Strict not equal | Attribute doesn't equal the specified value, with strict comparison. | any | Logic: `#!json { "!==" : [1, 2] }`
Result: `true`

Logic: `#!json { "!==" : [1, "1"] }`
Result: `true` | +| Exists | Attribute is defined | any | Logic: `#!json { "!!": [ "mike" ] }`
Result: `true`

Logic: `#!json { "!!": [ "" ] }`
Result: `false` | +| Not exists | Attribute is not defined | any | Logic: `#!json {"!": [ "mike" ] }`
Result: `false`

Logic: `#!json {"!": [ "" ] }`
Result: `true` | +| Greater than | Attribute is greater than the specified value | number | Logic: `#!json { ">" : [2, 1] }`
Result: `true`

Logic: `#!json { ">" : [1, 2] }`
Result: `false` | +| Greater than or equals | Attribute is greater or equal to the specified value | number | Logic: `#!json { ">=" : [2, 1] }`
Result: `true`

Logic: `#!json { ">=" : [1, 1] }`
Result: `true` | +| Less than | Attribute is less than the specified value | number | Logic: `#!json { "<" : [1, 2] }`
Result: `true`

Logic: `#!json { "<" : [2, 1] }`
Result: `false` | +| Less than or equals | Attribute is less or equal to the specified value | number | Logic: `#!json { "<=" : [1, 1] }`
Result: `true`

Logic: `#!json { "<=" : [2, 1] }`
Result: `false` | +| Between | Attribute between the specified values | number | Logic: `#!json { "<" : [1, 5, 10]}`
Result: `true`

Logic: `#!json { "<" : [1, 11, 10] }`
Result: `false` | +| Between inclusive | Attribute between or equal to the specified values | number | Logic: `#!json {"<=" : [1, 1, 10] }`
Result: `true`

Logic: `#!json {"<=" : [1, 11, 10] }`
Result: `false` | +| Contains | Contains string | string | Logic: `#!json { "in": ["Spring", "Springfield"] }`
Result: `true`

Logic: `#!json { "in":["Illinois", "Springfield"] }`
Result: `false` | +| Not contains | Does not contain a string | string | Logic: `#!json { "!": { "in":["Spring", "Springfield"] } }`
Result: `false`

Logic: `#!json { "!": { "in":["Illinois", "Springfield"] } }`
Result: `true` | +| In | Attribute is in an array of strings | string | Logic: `#!json { "in" : [ "Mike", ["Bob", "Mike"]] }`
Result: `true`

Logic: `#!json { "in":["Todd", ["Bob", "Mike"]] }`
Result: `false` | +| Not it | Attribute is not in an array of strings | string | Logic: `#!json { "!": { "in" : [ "Mike", ["Bob", "Mike"]] } }`
Result: `false`

Logic: `#!json { "!": { "in":["Todd", ["Bob", "Mike"]] } }`
Result: `true` | + +#### Custom Operations + +These are custom operations specific to flagd and flagd providers. +They are purpose built extensions to JsonLogic in order to support common feature flag use cases. + +| Function | Description | Example | +| ------------- | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `fractional` | Deterministic, pseudorandom fractional distribution | Logic: `#!json { "fractional" : [ { "var": "email" }, [ "red" , 50], [ "green" , 50 ] ] }`
Result: Pseudo randomly `red` or `green` based on the evaluation context property `email`.

Additional documentation can be found [here](./custom-operations/fractional.md) | +| `starts_with` | Attribute starts with the specified value | Logic: `#!json { "starts_with" : [ "192.168.0.1", "192.168"] }`
Result: `true`

Logic: `#!json { "starts_with" : [ "10.0.0.1", "192.168"] }`
Result: `false`
Additional documentation can be found [here](./custom-operations/string-comparision.md) | +| `ends_with` | Attribute ends with the specified value | Logic: `#!json { "ends_with" : [ "noreply@example.com", "@example.com"] }`
Result: `true`

Logic: `#!json { ends_with" : [ "noreply@example.com", "@test.com"] }`
Result: `false`
Additional documentation can be found [here](./custom-operations/string-comparision.md) | +| `sem_ver` | Attribute matches a semantic versioning condition | Logic: `#!json {"sem_ver": ["1.1.2", ">=", "1.0.0"]}`
Result: `true`

Additional documentation can be found [here](./custom-operations/semver-comparison.md) | ## Shared evaluators @@ -276,8 +262,8 @@ Example: "$ref": "emailWithFaas" }, { - "fractionalEvaluation": [ - "email", + "fractional": [ + { "var": "email" }, ["red", 25], ["blue", 25], ["green", 25], diff --git a/docs/reference/flagd-cli/flagd.md b/docs/reference/flagd-cli/flagd.md new file mode 100644 index 000000000..305d21f2d --- /dev/null +++ b/docs/reference/flagd-cli/flagd.md @@ -0,0 +1,18 @@ + +## 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. + +### Options + +``` + --config string config file (default is $HOME/.agent.yaml) + -x, --debug verbose logging + -h, --help help for flagd +``` + +### SEE ALSO + +* [flagd start](flagd_start) - Start flagd +* [flagd version](flagd_version) - Print the version number of flagd + diff --git a/docs/configuration/flagd.md b/docs/reference/flagd-cli/flagd/flagd.md similarity index 100% rename from docs/configuration/flagd.md rename to docs/reference/flagd-cli/flagd/flagd.md diff --git a/docs/configuration/flagd_start.md b/docs/reference/flagd-cli/flagd/flagd_start.md similarity index 100% rename from docs/configuration/flagd_start.md rename to docs/reference/flagd-cli/flagd/flagd_start.md diff --git a/docs/configuration/flagd_version.md b/docs/reference/flagd-cli/flagd/flagd_version.md similarity index 100% rename from docs/configuration/flagd_version.md rename to docs/reference/flagd-cli/flagd/flagd_version.md diff --git a/docs/reference/flagd-cli/flagd_start.md b/docs/reference/flagd-cli/flagd_start.md new file mode 100644 index 000000000..a633b66ad --- /dev/null +++ b/docs/reference/flagd-cli/flagd_start.md @@ -0,0 +1,41 @@ + +## flagd start + +Start flagd + +``` +flagd start [flags] +``` + +### Options + +``` + -b, --bearer-token string DEPRECATED: Superseded by --sources. + -C, --cors-origin strings CORS allowed origins, * will allow all origins + -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") + -h, --help help for start + -z, --log-format string Set the logging format, e.g. console or json (default "console") + -t, --metrics-exporter string Set the metrics exporter. Default(if unset) is Prometheus. Can be override to otel - OpenTelemetry metric exporter. Overriding to otel require otelCollectorURI to be present + -m, --metrics-port int32 Port to serve metrics on (default 8014) + -o, --otel-collector-uri string Set the grpc URI of the OpenTelemetry collector for flagd runtime. If unset, the collector setup will be ignored and traces will not be exported. + -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. + -s, --sources string JSON representation of an array of SourceConfig objects. This object contains 2 required fields, uri (string) and provider (string). Documentation for this object: https://github.com/open-feature/flagd/blob/main/docs/configuration/configuration.md#sync-provider-customisation + -y, --sync-provider string DEPRECATED: Set a sync provider e.g. filepath or remote + -a, --sync-provider-args stringToString DEPRECATED: Sync provider arguments as key values separated by = (default []) + -f, --uri .yaml/.yml/.json Set a sync provider uri to read data from, this can be a filepath,url (http and grpc) or FeatureFlagConfiguration. When flag keys are duplicated across multiple providers the merge priority follows the index of the flag arguments, as such flags from the uri at index 0 take the lowest precedence, with duplicated keys being overwritten by those from the uri at index 1. Please note that if you are using filepath, flagd only supports files with .yaml/.yml/.json extension. +``` + +### Options inherited from parent commands + +``` + --config string config file (default is $HOME/.agent.yaml) + -x, --debug verbose logging +``` + +### SEE ALSO + +* [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/reference/flagd-cli/flagd_version.md b/docs/reference/flagd-cli/flagd_version.md new file mode 100644 index 000000000..e8b23087d --- /dev/null +++ b/docs/reference/flagd-cli/flagd_version.md @@ -0,0 +1,26 @@ + +## flagd version + +Print the version number of flagd + +``` +flagd version [flags] +``` + +### Options + +``` + -h, --help help for version +``` + +### Options inherited from parent commands + +``` + --config string config file (default is $HOME/.agent.yaml) + -x, --debug verbose logging +``` + +### SEE ALSO + +* [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/web-docs/concepts/opentelemetry.md b/docs/reference/monitoring.md similarity index 70% rename from web-docs/concepts/opentelemetry.md rename to docs/reference/monitoring.md index 0c3d6c8b7..71304bd41 100644 --- a/web-docs/concepts/opentelemetry.md +++ b/docs/reference/monitoring.md @@ -1,4 +1,34 @@ -# OpenTelemetry +# Monitoring + +## Readiness & Liveness probes + +### HTTP + +Flagd exposes HTTP liveness and readiness probes. +These probes can be used for K8s deployments. +With default start-up configurations, these probes are exposed on the metrics port (default: 8014) at the following URLs, + +- Liveness: +- Readiness: + +### gRPC + +Flagd exposes a [standard gRPC liveness check](https://github.com/grpc/grpc/blob/master/doc/health-checking.md) on the metrics port (default: 8014). + +### Definition of Liveness + +The liveness probe becomes active and HTTP 200 status is served as soon as Flagd service is up and running. + +### 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. + +## OpenTelemetry flagd provides telemetry data out of the box. This telemetry data is compatible with OpenTelemetry. @@ -13,19 +43,19 @@ Given below is the current implementation overview of flagd telemetry internals, flagd expose following metrics, -* `http.server.duration` -* `http.server.response.size` -* `http.server.active_requests` -* `feature_flag.flagd.impression` -* `feature_flag.flagd.evaluation.reason` +- `http.server.duration` +- `http.server.response.size` +- `http.server.active_requests` +- `feature_flag.flagd.impression` +- `feature_flag.flagd.evaluation.reason` ## Traces flagd expose following traces, -* `flagEvaluationService(resolveX)` - SpanKind server - * `jsonEvaluator(resolveX)` - SpanKind internal -* `jsonEvaluator(setState)` - SpanKind internal +- `flagEvaluationService(resolveX)` - SpanKind server + - `jsonEvaluator(resolveX)` - SpanKind internal +- `jsonEvaluator(setState)` - SpanKind internal ## Export to OTEL collector diff --git a/docs/reference/naming.md b/docs/reference/naming.md new file mode 100644 index 000000000..9a42a8147 --- /dev/null +++ b/docs/reference/naming.md @@ -0,0 +1,6 @@ +# Naming + +_flagd_ was conceived as a simple program with a POSIX-style CLI that's designed to run as a service or [daemon](https://en.wikipedia.org/wiki/Daemon_(computing)). +For this reason, its name is stylized as all lower case: "flagd", consistent with other famous Unix/Linux daemons (_crond_, _sshd_, _systemd_, _ntpd_, _httpd_, etc). +Although the flagd system has expanded beyond the flagd application itself to include libraries which embed flagd's evaluation engine, the "flagd" stylization should be observed for all components relating to flagd. +Where possible, please treat "flagd" as a single word, including in library names, packages, variable names, etc. diff --git a/web-docs/k8s/crds/featureflagconfiguration.md b/docs/reference/openfeature-operator/crds/featureflagconfiguration.md similarity index 100% rename from web-docs/k8s/crds/featureflagconfiguration.md rename to docs/reference/openfeature-operator/crds/featureflagconfiguration.md diff --git a/web-docs/k8s/crds/flagsourceconfiguration.md b/docs/reference/openfeature-operator/crds/flagsourceconfiguration.md similarity index 100% rename from web-docs/k8s/crds/flagsourceconfiguration.md rename to docs/reference/openfeature-operator/crds/flagsourceconfiguration.md diff --git a/web-docs/k8s/index.md b/docs/reference/openfeature-operator/installation.md similarity index 93% rename from web-docs/k8s/index.md rename to docs/reference/openfeature-operator/installation.md index b12b79188..e7ecad4bf 100644 --- a/web-docs/k8s/index.md +++ b/docs/reference/openfeature-operator/installation.md @@ -1,4 +1,4 @@ -# Install the OpenFeature Operator +# Installing the OpenFeature Operator Use the [OpenFeature Operator](https://github.com/open-feature/open-feature-operator) to install and run flagd on a Kubernetes cluster. @@ -27,7 +27,7 @@ Create a namespace to house your flags: kubectl create namespace flags ``` -Next define your feature flag(s) using the `FeatureFlagConfiguration` CRD. +Next define your feature flag(s) using the [FeatureFlagConfiguration](./crds/featureflagconfiguration.md) custom resource definition (CRD). This example specifies one flag called `foo` which has two variants `bar` and `baz`. The `defaultVariant` is `bar`. @@ -54,7 +54,7 @@ EOF Next, tell the OpenFeature operator where to find flags. -Do so by creating a `FlagSourceConfiguration` CRD. +Do so by creating a [FlagSourceConfiguration](./crds/flagsourceconfiguration.md) CRD. This example specifies that the CRD called `sample-flags` (created above) can be found in the `flags` namespace and that the provider is `kubernetes`. diff --git a/docs/reference/providers.md b/docs/reference/providers.md new file mode 100644 index 000000000..639816904 --- /dev/null +++ b/docs/reference/providers.md @@ -0,0 +1,11 @@ +# Providers + +flagd was built from the ground up to be [Openfeature-compliant](../concepts/feature-flagging.md#openfeature-compliance). +To use it in your application, you must use the [OpenFeature SDK](https://openfeature.dev/docs/reference/technologies/) for your language, along with the associated OpenFeature _provider_. +For more information about Openfeature providers, see the [OpenFeature documentation](https://openfeature.dev/docs/reference/concepts/provider). + +Providers for flagd come in two flavors: those that are built to communicate with a flagd instance (over HTTP or gRPC) and those that embed flagd's evaluation engine directly (note that some providers are capable of operating in either mode). For more information on how to deploy and use flagd, see [architecture](../architecture.md) and [deployment](../deployment.md). + +For a catalog of available flagd providers, check out the [OpenFeature ecosystem](https://openfeature.dev/ecosystem?instant_search%5Bquery%5D=flagd&instant_search%5BrefinementList%5D%5Btype%5D%5B0%5D=Provider) page. + +For information on implementing a flagd provider, see the specifications for [RPC](./specifications/rpc-providers.md) and [in-process](./specifications/in-process-providers.md) providers. diff --git a/docs/other_resources/in-process-providers/evaluators/fractional_evaluation.md b/docs/reference/specifications/custom-operations/fractional-operation-spec.md similarity index 95% rename from docs/other_resources/in-process-providers/evaluators/fractional_evaluation.md rename to docs/reference/specifications/custom-operations/fractional-operation-spec.md index 94ab008b1..619040818 100644 --- a/docs/other_resources/in-process-providers/evaluators/fractional_evaluation.md +++ b/docs/reference/specifications/custom-operations/fractional-operation-spec.md @@ -1,8 +1,10 @@ -# Fractional Evaluation +# Fractional Operation Specification This evaluator allows to split the returned variants of a feature flag into different buckets, where each bucket can be assigned a percentage, representing how many requests will resolve to the corresponding -variant. The sum of all weights must be 100, and the distribution must be performed by using the value of a referenced +variant. + +The sum of all weights must be `100`, and the distribution must be performed by using the value of a referenced from the evaluation context to hash that value and map it to a value between [0, 100]. It is important to note that evaluations MUST be sticky, meaning that flag resolution requests containing the same value for the referenced property in their context MUST always resolve to the same variant. For calculating the hash value of the @@ -16,7 +18,7 @@ specifying the bucketing property to base the distribution of values on. If not The remaining items are `arrays`, each with two values, with the first being `string` item representing the name of the variant, and the second being a `float` item representing the percentage for that variant. The percentages of all items must add up to 100.0, otherwise unexpected behavior can occur during the evaluation. The `data` object can be an arbitrary -JSON object. Below is an example for a targetingRule containing a `fractionalEvaluation`: +JSON object. Below is an example of a targeting rule containing a `fractional`: ```json { @@ -30,7 +32,7 @@ JSON object. Below is an example for a targetingRule containing a `fractionalEva "defaultVariant": "red", "state": "ENABLED", "targeting": { - "fractionalEvaluation": [ + "fractional": [ {"var":"email"}, [ "red", @@ -125,7 +127,7 @@ func FractionalEvaluation(values, data interface{}) interface{} { return nil } - // 3. Parse the fractionalEvaluation values distribution + // 3. Parse the fractional values distribution sumOfPercentages := 0 var feDistributions []fractionalEvaluationDistribution diff --git a/docs/other_resources/in-process-providers/evaluators/semver_evaluation.md b/docs/reference/specifications/custom-operations/semver-operation-spec.md similarity index 97% rename from docs/other_resources/in-process-providers/evaluators/semver_evaluation.md rename to docs/reference/specifications/custom-operations/semver-operation-spec.md index aa86b947c..56c51f7af 100644 --- a/docs/other_resources/in-process-providers/evaluators/semver_evaluation.md +++ b/docs/reference/specifications/custom-operations/semver-operation-spec.md @@ -1,19 +1,8 @@ -# Semantic Versioning Evaluation +# Semantic Versioning Operation Specification This evaluator checks if the given property within the evaluation context matches a semantic versioning condition. It returns 'true', if the value of the given property meets the condition, 'false' if not. -The implementation of this evaluator should accept the object containing the `sem_ver` evaluator -configuration, and a `data` object containing the evaluation context. -The 'sem_ver' evaluation rule contains exactly three items: - -1. Target property value: the resolved value of the target property referenced in the targeting rule -2. Operator: One of the following: `=`, `!=`, `>`, `<`, `>=`, `<=`, `~` (match minor version), `^` (match major version) -3. Target value: this needs to resolve to a semantic versioning string. If this condition is not met, the evaluator should -log an appropriate error message and return `nil` - -The `sem_ver` evaluation returns a boolean, indicating whether the condition has been met. - ```js { "if": [ @@ -25,6 +14,17 @@ The `sem_ver` evaluation returns a boolean, indicating whether the condition has } ``` +The implementation of this evaluator should accept the object containing the `sem_ver` evaluator +configuration, and a `data` object containing the evaluation context. +The 'sem_ver' evaluation rule contains exactly three items: + +1. Target property value: the resolved value of the target property referenced in the targeting rule +2. Operator: One of the following: `=`, `!=`, `>`, `<`, `>=`, `<=`, `~` (match minor version), `^` (match major version) +3. Target value: this needs to resolve to a semantic versioning string. If this condition is not met, the evaluator should +log an appropriate error message and return `nil` + +The `sem_ver` evaluation returns a boolean, indicating whether the condition has been met. + Please note that the implementation of this evaluator can assume that instead of `{"var": "version"}`, it will receive the resolved value of that referenced property, as resolving the value will be taken care of by JsonLogic before applying the evaluator. diff --git a/docs/other_resources/in-process-providers/evaluators/string_comparison_evaluation.md b/docs/reference/specifications/custom-operations/string-comparison-operation-spec.md similarity index 96% rename from docs/other_resources/in-process-providers/evaluators/string_comparison_evaluation.md rename to docs/reference/specifications/custom-operations/string-comparison-operation-spec.md index bfa5b9fc0..3e9832563 100644 --- a/docs/other_resources/in-process-providers/evaluators/string_comparison_evaluation.md +++ b/docs/reference/specifications/custom-operations/string-comparison-operation-spec.md @@ -1,17 +1,8 @@ -# StartsWith/EndsWith evaluation +# Starts-With / Ends-With Operation Specification This evaluator selects a variant based on whether the specified property within the evaluation context starts/ends with a certain string. -The implementation of this evaluator should accept the object containing the `starts_with` or `ends_with` evaluator -configuration, and a `data` object containing the evaluation context. -The `starts_with`/`ends_with` evaluation rule contains exactly two items: - -1. The resolved string value from the evaluation context -2. The target string value - -The `starts_with`/`ends_with` evaluation returns a boolean, indicating whether the condition has been met. - ```js // starts_with property name used in a targeting rule "starts_with": [ @@ -22,6 +13,15 @@ The `starts_with`/`ends_with` evaluation returns a boolean, indicating whether t ] ``` +The implementation of this evaluator should accept the object containing the `starts_with` or `ends_with` evaluator +configuration, and a `data` object containing the evaluation context. +The `starts_with`/`ends_with` evaluation rule contains exactly two items: + +1. The resolved string value from the evaluation context +2. The target string value + +The `starts_with`/`ends_with` evaluation returns a boolean, indicating whether the condition has been met. + Please note that the implementation of this evaluator can assume that instead of `{"var": "email"}`, it will receive the resolved value of that referenced property, as resolving the value will be taken care of by JsonLogic before applying the evaluator. diff --git a/docs/other_resources/in-process-providers/specification.md b/docs/reference/specifications/in-process-providers.md similarity index 91% rename from docs/other_resources/in-process-providers/specification.md rename to docs/reference/specifications/in-process-providers.md index f9f62a0b7..020804617 100755 --- a/docs/other_resources/in-process-providers/specification.md +++ b/docs/reference/specifications/in-process-providers.md @@ -15,24 +15,24 @@ Prerequisites: - Understanding of [general provider concepts](https://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)) -The Flag Configuration containing the feature flags and JsonLogic based targeting rules shall be retrieved by the +The Flag definition containing the feature flags and JsonLogic based targeting rules shall be retrieved by the in-process flagd provider via a gRPC client connection to a sync server, such as [flagd-proxy](https://github.com/open-feature/flagd/tree/main/flagd-proxy). ## Sync source An implementation of an in-process flagd-provider must accept the following environment variables which determine the sync source: -- `FLAGD_SOURCE_URI`: The URI identifying the sync source. Depending on the sync provider type, this can be the URI of a gRPC service providing the `sync` API required by the in-process flagd provider, or the name of a [core.openfeature.dev/v1alpha2.FeatureFlagConfiguration](https://github.com/open-feature/open-feature-operator/blob/main/docs/crds.md#featureflagconfiguration-1) Custom Resource containing the flag configuration. +- `FLAGD_SOURCE_URI`: The URI identifying the sync source. Depending on the sync provider type, this can be the URI of a gRPC service providing the `sync` API required by the in-process flagd provider, or the name of a [core.openfeature.dev/v1alpha2.FeatureFlagConfiguration](https://github.com/open-feature/open-feature-operator/blob/main/docs/crds.md#featureflagconfiguration-1) Custom Resource containing the flag definition. - `FLAGD_SOURCE_PROVIDER_TYPE`: The type of the provider. E.g. `grpc` or `kubernetes`. -- `FLAGD_SOURCE_SELECTOR`: Optional selector for the feature flag configuration of interest. This is used as a `selector` for the flagd-proxie's sync API to identify a flag configuration within a collection of feature flag configurations. +- `FLAGD_SOURCE_SELECTOR`: Optional selector for the feature flag definition of interest. This is used as a `selector` for the flagd-proxie's sync API to identify a flag definition within a collection of feature flag definition. -An implementation of an in-process flagd provider should provide a source for retrieving the flag configuration, namely a gRPC source. +An implementation of an in-process flagd provider should provide a source for retrieving the flag definition, namely a gRPC source. Other sources may be desired eventually, so separation of concerns should be maintained between the abstractions evaluating flags and those retrieving confirmation. ## gRPC sources gRPC sync sources are identified by the `provider` field set to `grpc`. -When such a sync source is specified, the in-process flagd provider should connect to the gRPC service located at the `uri` of the sync source, and use its `sync` API ([see here](https://github.com/open-feature/schemas)) to retrieve the feature flag configurations. +When such a sync source is specified, the in-process flagd provider should connect to the gRPC service located at the `uri` of the sync source, and use its [sync API](./protos.md#syncv1sync_serviceproto) to retrieve the feature flag definition. If the `selector` field of the sync source is set, that selector should be passed through to the `Sync` and `FetchAllFlags` requests sent to the gRPC server. ### Protobuf @@ -74,7 +74,7 @@ If available, the JsonLogic library for the chosen technology should be used. In addition to the built-in evaluators provided by JsonLogic, the following custom targeting rules should be implemented by the provider: -- [Fractional evaluation](../../configuration/fractional_evaluation.md): +- [Fractional operation](../../reference/custom-operations/fractional-operation.md): This evaluator allows to split the returned variants of a feature flag into different buckets, where each bucket can be assigned a percentage, representing how many requests will resolve to the corresponding variant. The sum of all weights must be 100, and the distribution must be performed by using the value of a referenced @@ -85,15 +85,15 @@ For calculating the hash value of the referenced evaluation context property, the [MurmurHash3](https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp) hash function should be used. This is to ensure that flag resolution requests yield the same result, regardless of which implementation of the in-process flagd provider is being used. For more specific implementation guidelines, please refer to -[this document](./evaluators/fractional_evaluation.md). -- [SemVer evaluation](../../configuration/sem_ver_evaluation.md): +[this document](./custom-operations/fractional-operation-spec.md). +- [Semantic version evaluation](../../reference/custom-operations/semver-operation.md): This evaluator checks if the given property within the evaluation context matches a semantic versioning condition. It returns 'true', if the value of the given property meets the condition, 'false' if not. -For more specific implementation guidelines, please refer to [this document](./evaluators/semver_evaluation.md). -- [StartsWith/EndsWith evaluation](../../configuration/string_comparison_evaluation.md): +For more specific implementation guidelines, please refer to [this document](../specifications/custom-operations/semver-operation-spec.md). +- [StartsWith/EndsWith evaluation](../../reference/custom-operations/string-comparison-operation.md): This evaluator selects a variant based on whether the specified property within the evaluation context starts/ends with a certain string. -For more specific implementation guidelines, please refer to [this document](./evaluators/string_comparison_evaluation.md). +For more specific implementation guidelines, please refer to [this document](./custom-operations/string-comparison-operation-spec.md). ## Provider construction @@ -175,11 +175,11 @@ With the release of the v0.6.0 spec, OpenFeature now outlines a lifecycle for in In-process flagd providers should do the following to make use of OpenFeature v0.6.0 features: - start in a `NOT_READY` state -- fetch the flag configurations specified in the sync provider sources and set `state` to `READY` or `ERROR` in the `initialization` function +- fetch the flag definition specified in the sync provider sources and set `state` to `READY` or `ERROR` in the `initialization` function - note that the SDK will automatically emit `PROVIDER_READY`/`PROVIDER_ERROR` according to the termination of the `initialization` function - throw an exception or terminate abnormally if a connection cannot be established during `initialization` - For gRPC based sources (i.e. flagd-proxy), attempt to restore the streaming connection to flagd-proxy (if the connection cannot be established or is broken): - - If flag configurations have been retrieved previously, go into `STALE` state to indicate that flag resolution responsees are based on potentially outdated Flag Configurations. + - If flag definition have been retrieved previously, go into `STALE` state to indicate that flag resolution responsees are based on potentially outdated Flag definition. - reconnection should be attempted with an exponential back-off, with a max-delay of `maxSyncRetryInterval` (see [configuration](#configuration)) - reconnection should be attempted up to `maxSyncRetryDelay` times (see [configuration](#configuration)) - `PROVIDER_READY` and `PROVIDER_CONFIGURATION_CHANGED` should be emitted, in that order, after successful reconnection diff --git a/docs/reference/specifications/protos.md b/docs/reference/specifications/protos.md new file mode 100644 index 000000000..8f1340cee --- /dev/null +++ b/docs/reference/specifications/protos.md @@ -0,0 +1,432 @@ + +# Protocol Documentation + + + +## schema/v1/schema.proto +Flag evaluation API + +This proto forms the basis of a flag-evaluation API. +It supports single and bulk evaluation RPCs, and flags of various types, as well as establishing a stream for getting notifications about changes in a flag definition. +It supports the inclusion of a "context" with each evaluation, which may contain arbitraty attributes relevant to flag evaluation. + + + + +### AnyFlag +A variant type flag response. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| reason | [string](#string) | | The reason for the given return value, see https://openfeature.dev/docs/specification/types#resolution-details | +| variant | [string](#string) | | The variant name of the returned flag value. | +| bool_value | [bool](#bool) | | | +| string_value | [string](#string) | | | +| double_value | [double](#double) | | | +| object_value | [google.protobuf.Struct](#google-protobuf-Struct) | | | + + + + + + + + +### EventStreamRequest +Empty stream request body + + + + + + + + +### EventStreamResponse +Response body for the EventStream stream response + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | String key indicating the type of event that is being received, for example, provider_ready or configuration_change | +| data | [google.protobuf.Struct](#google-protobuf-Struct) | | Object structure for use when sending relevant metadata to provide context to the event. Can be left unset when it is not required. | + + + + + + + + +### ResolveAllRequest +Request body for bulk flag evaluation, used by the ResolveAll rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| context | [google.protobuf.Struct](#google-protobuf-Struct) | | Object structure describing the EvaluationContext used in the flag evaluation, see https://openfeature.dev/docs/reference/concepts/evaluation-context | + + + + + + + + +### ResolveAllResponse +Response body for bulk flag evaluation, used by the ResolveAll rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| flags | [ResolveAllResponse.FlagsEntry](#schema-v1-ResolveAllResponse-FlagsEntry) | repeated | Object structure describing the evaluated flags for the provided context. | + + + + + + + + +### ResolveAllResponse.FlagsEntry + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| key | [string](#string) | | | +| value | [AnyFlag](#schema-v1-AnyFlag) | | | + + + + + + + + +### ResolveBooleanRequest +Request body for boolean flag evaluation, used by the ResolveBoolean rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| flag_key | [string](#string) | | Flag key of the requested flag. | +| context | [google.protobuf.Struct](#google-protobuf-Struct) | | Object structure describing the EvaluationContext used in the flag evaluation, see https://openfeature.dev/docs/reference/concepts/evaluation-context | + + + + + + + + +### ResolveBooleanResponse +Response body for boolean flag evaluation. used by the ResolveBoolean rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| value | [bool](#bool) | | The response value of the boolean flag evaluation, will be unset in the case of error. | +| reason | [string](#string) | | The reason for the given return value, see https://openfeature.dev/docs/specification/types#resolution-details | +| variant | [string](#string) | | The variant name of the returned flag value. | +| metadata | [google.protobuf.Struct](#google-protobuf-Struct) | | Metadata for this evaluation | + + + + + + + + +### ResolveFloatRequest +Request body for float flag evaluation, used by the ResolveFloat rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| flag_key | [string](#string) | | Flag key of the requested flag. | +| context | [google.protobuf.Struct](#google-protobuf-Struct) | | Object structure describing the EvaluationContext used in the flag evaluation, see https://openfeature.dev/docs/reference/concepts/evaluation-context | + + + + + + + + +### ResolveFloatResponse +Response body for float flag evaluation. used by the ResolveFloat rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| value | [double](#double) | | The response value of the float flag evaluation, will be empty in the case of error. | +| reason | [string](#string) | | The reason for the given return value, see https://openfeature.dev/docs/specification/types#resolution-details | +| variant | [string](#string) | | The variant name of the returned flag value. | +| metadata | [google.protobuf.Struct](#google-protobuf-Struct) | | Metadata for this evaluation | + + + + + + + + +### ResolveIntRequest +Request body for int flag evaluation, used by the ResolveInt rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| flag_key | [string](#string) | | Flag key of the requested flag. | +| context | [google.protobuf.Struct](#google-protobuf-Struct) | | Object structure describing the EvaluationContext used in the flag evaluation, see https://openfeature.dev/docs/reference/concepts/evaluation-context | + + + + + + + + +### ResolveIntResponse +Response body for int flag evaluation. used by the ResolveInt rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| value | [int64](#int64) | | The response value of the int flag evaluation, will be unset in the case of error. | +| reason | [string](#string) | | The reason for the given return value, see https://openfeature.dev/docs/specification/types#resolution-details | +| variant | [string](#string) | | The variant name of the returned flag value. | +| metadata | [google.protobuf.Struct](#google-protobuf-Struct) | | Metadata for this evaluation | + + + + + + + + +### ResolveObjectRequest +Request body for object flag evaluation, used by the ResolveObject rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| flag_key | [string](#string) | | Flag key of the requested flag. | +| context | [google.protobuf.Struct](#google-protobuf-Struct) | | Object structure describing the EvaluationContext used in the flag evaluation, see https://openfeature.dev/docs/reference/concepts/evaluation-context | + + + + + + + + +### ResolveObjectResponse +Response body for object flag evaluation. used by the ResolveObject rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| value | [google.protobuf.Struct](#google-protobuf-Struct) | | The response value of the object flag evaluation, will be unset in the case of error. + +NOTE: This structure will need to be decoded from google/protobuf/struct.proto before it is returned to the SDK | +| reason | [string](#string) | | The reason for the given return value, see https://openfeature.dev/docs/specification/types#resolution-details | +| variant | [string](#string) | | The variant name of the returned flag value. | +| metadata | [google.protobuf.Struct](#google-protobuf-Struct) | | Metadata for this evaluation | + + + + + + + + +### ResolveStringRequest +Request body for string flag evaluation, used by the ResolveString rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| flag_key | [string](#string) | | Flag key of the requested flag. | +| context | [google.protobuf.Struct](#google-protobuf-Struct) | | Object structure describing the EvaluationContext used in the flag evaluation, see https://openfeature.dev/docs/reference/concepts/evaluation-context | + + + + + + + + +### ResolveStringResponse +Response body for string flag evaluation. used by the ResolveString rpc. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| value | [string](#string) | | The response value of the string flag evaluation, will be unset in the case of error. | +| reason | [string](#string) | | The reason for the given return value, see https://openfeature.dev/docs/specification/types#resolution-details | +| variant | [string](#string) | | The variant name of the returned flag value. | +| metadata | [google.protobuf.Struct](#google-protobuf-Struct) | | Metadata for this evaluation | + + + + + + + + + + + + + + +### Service +Service defines the exposed rpcs of flagd + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| ResolveAll | [ResolveAllRequest](#schema-v1-ResolveAllRequest) | [ResolveAllResponse](#schema-v1-ResolveAllResponse) | | +| ResolveBoolean | [ResolveBooleanRequest](#schema-v1-ResolveBooleanRequest) | [ResolveBooleanResponse](#schema-v1-ResolveBooleanResponse) | | +| ResolveString | [ResolveStringRequest](#schema-v1-ResolveStringRequest) | [ResolveStringResponse](#schema-v1-ResolveStringResponse) | | +| ResolveFloat | [ResolveFloatRequest](#schema-v1-ResolveFloatRequest) | [ResolveFloatResponse](#schema-v1-ResolveFloatResponse) | | +| ResolveInt | [ResolveIntRequest](#schema-v1-ResolveIntRequest) | [ResolveIntResponse](#schema-v1-ResolveIntResponse) | | +| ResolveObject | [ResolveObjectRequest](#schema-v1-ResolveObjectRequest) | [ResolveObjectResponse](#schema-v1-ResolveObjectResponse) | | +| EventStream | [EventStreamRequest](#schema-v1-EventStreamRequest) | [EventStreamResponse](#schema-v1-EventStreamResponse) stream | | + + + + + + +

Top

+ +## sync/v1/sync_service.proto +Flag definition sync API + +This proto defines a simple API to synchronize a feature flag definition. +It supports establishing a stream for getting notifications about changes in a flag definition. + + + + +### FetchAllFlagsRequest +FetchAllFlagsRequest is the request to fetch all flags. Flagd sends this request as the client in order to resync its internal state + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| provider_id | [string](#string) | | Optional: A unique identifier for flagd(grpc client) initiating the request. The server implementations may utilize this identifier to uniquely identify, validate(ex:- enforce authentication/authorization) and filter flag configurations that it can expose to this request. This field is intended to be optional. However server implementations may enforce it. ex:- provider_id: flagd-weatherapp-sidecar | +| selector | [string](#string) | | Optional: A selector for the flag configuration request. The server implementation may utilize this to select flag configurations from a collection, select the source of the flag or combine this to any desired underlying filtering mechanism. ex:- selector: 'source=database,app=weatherapp' | + + + + + + + + +### FetchAllFlagsResponse +FetchAllFlagsResponse is the server response containing feature flag configurations + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| flag_configuration | [string](#string) | | flagd feature flag configuration. Must be validated to schema - https://raw.githubusercontent.com/open-feature/schemas/main/json/flagd-definitions.json | + + + + + + + + +### SyncFlagsRequest +SyncFlagsRequest is the request initiating the sever-streaming rpc. Flagd sends this request, acting as the client + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| provider_id | [string](#string) | | Optional: A unique identifier for flagd(grpc client) initiating the request. The server implementations may utilize this identifier to uniquely identify, validate(ex:- enforce authentication/authorization) and filter flag configurations that it can expose to this request. This field is intended to be optional. However server implementations may enforce it. ex:- provider_id: flagd-weatherapp-sidecar | +| selector | [string](#string) | | Optional: A selector for the flag configuration request. The server implementation may utilize this to select flag configurations from a collection, select the source of the flag or combine this to any desired underlying filtering mechanism. ex:- selector: 'source=database,app=weatherapp' | + + + + + + + + +### SyncFlagsResponse +SyncFlagsResponse is the server response containing feature flag configurations and the state + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| flag_configuration | [string](#string) | | flagd feature flag configuration. Must be validated to schema - https://raw.githubusercontent.com/open-feature/schemas/main/json/flagd-definitions.json | +| state | [SyncState](#sync-v1-SyncState) | | State conveying the operation to be performed by flagd. See the descriptions of SyncState for an explanation of supported values | + + + + + + + + + + +### SyncState +SyncState conveys the state of the payload. These states are related to flagd isync.go type definitions but +contains extras to optimize grpc use case. Refer - https://github.com/open-feature/flagd/blob/main/pkg/sync/isync.go + +| Name | Number | Description | +| ---- | ------ | ----------- | +| SYNC_STATE_UNSPECIFIED | 0 | Value is ignored by the listening flagd | +| SYNC_STATE_ALL | 1 | All the flags matching the request. This is the default response and other states can be ignored by the implementation. Flagd internally replaces all existing flags for this response state. | +| SYNC_STATE_ADD | 2 | Convey an addition of a flag. Flagd internally handles this by combining new flags with existing ones | +| SYNC_STATE_UPDATE | 3 | Convey an update of a flag. Flagd internally attempts to update if the updated flag already exist OR if it does not, it will get added | +| SYNC_STATE_DELETE | 4 | Convey a deletion of a flag. Flagd internally removes the flag | +| SYNC_STATE_PING | 5 | Optional server ping to check client connectivity. Handling is ignored by flagd and is to merely support live check | + + + + + + + + + +### FlagSyncService +FlagService implements a server streaming to provide realtime flag configurations + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| SyncFlags | [SyncFlagsRequest](#sync-v1-SyncFlagsRequest) | [SyncFlagsResponse](#sync-v1-SyncFlagsResponse) stream | | +| FetchAllFlags | [FetchAllFlagsRequest](#sync-v1-FetchAllFlagsRequest) | [FetchAllFlagsResponse](#sync-v1-FetchAllFlagsResponse) | | + + + + + +## Scalar Value Types + +| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | +| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | +| double | | double | double | float | float64 | double | float | Float | +| float | | float | float | float | float32 | float | float | Float | +| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) | +| uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) | +| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) | +| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum | +| sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| bool | | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | +| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) | +| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) | + diff --git a/docs/reference/specifications/rpc-providers.md b/docs/reference/specifications/rpc-providers.md new file mode 100644 index 000000000..599a5ecda --- /dev/null +++ b/docs/reference/specifications/rpc-providers.md @@ -0,0 +1,155 @@ +# Creating an RPC flagd provider + +By default, **flagd** is a remote service that is accessed via **grpc** by a client application to retrieve feature flags. +Depending on the environment, flagd therefore is usually deployed as a standalone service, e.g. as a Kubernetes Deployment, +or injected as a sidecar container into the pod running the client application, +as it is done in the [OpenFeature Operator](https://github.com/open-feature/open-feature-operator). + +Prerequisites: + +- Understanding of [general provider concepts](https://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)) + +## flagd Evaluation API + +Fundamentally, RPC providers use the [evaluation schema](./protos.md#schemav1schemaproto) to connect to flagd, initiate evaluation RPCs, and listen for changes in the flag definitions. +In order to do this, you must generate the gRPC primitives (message types and client) using the protobuf code generation mechanisms available in your language. +If you are unable to use gRPC code generation, you can also use REST (via the [connect protocol](https://buf.build/blog/connect-a-better-grpc)) to communivate with flagd, though in this case, you will not be able to open a stream to listen for changes. + +### Protobuf + +Protobuf schemas define the contract between the flagd evaluation API and a client. + +#### Code generation for gRPC sync + +Leverage the [buf CLI](https://docs.buf.build/installation) or protoc to generate a `flagd-proxy` 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 +``` + +As an alternative to buf, use the .proto file directly along with whatever protoc-related tools or plugins avaialble for your language. + +Move the generated code (following convention for the chosen language) and add its location to .gitignore + +Note that for the in-process provider only the `schema` package will be relevant, since RPC providers communicate directly to flagd. + +## Provider lifecycle, initialization and shutdown + +With the release of the v0.6.0 spec, OpenFeature now outlines a lifecycle for in-process flagd provider initialization and shutdown. + +In-process flagd providers should do the following to make use of OpenFeature v0.6.0 features: + +- start in a `NOT_READY` state +- fetch the flag definition specified in the sync provider sources and set `state` to `READY` or `ERROR` in the `initialization` function + - note that the SDK will automatically emit `PROVIDER_READY`/`PROVIDER_ERROR` according to the termination of the `initialization` function +- throw an exception or terminate abnormally if a connection cannot be established during `initialization` +- For gRPC based sources (i.e. flagd-proxy), attempt to restore the streaming connection to flagd-proxy (if the connection cannot be established or is broken): + - If flag definition have been retrieved previously, go into `STALE` state to indicate that flag resolution responsees are based on potentially outdated Flag definition. + - reconnection should be attempted with an exponential back-off, with a max-delay of `maxSyncRetryInterval` (see [configuration](#configuration)) + - reconnection should be attempted up to `maxSyncRetryDelay` times (see [configuration](#configuration)) + - `PROVIDER_READY` and `PROVIDER_CONFIGURATION_CHANGED` should be emitted, in that order, after successful reconnection +- For Kubernetes sync sources, retry to retrieve the FlagConfiguration resource, using an exponential back-off strategy, with a max-delay of `maxSyncRetryInterval` (see [configuration](#configuration)) +- emit `PROVIDER_CONFIGURATION_CHANGED` event and update ruleset when a `configuration_change` message is received on the streaming connection +- close the streaming connection in the`shutdown` function + +```mermaid +stateDiagram-v2 + [*] --> NOT_READY + NOT_READY --> READY: initialize(), connection to flagd established, stream connected + NOT_READY --> ERROR: initialize(), unable to connect or establish stream(retry) + READY --> ERROR: stream or connection disconnected + READY --> READY: configuration_change (emit changed*, invalidate cache) + ERROR --> READY: reconnect successful (emit ready*, changed*, invalidate cache) + ERROR --> ERROR: maxSyncRetries reached + ERROR --> [*]: shutdown(), stream disconnected +``` + +\* ready=`PROVIDER_READY`, changed=`PROVIDER_CONFIGURATION_CHANGED`, stale=`PROVIDER_STALE`, error=`PROVIDER_ERROR` + +## Caching + +`flagd` has a caching strategy implementable by RPC providers that support server-to-client streaming. + +### 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)). + +Put simply in pseudocode: + +```pseudo +if reason == "STATIC" { + isFlagCacheable = true +} +``` + +### 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: + +```json +{ + "type": "delete", // ENUM:["delete","write","update"] + "source": "/flag-configuration.json", // the source of the flag configuration + "flagKey": "foo" +} +``` + +A client should invalidate the cache of any flag found in a `configuration_change` event to prevent stale data. +If the connection drops all cache values must be cleared (any number of events may have been missed). + +## Configuration + +Expose means to configure the provider aligned with the following priority system (highest to lowest). + +```mermaid +flowchart LR + constructor-parameters -->|highest priority| environment-variables -->|lowest priority| defaults +``` + +### Explicit declaration + +This takes the form of parameters to the provider's constructor, it has the highest priority. + +### Environment variables + +Read environment variables with sensible defaults (before applying the values explicitly declared to the constructor). + +| Option name | Environment variable name | Type & Values | Default | +|-----------------------|--------------------------------|------------------------|-----------| +| host | FLAGD_HOST | String | localhost | +| port | FLAGD_PORT | int | 8013 | +| tls | FLAGD_TLS | boolean | false | +| socketPath | FLAGD_SOCKET_PATH | String | null | +| certPath | FLAGD_SERVER_CERT_PATH | String | null | +| deadline | FLAGD_DEADLINE_MS | int | 500 | +| cache | FLAGD_CACHE | String - lru, disabled | lru | +| maxCacheSize | FLAGD_MAX_CACHE_SIZE | int | 1000 | +| maxEventStreamRetries | FLAGD_MAX_EVENT_STREAM_RETRIES | int | 5 | +| retryBackoffMs | FLAGD_RETRY_BACKOFF_MS | int | 1000 | + +## Error handling + +Handle flag evaluation errors by using the error constructors exported by the SDK (e.g. `openfeature.NewProviderNotReadyResolutionError(ConnectionError)`), thereby allowing the SDK to parse and handle the error appropriately. + +## Post creation + +The following steps will extend the reach of the newly created provider to other developers of the chosen technology. + +### Open an issue to document the provider + +Create an issue in openfeature.dev [here](https://github.com/open-feature/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/reference/sync-configuration.md b/docs/reference/sync-configuration.md new file mode 100644 index 000000000..089cf8f4d --- /dev/null +++ b/docs/reference/sync-configuration.md @@ -0,0 +1,82 @@ +# Sync configuration + +See [syncs](../concepts/syncs.md) for a conceptual overview. + +## URI patterns + +Any URI passed to flagd via the `--uri` (`-f`) flag must follow one of the 4 following patterns with prefixes to ensure that +it is passed to the correct implementation: + +| Implied Sync Provider | Prefix | Example | +| --------------------- | ---------------------- | ------------------------------------- | +| `kubernetes` | `core.openfeature.dev` | `core.openfeature.dev/default/my-crd` | +| `file` | `file:` | `file:etc/flagd/my-flags.json` | +| `http` | `http(s)://` | `https://my-flags.com/flags` | +| `grpc` | `grpc(s)://` | `grpc://my-flags-server` | + +## Source Configuration + +While a URI may be passed to flagd via the `--uri` (`-f`) flag, some implementations may require further configurations. +In these cases the `--sources` flag should be used. + +The flagd accepts a string argument, which should be a JSON representation of an array of `SourceConfig` objects. + +Alternatively, these configurations can be passed to flagd via config file, specified using the `--config` flag. + +| Field | Type | Note | +| ----------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| uri | required `string` | Flag configuration source of the sync | +| provider | required `string` | Provider type - `file`, `kubernetes`, `http` or `grpc` | +| bearerToken | optional `string` | Used for http sync; token gets appended to `Authorization` header with [bearer schema](https://www.rfc-editor.org/rfc/rfc6750#section-2.1) | +| interval | optional `uint32` | Used for http sync; requests will be made at this interval. Defaults to 5 seconds. | +| tls | optional `boolean` | Enable/Disable secure TLS connectivity. Currently used only by gRPC sync. Default(ex:- if unset) is false, which will use an insecure connection | +| providerID | optional `string` | Value binds to grpc connection's providerID field. gRPC server implementations may use this to identify connecting flagd instance | +| selector | optional `string` | Value binds to grpc connection's selector field. gRPC server implementations may use this to filter flag configurations | +| certPath | optional `string` | Used for grpcs sync when TLS certificate is needed. If not provided, system certificates will be used for TLS connection | + +The `uri` field values **do not** follow the [URI patterns](#uri-patterns). The provider type is instead derived +from the `provider` field. Only exception is the remote provider where `http(s)://` is expected by default. Incorrect +URIs will result in a flagd start-up failure with errors from the respective sync provider implementation. + +Given below are example sync providers, startup command and equivalent config file definition: + +Sync providers: + +- `file` - config/samples/example_flags.json +- `http` - +- `https` - +- `kubernetes` - default/my-flag-config +- `grpc`(insecure) - grpc-source:8080 +- `grpcs`(secure) - my-flag-source:8080 + +Startup command: + +```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-source:8080","provider":"grpc"}, + {"uri":"my-flag-source:8080","provider":"grpc", "certPath": "/certs/ca.cert", "tls": true, "providerID": "flagd-weatherapp-sidecar", "selector": "source=database,app=weatherapp"}]' +``` + +Configuration file, + +```yaml +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: my-flag-source:8080 + provider: grpc + - uri: my-flag-source:8080 + provider: grpc + certPath: /certs/ca.cert + tls: true + providerID: flagd-weatherapp-sidecar + selector: 'source=database,app=weatherapp' +``` diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 000000000..4ee755601 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,46 @@ +# Troubleshooting flagd + +## Debugging Evaluations + +If a flag or targeting rule isn't proceeding the way you'd expect, you may want to enable more verbose logging. + +flagd and flagd providers typically have debug or verbose logging modes that you can use for this sort of troubleshooting. +You can do this in the standalone version of flagd by starting it with the `--debug` flag (see [CLI](./reference/flagd-cli/flagd/flagd.md) for more information). + +_In-process_ providers which embed the flag evaluation engine use a logging consistent with their implementation language and SDK. +See your provider's documentation for details on how to enable verbose logging. + +The [detailed evaluation](https://openfeature.dev/docs/reference/concepts/evaluation-api#detailed-evaluation) functions can also be helpful in understanding why an evaluation proceeded a particular way. + +--- + +## HTTP 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 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/usage/evaluation_examples.md b/docs/usage/evaluation_examples.md deleted file mode 100644 index f83946f6a..000000000 --- a/docs/usage/evaluation_examples.md +++ /dev/null @@ -1,208 +0,0 @@ -# Evaluation Examples Using curl - -1. Download sample flag configuration: - - ```shell - curl https://raw.githubusercontent.com/open-feature/flagd/main/config/samples/example_flags.flagd.json -o example_flags.flagd.json - ``` - -1. Run one of the following commands, depending on how [flagd was installed](../usage/getting_started.md): - - binary: - - ```shell - flagd start --uri file:example_flags.flagd.json - ``` - - - Docker: - - ```shell - 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! - -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 - -Command: - -```sh -curl -X POST "localhost:8013/schema.v1.Service/ResolveBoolean" -d '{"flagKey":"myBoolFlag","context":{}}' -H "Content-Type: application/json" -``` - -For Windows users, open a command prompt and use: - -```sh -set json={"flagKey":"myBoolFlag","context":{}} -curl -i -X POST -H "Content-Type: application/json" -d %json:"=\"% "localhost:8013/schema.v1.Service/ResolveBoolean" -``` - -Result: - -```sh -{"value":true,"reason":"DEFAULT","variant":"on"} -``` - -## Resolve a string value - -Command: - -```sh -curl -X POST "localhost:8013/schema.v1.Service/ResolveString" -d '{"flagKey":"myStringFlag","context":{}}' -H "Content-Type: application/json" -``` - -For Windows users, open a command prompt and use: - -```sh -set json={"flagKey":"myStringFlag","context":{}} -curl -i -X POST -H "Content-Type: application/json" -d %json:"=\"% "localhost:8013/schema.v1.Service/ResolveString" -``` - -Result: - -```sh -{"value":"val1","reason":"DEFAULT","variant":"key1"} -``` - -## Resolve a integer value - -Command: - -```sh -curl -X POST "localhost:8013/schema.v1.Service/ResolveInt" -d '{"flagKey":"myIntFlag","context":{}}' -H "Content-Type: application/json" -``` - -For Windows users, open a command prompt and use: - -```sh -set json={"flagKey":"myIntFlag","context":{}} -curl -i -X POST -H "Content-Type: application/json" -d %json:"=\"% "localhost:8013/schema.v1.Service/ResolveInt" -``` - -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 - -Command: - -```sh -curl -X POST "localhost:8013/schema.v1.Service/ResolveFloat" -d '{"flagKey":"myFloatFlag","context":{}}' -H "Content-Type: application/json" -``` - -For Windows users, open a command prompt and use: - -```sh -set json={"flagKey":"myFloatFlag","context":{}} -curl -i -X POST -H "Content-Type: application/json" -d %json:"=\"% "localhost:8013/schema.v1.Service/ResolveFloat" -``` - -Result: - -```sh -{"value":1.23,"reason":"DEFAULT","variant":"one"} -``` - -## Resolve an object value - -Command: - -```sh -curl -X POST "localhost:8013/schema.v1.Service/ResolveObject" -d '{"flagKey":"myObjectFlag","context":{}}' -H "Content-Type: application/json" -``` - -For Windows users, open a command prompt and use: - -```sh -set json={"flagKey":"myObjectFlag","context":{}} -curl -i -X POST -H "Content-Type: application/json" -d %json:"=\"% "localhost:8013/schema.v1.Service/ResolveObject" -``` - -Result: - -```sh -{"value":{"key":"val"},"reason":"DEFAULT","variant":"object1"} -``` - -## Resolve a boolean value with evaluation context - -Command: - -```sh -curl -X POST "localhost:8013/schema.v1.Service/ResolveBoolean" -d '{"flagKey":"isColorYellow","context":{"color":"yellow"}}' -H "Content-Type: application/json" -``` - -For Windows users, open a command prompt and use: - -```sh -set json={"flagKey":"isColorYellow","context":{"color":"yellow"}} -curl -i -X POST -H "Content-Type: application/json" -d %json:"=\"% "localhost:8013/schema.v1.Service/ResolveBoolean" -``` - -Result: - -```sh -{"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. - -Command: - -```sh -curl -X POST "localhost:8013/schema.v1.Service/ResolveString" -d '{"flagKey":"myBoolFlag","context":{}}' -H "Content-Type: application/json" -``` - -Result: - -```sh -{"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. - -Command: - -```sh -curl -X POST "localhost:8013/schema.v1.Service/ResolveBoolean" -d '{"flagKey":"aMissingFlag","context":{}}' -H "Content-Type: application/json" -``` - -Result: - -```sh -{"code":"not_found","message":"FLAG_NOT_FOUND"} -``` - -## Resolve all values - -Command: - -```sh -curl -X POST "localhost:8013/schema.v1.Service/ResolveAll" -d '{"context":{}}' -H "Content-Type: application/json" -``` - -For Windows users, open a command prompt and use: - -```sh -set json={"context":{}} -curl -i -X POST -H "Content-Type: application/json" -d %json:"=\"% "localhost:8013/schema.v1.Service/ResolveAll" -``` - -Result: - -```sh -{"flags":{"fibAlgo":{"reason":"DEFAULT", "variant":"recursive", "stringValue":"recursive"}, "headerColor":{"reason":"DEFAULT", "variant":"red", "stringValue":"#FF0000"}, "isColorYellow":{"reason":"TARGETING_MATCH", "variant":"off", "boolValue":false}, "myBoolFlag":{"reason":"STATIC", "variant":"on", "boolValue":true}, "myFloatFlag":{"reason":"STATIC", "variant":"one", "doubleValue":1.23}, "myIntFlag":{"reason":"STATIC", "variant":"one", "doubleValue":1}, "myObjectFlag":{"reason":"STATIC", "variant":"object1", "objectValue":{"key":"val"}}, "myStringFlag":{"reason":"STATIC", "variant":"key1", "stringValue":"val1"}}} -``` diff --git a/docs/usage/flagd_providers.md b/docs/usage/flagd_providers.md deleted file mode 100644 index f6146889f..000000000 --- a/docs/usage/flagd_providers.md +++ /dev/null @@ -1,16 +0,0 @@ -# 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: - -| Language | Provider | -| ----------- | ----------- | -| Go | [Go Flagd Provider](https://github.com/open-feature/go-sdk-contrib/tree/main/providers/flagd) -| Java | [Java Flagd Provider](https://github.com/open-feature/java-sdk-contrib/tree/main/providers/flagd) -| Javascript | [Javascript Flagd Provider](https://github.com/open-feature/js-sdk-contrib/tree/main/libs/providers/flagd) -| PHP | [PHP Flagd Provider](https://github.com/open-feature/php-sdk-contrib/tree/main/src/Flagd) -| Python | Not currently available, [help by contributing here](https://github.com/open-feature/python-sdk-contrib) -| .NET | [.NET Flagd Provider](https://github.com/open-feature/dotnet-sdk-contrib/tree/main/src/OpenFeature.Contrib.Providers.Flagd) -| Ruby | Not currently available, [help by contributing here](https://github.com/open-feature/ruby-sdk-contrib) - -Any (new or existing) `flagd` providers ought to follow [these guidelines](../other_resources/creating_providers.md). diff --git a/docs/usage/installation_options.md b/docs/usage/installation_options.md deleted file mode 100644 index e8e93dc5e..000000000 --- a/docs/usage/installation_options.md +++ /dev/null @@ -1,42 +0,0 @@ -# Get Started - -## Installation - -There are many ways to get started with flagd. -Choose the method that best serves your requirements to get started. - -### Docker - -1. `docker pull ghcr.io/open-feature/flagd:latest` - -### Homebrew 🍺 - -1. `brew install flagd` - -### Go binary - -1. Install Go 1.20 or above -1. run `go install github.com/open-feature/flagd/flagd@latest` - -### Release binary - -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: - -## Next Steps - -The documentation in the following pages will help you to correctly configure your flagd service, as well as create and evaluate your own custom flags, either using curl or one of the OpenFeature language specific providers. - -- [Configuring flagd](../configuration/configuration.md) -- [Creating custom flag definitions](../configuration/flag_configuration.md) -- [Evaluating flag values using a flagd provider](../usage/flagd_providers.md) -- [Evaluating flag values using curl](../usage/evaluation_examples.md) diff --git a/flagd/cmd/doc/main.go b/flagd/cmd/doc/main.go index 8b909d31a..82e4a1c12 100644 --- a/flagd/cmd/doc/main.go +++ b/flagd/cmd/doc/main.go @@ -6,7 +6,7 @@ import ( "github.com/open-feature/flagd/flagd/cmd" ) -const docPath = "../docs/configuration" +const docPath = "../docs/reference/flagd-cli" func main() { if err := cmd.GenerateDoc(docPath); err != nil { diff --git a/flagd/cmd/version.go b/flagd/cmd/version.go index 7a6e7e8dc..67abc0ceb 100644 --- a/flagd/cmd/version.go +++ b/flagd/cmd/version.go @@ -10,7 +10,7 @@ import ( // versionCmd represents the version command var versionCmd = &cobra.Command{ Use: "version", - Short: "Print the version number of FlagD", + Short: "Print the version number of flagd", Long: ``, Run: func(cmd *cobra.Command, args []string) { if Version == "dev" { diff --git a/mkdocs.yml b/mkdocs.yml index 4e29fe76c..3195912dd 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,4 +1,5 @@ site_name: flagd +site_description: A feature flag daemon with a Unix philosophy theme: name: material palette: @@ -15,44 +16,77 @@ theme: toggle: icon: material/brightness-4 name: Switch to light mode - # logo: path/to/file.png - # favicon: path/to/file.png + logo: assets/logo-white.svg + favicon: assets/logo-white.svg icon: repo: fontawesome/brands/github features: - # - navigation.tabs - - content.action.edit - - content.code.copy + - navigation.footer + - content.action.edit + - content.code.copy -docs_dir: web-docs +docs_dir: docs repo_url: https://github.com/open-feature/flagd repo_name: open-feature/flagd -edit_uri: edit/main/web-docs/ +edit_uri: edit/main/docs/ markdown_extensions: - pymdownx.highlight: anchor_linenums: true # linenums: true + line_spans: __span + pygments_lang_class: true - pymdownx.inlinehilite - pymdownx.snippets - - pymdownx.superfences + - admonition + - pymdownx.details + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + - tables + - attr_list + - pymdownx.emoji: + # we are using an older emoji extension because the newest (suggested in docs) doesn't work with Python < 3.10 + emoji_index: !!python/name:materialx.emoji.twemoji + emoji_generator: !!python/name:materialx.emoji.to_svg nav: - - 'Home': 'index.md' + - 'Introduction': 'index.md' + - 'Quick start': 'quick-start.md' - 'Concepts': - - 'Flagd concepts (start here)': 'concepts/index.md' - - 'Flagd architecture': 'concepts/architecture.md' - - 'Flagd on Kubernetes': - - 'Getting started with flagd on kubernetes': 'k8s/index.md' - - 'Flagd outside of kubernetes': - - 'Getting started with flagd outside of kubernetes': 'nonk8s/index.md' - - 'Flag merging': 'nonk8s/flagmerging.md' - - 'Flagd as a system service': 'nonk8s/systemservice.md' - - 'Kubernetes Custom Resources': - - 'FlagSourceConfiguration': 'k8s/crds/flagsourceconfiguration.md' - - 'FeatureFlagConfiguration': 'k8s/crds/featureflagconfiguration.md' - - 'Flagd Telemetry': - - 'Flagd OpenTelemetry Support': 'concepts/opentelemetry.md' + - 'Feature Flagging': 'concepts/feature-flagging.md' + - 'Syncs': 'concepts/syncs.md' + - 'Architecture': 'architecture.md' + - 'Deployment': 'deployment.md' + - 'Reference': + - 'CLI': + - 'Commands': 'reference/flagd-cli/flagd.md' + - '"start" Command': 'reference/flagd-cli/flagd_start.md' + - '"version" Command': 'reference/flagd-cli/flagd/flagd_version.md' + - 'Sync Configuration': 'reference/sync-configuration.md' + - 'Flag Definitions': + - 'Definition Overview': 'reference/flag-definitions.md' + - 'Custom Operations': + - 'Fractional': 'reference/custom-operations/fractional-operation.md' + - 'Semantic Version': 'reference/custom-operations/semver-operation.md' + - 'String Comparison': 'reference/custom-operations/string-comparison-operation.md' + - 'Providers': 'reference/providers.md' + - 'Monitoring': 'reference/monitoring.md' + - 'Specifications': + - 'RPC Providers': 'reference/specifications/rpc-providers.md' + - 'In-Process Providers': 'reference/specifications/in-process-providers.md' + - 'Protobuf Schemas': 'reference/specifications/protos.md' + - 'Custom Operations': + - 'Fractional Specification': 'reference/specifications/custom-operations/fractional-operation-spec.md' + - 'Semantic Version Specification': 'reference/specifications/custom-operations/semver-operation-spec.md' + - 'String Comparison Specification': 'reference/specifications/custom-operations/string-comparison-operation-spec.md' + - 'OpenFeature Operator': + - 'Installation': 'reference/openfeature-operator/installation.md' + - 'FeatureFlagConfiguration': 'reference/openfeature-operator/crds/featureflagconfiguration.md' + - 'FlagSourceConfiguration': 'reference/openfeature-operator/crds/flagsourceconfiguration.md' + - 'Naming': 'reference/naming.md' + - 'FAQ': 'faq.md' - 'Troubleshooting': 'troubleshooting.md' - - 'Ecosystem and Tooling': 'ecosystem.md' plugins: - - social \ No newline at end of file + - social diff --git a/netlify.toml b/netlify.toml index 5bc3c1d40..d53b4732f 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,7 +1,7 @@ [build] publish = "site" # https://docs.netlify.com/configure-builds/ignore-builds/ - ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF netlify.toml mkdocs.yml pyproject.toml ./web-docs/" + ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF netlify.toml mkdocs.yml pyproject.toml ./docs/" command = """ pip3 install -q poetry && poetry config virtualenvs.in-project true && diff --git a/schemas b/schemas new file mode 160000 index 000000000..f3e419c5e --- /dev/null +++ b/schemas @@ -0,0 +1 @@ +Subproject commit f3e419c5ea676b6e0a4384b1ba3c9ad43b047eed diff --git a/web-docs/concepts/architecture.md b/web-docs/concepts/architecture.md deleted file mode 100644 index e97a12e3b..000000000 --- a/web-docs/concepts/architecture.md +++ /dev/null @@ -1,60 +0,0 @@ -# flagd Architecture - -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 -for flag configuration change notifications. - -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 Runtime stays in between these components and coordinates operations. - -![flagd runtime](../images/of-flagd-0.png) - -## 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 -(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 -process gets pushed to event subscribers. - -![flagd sync logic](../images/of-flagd-1.png) - -## Readiness & Liveness probes - -### HTTP - -Flagd exposes HTTP liveness and readiness probes. -These probes can be used for K8s deployments. -With default start-up configurations, these probes are exposed on the metrics port (default: 8014) at the following URLs, - -- Liveness: -- Readiness: - -### gRPC - -Flagd exposes a [standard gRPC liveness check](https://github.com/grpc/grpc/blob/master/doc/health-checking.md) on the metrics port (default: 8014). - -### Definition of Liveness - -The liveness probe becomes active and HTTP 200 status is served as soon as Flagd service is up and running. - -### 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. diff --git a/web-docs/concepts/index.md b/web-docs/concepts/index.md deleted file mode 100644 index 573ad51db..000000000 --- a/web-docs/concepts/index.md +++ /dev/null @@ -1,225 +0,0 @@ -# flagd basics - -Your flagd journey will start by defining your feature flags. - -flagd will then read those feature flags and make them available to your application. - -Your application will interact with flagd via the [OpenFeature SDK](https://openfeature.dev/docs/reference/concepts/evaluation-api) to retrieve flag values via the flagd API. - -![flagd architecture](../images/flagd-logical-architecture.jpg) - -## Defining feature flags - -Flags can be defined in either [JSON](https://github.com/open-feature/flagd/blob/main/samples/example_flags.flagd.json) or [YAML](https://github.com/open-feature/flagd/blob/main/samples/example_flags.flagd.yaml) syntax and the values can be of different types. - -Here are two flags, `flagOne` has `boolean` values and `flagTwo` has `string` values. - -### flags represented as JSON - -```json -{ - "flags": { - "flagOne": { - "state": "ENABLED", - "variants": { - "on": true, - "off": false - }, - "defaultVariant": "on", - "targeting": {} - }, - "flagTwo": { - "state": "ENABLED", - "variants": { - "key1": "val1", - "key2": "val2" - }, - "defaultVariant": "key1", - "targeting": {} - } - } -} -``` - -### flags represented as YAML - -```yaml -flags: - flagOne: - state: ENABLED - variants: - 'on': true - 'off': false - defaultVariant: 'on' - targeting: - flagTwo: - state: ENABLED - variants: - key1: val1 - key2: val2 - defaultVariant: 'key1' - targeting: -``` - -## The structure of a flag - -Each flag has: - -- A flag key: `flagOne` and `flagTwo` above -- A state: `ENABLED` or `DISABLED` -- One or more possible `variants`. These are the possible values that a flag key can take. -- An optional `targeting` rule (explained below) - -## Targeting rules - -Imagine you are introducing a new feature. You create a flag with two possible variants: `on` and `off`. You want to safely roll out the feature. -Therefore the flags `defaultValue` is set to `off` for all users. - -In other words, the new feature is disabled by default. - -Now imagine you want to enable the feature, but only when the following is true: - -- Logged in users where the user's email ends in `@example.com` - -Rather than codifying that in your application, flagd targeting rules can be used. The flag definition below models this behaviour. - -When a user logs into your application, your application is responsible for sending the `email` address via OpenFeature's context parameter (see below) and flagd will return the correct flag. -If the email address of the logged in users contains `@example.com` then flagd will return the `on` variant (ie. `true`). -All other users receives the `defaultVariant` of `off` (ie. false). -In this context, "all other users" means: - -- Any logged in user whos email does not contain `@example.com` -- Any logged out user - -Your application is responsible for sending the `email` address via OpenFeature's context parameter (see below) and flagd will return the correct flag. - -```json -{ - "flags": { - "isFeatureEnabled": { - "state": "ENABLED", - "variants": { - "on": true, - "off": false - }, - "defaultVariant": "off", - "targeting": { - "if": [{ - "in": [ - "@example.com", - { - "var": ["email"] - }] - }, - "on", null] - } - } - } -} -``` - -### Pseudo-code of application passing context - -```js -// Here, we provide an empty context, hence the flag evaluates to false value which is the defaultVariant -featureAvailable = openFeature.getBooleanValue("isFeatureEnabled", false, {}) // false - -// Here, we provide email for the flag evaluation. Still flag evaluates to defaultVariant of false as email does not end with desired domain -featureAvailable = openFeature.getBooleanValue("isFeatureEnabled", false, {"email": "example@gmail.com"}) // false - -// Here, the flag is evaluated with targeting rule matching, hence the value of true -featureAvailable = openFeature.getBooleanValue("isFeatureEnabled", false, {"email": "someone@example.com"}) // true -``` - -## Fractional Evaluation - -In some scenarios, it is desirable to use contextual information to segment the user population further and thus return dynamic values. - -Look at the `headerColor` flag below. The `defaultVariant` is `red`, but the flag contains a targeting rule, meaning a fractional evaluation occurs when a context is passed and a key of `email` contains the value `@example.com`. - -In this case, `25%` of the email addresses will receive `red`, `25%` will receive `blue`, and so on. - -```json -{ - "flags": { - "headerColor": { - "variants": { - "red": "#FF0000", - "blue": "#0000FF", - "green": "#00FF00", - "yellow": "#FFFF00" - }, - "defaultVariant": "red", - "state": "ENABLED", - "targeting": { - "if": [{ - "emailWithFaas": { - "in": ["@faas.com", { - "var": ["email"] - }] - } - }, - { - "fractional": [ - { "var": "email" }, - [ "red", 25 ], [ "blue", 25 ], [ "green", 25 ], [ "yellow", 25 ] - ] - }, null - ] - } - } - } -} -``` - -### Fractional evaluations are sticky - -Fractional evaluations are "sticky" (deterministic) meaning that the same email address will always belong to the same "bucket" and thus always receive the same color. -This is true even if you run multiple flagd instances completely independently. - -Note that the first argument to the `fractional` operator is an expression specifying the *bucketing value*. -This value is used as input to the bucketing algorithm to ensure a deterministic result. -This argument can be omitted, in which case a concatenation of the `targetingKey` and the `flagKey` will be used as the bucketing value. - -See this page for more information on [flagd fractional evaluation logic](https://github.com/open-feature/flagd/blob/main/docs/configuration/fractional_evaluation.md). - -### Migrating from legacy fractionalEvaluation - -If you are using a legacy fractional evaluation (`fractionalEvaluation`), it's recommended you migrate to `fractional`. -The new `fractional` evaluator supports nested properties and JsonLogic expressions. -To migrate, simply use a JsonLogic variable declaration for the bucketing property, instead of a string: - -old: - -```json -"fractionalEvaluation": [ - "email", - [ "red", 25 ], [ "blue", 25 ], [ "green", 25 ], [ "yellow", 25 ] -] -``` - -new: - -```json -"fractional": [ - { "var": "email" }, - [ "red", 25 ], [ "blue", 25 ], [ "green", 25 ], [ "yellow", 25 ] -] -``` - -## Other target specifiers - -The example above shows the `in` keyword being used, but flagd is also compatible with: - -- [starts_with](https://github.com/open-feature/flagd/blob/main/docs/configuration/string_comparison_evaluation.md#startswith-evaluation-configuration) -- [ends_with](https://github.com/open-feature/flagd/blob/main/docs/configuration/string_comparison_evaluation.md#endswith-evaluation-configuration) -- [sem_ver comparisons](https://github.com/open-feature/flagd/blob/main/docs/configuration/sem_ver_evaluation.md) - -## flagd OpenTelemetry - -flagd is fully compatible with OpenTelemetry: - -- flagd exposes metrics at `http://localhost:8014/metrics` -- flagd can export metrics and traces to an OpenTelemetry collector. - -See the [flagd OpenTelemetry](opentelemetry.md) page for more information. diff --git a/web-docs/ecosystem.md b/web-docs/ecosystem.md deleted file mode 100644 index 0ccd5c504..000000000 --- a/web-docs/ecosystem.md +++ /dev/null @@ -1,39 +0,0 @@ -# The flagd ecosystem - -flagd relies upon many projects and tools. - -We thank the following projects and always contribute upstream whenever and wherever it makes sense. - -## Kubernetes and the Operator Pattern - -flagd can be leveraged in Kubernetes clusters by installing the [OpenFeature Operator](k8s/index.md). - -The OpenFeature Operator is, as the name suggests, written to follow the [Kubernetes Operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/). - -## ArtifactHub - -The OpenFeature Operator is [listed on ArtifactHub](https://artifacthub.io/packages/helm/open-feature-operator/open-feature-operator). - -## Helm - -[Helm](https://helm.sh) is used to package and install the OpenFeature Operator. - -## cert-manager - -[cert-manager](https://cert-manager.io/) is a prerequisite of installing the OpenFeature Operator. - -The OpenFeature Operator is a server that communicates with Kubernetes components within a cluster. -As such, it requires a means of authorizing requests between peers. -Cert manager handles authorization by adding certificates and certificate issuers as resource types in Kubernetes clusters. -This simplifies the process of obtaining, renewing, and using those certificates. - -## gRPC - -flagd offers [gRPC](https://grpc.io) in two ways: - -- An interface between the OpenFeature SDK (ie. clients who want to query flagd). -- An interface between flagd and feature flag providers (via the SyncProvider concept). - -## MurmurHash - -[MurmurHash](https://github.com/aappleby/smhasher) (specificially [MurmurHash3](https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp)) is used by flagd during [fractional evaluation](concepts/index.md#fractional-evaluation). diff --git a/web-docs/images/flagd-logical-architecture.jpg b/web-docs/images/flagd-logical-architecture.jpg deleted file mode 100644 index e3a5c7147..000000000 Binary files a/web-docs/images/flagd-logical-architecture.jpg and /dev/null differ diff --git a/web-docs/images/flagd-telemetry.png b/web-docs/images/flagd-telemetry.png deleted file mode 100644 index b4182b506..000000000 Binary files a/web-docs/images/flagd-telemetry.png and /dev/null differ diff --git a/web-docs/images/of-flagd-0.png b/web-docs/images/of-flagd-0.png deleted file mode 100644 index a910f0b4e..000000000 Binary files a/web-docs/images/of-flagd-0.png and /dev/null differ diff --git a/web-docs/images/of-flagd-1.png b/web-docs/images/of-flagd-1.png deleted file mode 100644 index 5e059fcb1..000000000 Binary files a/web-docs/images/of-flagd-1.png and /dev/null differ diff --git a/web-docs/index.md b/web-docs/index.md deleted file mode 100644 index 78c5e9d63..000000000 --- a/web-docs/index.md +++ /dev/null @@ -1,30 +0,0 @@ -# flagd - -Flagd is a feature flag daemon. It is a ready-made, open source, [OpenFeature](https://openfeature.dev) compliant feature flag backend system. - -- OpenFeature compliant and speaks your language. -- Easy to extend to new languages. -- Supports multiple data sources simultaneously. -- Feature Flag updates occur in near real-time. -- Contains a powerful and flexible rule targeting engine and deterministic percentage-based rollouts. -- Flag evaluation statistics and metrics are exposed and compatible with OpenTelemetry. - -![flagd architecture](images/flagd-logical-architecture.jpg) - -## flagd concepts - -Whether you choose to run flagd on a Kubernetes cluster or outside of Kubernetes, there are concepts that apply to both equally. - -Start your flagd learning journey here: [flagd concepts](concepts/index.md) - -## Running flagd - -### Kubernetes or Non-Kubernetes? - -Now that you know the concepts, it's time to decide how to run flagd. - -flagd can run on Kubernetes and in non-k8s environments. -Choose your mode of deployment to learn more: - -- [flagd on Kubernetes](k8s/index.md) -- [flagd running outside of Kubernetes](nonk8s/index.md) diff --git a/web-docs/nonk8s/flagmerging.md b/web-docs/nonk8s/flagmerging.md deleted file mode 100644 index 5a171850a..000000000 --- a/web-docs/nonk8s/flagmerging.md +++ /dev/null @@ -1,46 +0,0 @@ -# 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: - -![flag merge 1](../images/flag-merge-1.svg) - -In this example, `source-A` and `source-B` provide a single flag configuration, the `foo` flag and the `bar` flag 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: - -```sh -./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. - -![flag merge 2](../images/flag-merge-2.svg) - -## State Resync Events - -Given the above example, the `source-A` and `source-B` 'versions' of flag configuration the `foo` have been discarded, so if a delete event in `source-C` results in the removal of the `foo`flag, there will no longer be any reference of `foo` 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 the `foo` flag from `source-B` will be stored in the merged state, preventing flagd from returning `FLAG_NOT_FOUND` errors. - -![flag merge 3](../images/flag-merge-3.svg) - -In the example above, a delete event results in a resync event being fired, as `source-C` has deleted its 'version' of the `foo`, this results in a new merge state being formed from the remaining configurations. - -![flag merge 4](../images/flag-merge-4.svg) - -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/web-docs/nonk8s/index.md b/web-docs/nonk8s/index.md deleted file mode 100644 index f5f76c86e..000000000 --- a/web-docs/nonk8s/index.md +++ /dev/null @@ -1,139 +0,0 @@ -# Install flagd - -Install and run flagd almost anywhere outside of Kubernetes. - -## Download flagd binary or container - -flagd can be run as a standalone-binary or container. - -Kubernetes-native? flagd can also be run [as part of the Kubernetes Operator](../k8s/index.md). - -There are many ways to get started with flagd. -Choose the method that best serves your requirements to get started. - -## Release binary - -Download pre-built binaries from - -## Docker - -```shell -docker pull ghcr.io/open-feature/flagd:latest -``` - -## Homebrew - -```shell -brew install flagd -``` - -## Snap - -[flagd is available on snapcraft](https://snapcraft.io/flagd): - -```shell -sudo snap install flagd -``` - -## Go binary - -1. Install Go 1.20 or above -1. Run `go install github.com/open-feature/flagd/flagd@latest` - -## Systemd service - -Documentation for installing flagd as a systemd service can be found [here](systemservice.md) - -## flagd start - -Start flagd: - -```shell -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 does not work and this is a [limitation of Docker](https://github.com/docker/for-win/issues/8479)._ - -```shell -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: - -```sh -wget https://raw.githubusercontent.com/open-feature/flagd/main/samples/example_flags.flagd.json -``` - -In local mode, run flagd like this: - -```sh -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_): - -```sh -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](https://github.com/open-feature/flagd/blob/main/docs/configuration/configuration.md#uri-patterns). - -## Multiple flag sources and flag merging logic - -Multiple `--uri` parameters can be specified. In other words, flagd can retrieve flags from multiple sources simultaneously. - -See the [flag merging page](flagmerging.md) for more information. - -## Perform flag evaluations - -Flagd is now ready to perform flag evaluations over either `HTTP(s)` or `gRPC`. This example utilizes `HTTP` via `cURL`. - -Retrieve a `String` value: - -```sh -curl -X POST "http://localhost:8013/schema.v1.Service/ResolveString" \ - -d '{"flagKey":"myStringFlag","context":{}}' -H "Content-Type: application/json" -``` - -For Windows we recommend using a [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) terminal. -Otherwise, use the following with `cmd`: - -```sh -set json={"flagKey":"myStringFlag","context":{}} -curl -i -X POST -H "Content-Type: application/json" -d %json:"=\"% "localhost:8013/schema.v1.Service/ResolveString" -``` - -Result: - -```json -{ - "value": "val1", - "reason": "DEFAULT", - "variant":"key1" -} -``` - -Updates to the underlying flag store (e.g. JSON file) are reflected by flagd in realtime. No restarts required. - -flagd also supports boolean, integer, float and object flag types. Read more on the [evaluation examples page](https://github.com/open-feature/flagd/blob/main/docs/usage/evaluation_examples.md) - -## Integrate your application - -Now that flagd is running, it is time to integrate into your application. Do this by using [an OpenFeature provider in a language of your choice](https://github.com/open-feature/flagd/blob/main/docs/usage/flagd_providers.md). diff --git a/web-docs/nonk8s/systemservice.md b/web-docs/nonk8s/systemservice.md deleted file mode 100644 index 7d6e359c2..000000000 --- a/web-docs/nonk8s/systemservice.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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`. - -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 - Main PID: 64610 (flagd) - Tasks: 7 (limit: 4572) - Memory: 1.4M - CGroup: /system.slice/flagd.service - └─64610 /usr/local/bin/flagd start -f=/etc/flagd/flags.json - -May 30 12:19:55 foo systemd[1]: Started "A generic feature flag daemon". -``` diff --git a/web-docs/troubleshooting.md b/web-docs/troubleshooting.md deleted file mode 100644 index 7d99a8f1a..000000000 --- a/web-docs/troubleshooting.md +++ /dev/null @@ -1,32 +0,0 @@ -# Troubleshooting flagd - -## 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: - -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"} -```