Skip to content

Commit

Permalink
fix(develop) Port recent changes from develop to docs
Browse files Browse the repository at this point in the history
I forgot that the develop repo was merged with this one, and I made
changes to the old repo (as it wasn't archived). These changes port my
updates to this repository.
  • Loading branch information
markstory committed Aug 14, 2024
1 parent 549d69e commit 3576a45
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 36 deletions.
37 changes: 30 additions & 7 deletions develop-docs/application/feature-flags/flagpole.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ options:
: A wrapper around a list of conditions, acting as a logical grouping of customers/entities to enable the feature flag for. Segments allow you to create `OR` operations with other segments, meaning at least one segment must evaluate to `True` for a feature to be granted. If an empty segments list is provided, the feature will evaluate to `False`.

### Segments

`conditions`

: A list of predicates to evaluate for the feature flag to be enabled for this segment. All conditions in a segment must be evaluate to `True` in order for the segment to be enabled. If no conditions are defined, the segment will evaluate to `False`.
Expand Down Expand Up @@ -98,9 +99,11 @@ Flagpole currently supports the following `operator` types:
: The inverse of `equals`, evaluates to `True` if the context property value does not match the provided value.

## Evaluation Contexts

When a feature flag is checked, the caller must provide one or more entity objects for the feature handler to match against. These objects are used to construct an `EvaluationContext`, which is essentially a flat dictionary of keys and primitive values. This context is passed to each feature, which provides this context to each segment and condition to enforce whether the feature should be enabled.

### Context Builders

Each evaluation context is built up in stages via a [`ContextBuilder`](https://github.com/getsentry/sentry/blob/3cbf73a389a4ea006cbad9d8c4b8073effe09393/src/flagpole/evaluation_context.py#L54) object.

Each context builder consists of a list of context transformers, which are responsible for creating individual slices of the larger evaluation context.
Expand All @@ -113,6 +116,10 @@ Here are some common properties we surface via our Sentry and GetSentry context

: The sentry region or single-tenant the flag check is being perfomed in.

`sentry_singletenant` [bool]

: Whether or not the application is operating in a single-tenant environment.

**Organization Context Properties**

`organization_id` [int]
Expand Down Expand Up @@ -160,7 +167,7 @@ To see the latest context properties available, check out the repo-specific cont

## Rolling out a new Flagpole feature

Creating a new Flagpole Feature is currently a 2 step process:
Creating a new Flagpole Feature is currently a 3 step process:
1. Register a new feature in Sentry's `temporary.py` file or GetSentry's `features.py` file with the Flagpole strategy:
```python
features.add(
Expand All @@ -176,22 +183,38 @@ features.add(
feature.<feature_scope>:<feature_name>
```

<Alert level="info">
The feature config should not be merged until the registration commit in step 1 has been fully deployed to all target environments. This is because Options Automator will fail to push options to any environments missing the option registration.
If this happens, make sure to rerun the options deployment once all environments have been updated to ensure your feature is active.
</Alert>

Once the option change is deployed, the feature checks will immediately be active in all environments and regions.

_Note:_ The feature config should not be merged until the registration commit in step 1 has been fully deployed to all target environments. This is because Options Automator will fail to push options to any environments missing the option registration.
## Using flagpole in single-tenants

To allow your flagpole feature configuration to be used in single-tenant
environments, you'll need to add your feature name to the
`flagpole.allowed_features` list for each tenant. For example, in
`options/regions/acme.yml` add the following:

```yaml
options:
flagpole.allowed_features: ["organizations:is_sentry"]
```

You can also use the `sentry_singletenant` and `sentry_region` context values in
your feature conditions as required.

If this happens, make sure to rerun the options deployment once all environments have been updated to ensure your feature is active.

## Testing a Flagpole feature locally

You can test a flagpole feature flag locally using the GetSentry devserver.
Because the feature handler for Flagpole only exists in GetSentry, it's not possible
to test flagpole features using the Sentry devserver at this time.

Start by creating a new yaml file containing your feature config. The config
file should contain a top-level `options` object containing your feature object,
and a `flagpole.flagpole_only_features` list option containing the name of the
feature you want to test, without the `feature.` prefix:
Start by creating a new yaml file containing your feature config:
```yaml
options:
'feature.organizations:is_sentry':
created_at: '2024-06-01T00:00:00.000000'
enabled: false
Expand Down
63 changes: 34 additions & 29 deletions develop-docs/application/feature-flags/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ They're declared on the `FeatureManager` like so:
```python
# pass FeatureHandlerStrategy.FLAGPOLE to use our options-backed feature flagging system:
manager.add("organizations:onboarding", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE)

# pass FeatureHandlerStrategy.INTERNAL if you don't plan to use options automator:
manager.add("organizations:onboarding", OrganizationFeature, FeatureHandlerStrategy.INTERNAL)

# [DEPRECATED] pass FeatureHandlerStrategy.OPTIONS to use options automator:
manager.add("organizations:onboarding", OrganizationFeature, FeatureHandlerStrategy.OPTIONS)
```
Expand All @@ -43,7 +45,7 @@ to remove later

Features can be scoped by organization, and projects. If you're not confident
you want a project feature, create an organization level one. In this example
we'll build a feature called `test-feature` scoped at the _organization_ level.
we'll build a feature called `test-feature` scoped at the _organization_ level.

### Determine the permanency of your feature

Expand All @@ -57,22 +59,27 @@ Most Sentry feature flags are placed in `temporary.py`, while permanent Sentry f

GetSentry only flags are typically placed in [`features.py`](https://github.com/getsentry/getsentry/blob/master/getsentry/features.py).

### Determine whether your feature needs to be exposed in the API

If you plan on using your feature in the frontend, you need to set
`api_expose=True` when adding your feature. Features that have `api_expose` will
be included in the results of the organization details response.

### Add your feature to the FeatureManager

If you want to back your feature flag via options, you can do so using the [Flagpole](/feature-flags/flagpole/) library
If you want to back your feature flag via options, you can do so using the [Flagpole](/application/feature-flags/flagpole/) library
by adding the feature to the `FeatureManager` with the `FLAGPOLE` enum set as the feature strategy:

```python
default_manager.add('organizations:test-feature', OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE)
default_manager.add('organizations:test-feature', OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
```

_Note:_ It used to be required to add a new feature's name to `server.py` in Sentry in order to set a default value, but this
is no longer required. Instead, the `manager.add()` method takes a default value, or automatically sets the value
to `False` if no default is provided.
When defining a feature you can also set the default state of the feature flag.
If no default is provided, `False` will be used.

```python
# Example of a feature set with a default value of True
default_manager.add('organizations:test-feature', OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, default=True)
default_manager.add('organizations:test-feature', OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, default=True, api_expose=True)
```

If you don't plan to use Flagpole, use `FeatureHandlerStrategy.INTERNAL` with a custom feature handler instead, for example:
Expand All @@ -85,7 +92,7 @@ default_manager.add('organizations:test-feature', OrganizationFeature, FeatureHa

The Organization model serializer
(`src/sentry/api/serializers/models/organization.py`) builds a list
called `feature_list` that is given to the front-end to use. By default, all
called `feature_list` that is given to the front-end to use. By default, all
organization features are checked and those that are present are added into the list. If
your feature requires additional custom logic, you will have to update the
organization serializer to check and include it manually.
Expand All @@ -94,7 +101,7 @@ organization serializer to check and include it manually.

Sometimes a flag on the model is used to indicate a feature flag as shown
below. This is not recommended unless there is a specific reason to make
changes to the model. For example, the `require_2fa` flag affects behavior on
changes to the model. For example, the `require_2fa` flag affects behavior on
the backend to enforce two-factor authentication.

```python
Expand All @@ -112,9 +119,9 @@ if getattr(obj.flags, 'require_2fa'):

### In Python code

The FeatureManager's `has` method checks see if the feature exists. The `has`
The FeatureManager's `has` method checks see if the feature exists. The `has`
method takes in the feature's name, the objects that correspond to the scope of
the feature *(i.e. an organization for an organization level feature or
the feature *(i.e. an organization for an organization level feature or
a project for a project level feature)*, and the actor (aka user). Here's an example
Organization feature check:

Expand All @@ -126,13 +133,12 @@ if features.has('organizations:test-feature', obj, actor=user):
The example code only adds the feature to the `feature_list` if that feature is enabled for
the organization and the type of user given. Note that when we give the feature
to the frontend, we remove the scope prefix, and
our `'organizations:test-feature'` becomes `'test-feature'`.
our `'organizations:test-feature'` becomes `'test-feature'`.

### In JavaScript

There is a difference between using the flag in Sentry and in GetSentry. At this
stage you're not quite ready to use your feature flag in GetSentry, but you are
able to use it inside Sentry.
In order to check a feature flag in JavaScript, your feature flag will need
`api_expose=True`.

### Declarative features with the Feature component

Expand Down Expand Up @@ -166,9 +172,9 @@ organization.features.includes('test-feature'); // evals to True/False

## Enabling features in development

In Sentry you can run `sentry devserver` to view your changes in development
In Sentry you can run `sentry devserver` to view your changes in development
mode. If you would like to view a change behind a feature flag, you will need to
open the file `~/.sentry/sentry.conf.py` on your local machine. This file
open the file `~/.sentry/sentry.conf.py` on your local machine. This file
contains your local settings for the sentry application, and can be viewed and
edited. If you would like to toggle a flag on or off, add this to your
configuration file:
Expand All @@ -177,9 +183,8 @@ configuration file:
SENTRY_FEATURES['organizations:test-feature'] = True
```


Alternatively, you can test Flagpole features by setting custom options locally.
See the [Flagpole Local Development](/feature-flags/flagpole/#testing-a-flagpole-feature-locally) docs for more information on this.
See the [Flagpole Local Development](/application/feature-flags/flagpole/#testing-a-flagpole-feature-locally) docs for more information on this.

## Enabling your feature in production

Expand All @@ -188,14 +193,14 @@ flags are then configured via `sentry.conf.py`. For Sentry's SaaS deployment,
you have the choice of using an option backed rollout via Options Automator with Flagpole,
or by writing a custom feature flag handler.

- [Flagpole](/feature-flags/flagpole/) is Sentry's internal feature flagging library, allowing a feature
with multiple target segments and condition filters to be defined in YAML within Options Automator.
- [Flagpole](/application/feature-flags/flagpole/) is Sentry's internal feature flagging library, allowing a feature
with multiple target segments and condition filters to be defined in YAML within Options Automator.

- [Options based features](options-backed-features/) [DEPRECATED] allow a feature
to be rolled out to a specific subset of LA orgs, a percentage of EA orgs,
and/or a percentage of all orgs. These can be used in high scale situations, and are generally
preferred over customer feature handlers. This strategy predates Flagpole, which is the new standard
way to define an option-backed feature flag.
- [Options based features](/application/feature-flags/options-backed-features/) [DEPRECATED] allow a feature
to be rolled out to a specific subset of LA orgs, a percentage of EA orgs,
and/or a percentage of all orgs. These can be used in high scale situations, and are generally
preferred over customer feature handlers. This strategy predates Flagpole, which is the new standard
way to define an option-backed feature flag.

## After launch (Graduation)

Expand All @@ -209,9 +214,9 @@ sentry.io, you have a few potential paths:
options-automator repositories.
- If the feature will only be available to SaaS customers on specific plans, you
need to add your feature flag to the appropriate plans and update feature
handlers (see below).You should also enable the feature by default in
[`conf/server.py`](https://github.com/getsentry/sentry/blob/master/src/sentry/conf/server.py)
in sentry to ensure that the feature is available for self-hosted deployments.
handlers (see below). You should also move the feature flag from
`temporary.py` to `permanent.py` and update the default value to enable the
feature in self-hosted instances.

## Getsentry feature handlers

Expand Down

0 comments on commit 3576a45

Please sign in to comment.