Skip to content

Commit 61eb4e3

Browse files
committed
move to platform flags
1 parent 77c2576 commit 61eb4e3

File tree

9 files changed

+88
-107
lines changed

9 files changed

+88
-107
lines changed

poetry.lock

Lines changed: 32 additions & 32 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ cryptography = ">=42,<43"
4747
kubernetes = "26.1.*"
4848
podman = "5.4.*"
4949
rq-scheduler = "^0.10"
50-
django-ansible-base = { git = "https://github.com/ansible/django-ansible-base.git", tag = "2025.5.8", extras = [
50+
django-ansible-base = { git = "https://github.com/zkayyali812/django-ansible-base.git", branch = "phase2/feature-flags/poc", extras = [
5151
"channel-auth",
5252
"rbac",
5353
"redis-client",

src/aap_eda/api/resource_api.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
14+
from ansible_base.feature_flags.models import AAPFlag
1515
from ansible_base.resource_registry.registry import (
1616
ParentResource,
1717
ResourceConfig,
1818
ServiceAPIConfig,
1919
SharedResource,
2020
)
2121
from ansible_base.resource_registry.shared_types import (
22+
FeatureFlagType,
2223
OrganizationType,
2324
TeamType,
2425
UserType,
@@ -52,4 +53,10 @@ class APIConfig(ServiceAPIConfig):
5253
serializer=OrganizationType, is_provider=False
5354
),
5455
),
56+
ResourceConfig(
57+
AAPFlag,
58+
shared_resource=SharedResource(
59+
serializer=FeatureFlagType, is_provider=False
60+
),
61+
),
5562
)

src/aap_eda/settings/core.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,9 @@
1919
DISPATCHERD_FEATURE_FLAG_NAME = "FEATURE_DISPATCHERD_ENABLED"
2020
ANALYTICS_FEATURE_FLAG_NAME = "FEATURE_EDA_ANALYTICS_ENABLED"
2121

22-
FLAGS = {
23-
ANALYTICS_FEATURE_FLAG_NAME: [
24-
{
25-
"condition": "boolean",
26-
"value": False,
27-
},
28-
],
29-
DISPATCHERD_FEATURE_FLAG_NAME: [
30-
{
31-
"condition": "boolean",
32-
"value": False,
33-
},
34-
],
35-
}
3622

3723
INSTALLED_APPS = [
3824
"daphne",
39-
"flags",
4025
# Django apps
4126
"django.contrib.auth",
4227
"django.contrib.contenttypes",

src/aap_eda/settings/default.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
load_dab_settings,
2020
load_envvars,
2121
load_standard_settings_files,
22-
toggle_feature_flags,
2322
)
2423

2524
from .post_load import post_loading
@@ -52,13 +51,4 @@
5251
post_loading(DYNACONF)
5352
load_dab_settings(DYNACONF)
5453

55-
# toggle feature flags, considering flags coming from
56-
# /etc/ansible-automation-platform/*.yaml
57-
# and envvars like `EDA_FEATURE_FOO_ENABLED=true
58-
DYNACONF.update(
59-
toggle_feature_flags(DYNACONF),
60-
loader_identifier="settings:toggle_feature_flags",
61-
merge=True,
62-
)
63-
6454
export(__name__, DYNACONF) # export back to django.conf.settings

tests/conftest.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
import logging
1616

1717
import pytest
18+
from ansible_base.feature_flags.utils import (
19+
create_initial_data as seed_feature_flags,
20+
)
1821
from django.conf import settings
1922

2023
from aap_eda.core import enums, models
@@ -91,6 +94,11 @@ def aap_credential_type(preseed_credential_types):
9194
)
9295

9396

97+
@pytest.fixture
98+
def preseed_feature_flags():
99+
seed_feature_flags()
100+
101+
94102
#################################################################
95103
# Redis
96104
#################################################################
Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,31 @@
11
import pytest
2-
from ansible_base.lib.dynamic_config import toggle_feature_flags
2+
from ansible_base.feature_flags.models import AAPFlag
3+
from ansible_base.feature_flags.utils import (
4+
create_initial_data as seed_feature_flags,
5+
)
36
from django.conf import settings
4-
from django.test import override_settings
5-
from flags.state import flag_state
7+
from flags.state import flag_state, get_flags
68
from rest_framework import status
79

810
from tests.integration.constants import api_url_v1
911

1012

1113
@pytest.mark.django_db
12-
def test_feature_flags_list_endpoint(admin_client):
14+
def test_feature_flags_list_endpoint(admin_client, preseed_feature_flags):
1315
response = admin_client.get(f"{api_url_v1}/feature_flags_state/")
1416
assert response.status_code == status.HTTP_200_OK, response.data
1517
# Validates expected default feature flags
1618
# Modify each time a flag is added to default settings
17-
assert len(response.data) == 2
19+
assert len(response.data) == len(get_flags())
1820
assert response.data[settings.ANALYTICS_FEATURE_FLAG_NAME] is False
1921
assert response.data[settings.DISPATCHERD_FEATURE_FLAG_NAME] is False
2022

2123

22-
@override_settings(
23-
FLAGS={
24-
"FEATURE_SOME_PLATFORM_FLAG_ENABLED": [
25-
{"condition": "boolean", "value": False},
26-
],
27-
},
28-
FEATURE_SOME_PLATFORM_FLAG_ENABLED=True,
29-
)
24+
@pytest.mark.parametrize("flag_value", [True, False])
3025
@pytest.mark.django_db
31-
def test_feature_flags_toggle():
32-
settings_override = {
33-
"FLAGS": settings.FLAGS,
34-
"FEATURE_SOME_PLATFORM_FLAG_ENABLED": settings.FEATURE_SOME_PLATFORM_FLAG_ENABLED, # noqa: E501
35-
}
36-
assert toggle_feature_flags(settings_override) == {
37-
"FLAGS__FEATURE_SOME_PLATFORM_FLAG_ENABLED": [
38-
{"condition": "boolean", "value": True},
39-
]
40-
}
41-
assert flag_state("FEATURE_SOME_PLATFORM_FLAG_ENABLED") is True
26+
def test_feature_flags_toggle(flag_value):
27+
flag_name = "FEATURE_EDA_ANALYTICS_ENABLED"
28+
setattr(settings, flag_name, flag_value)
29+
AAPFlag.objects.all().delete()
30+
seed_feature_flags()
31+
assert flag_state(flag_name) is flag_value

tests/integration/api/test_root.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@
8383
"/organizations/",
8484
"/teams/",
8585
"/event-streams/",
86+
"feature_flags/states/",
87+
# To be removed after all components
88+
# have migrated away from this endpoint
8689
"/feature_flags_state/",
8790
],
8891
False,

tests/unit/test_features.py

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
"""Unit tests for feature flags functionality."""
1616

1717
import pytest
18+
from ansible_base.feature_flags.models import AAPFlag
19+
from ansible_base.feature_flags.utils import (
20+
create_initial_data as seed_feature_flags,
21+
)
1822

1923
from aap_eda.settings import features
2024
from aap_eda.settings.features import _get_feature
@@ -29,14 +33,10 @@ def clear_feature_cache():
2933
@pytest.mark.django_db
3034
def test_get_feature_flag(settings):
3135
"""Test getting feature flag values."""
32-
settings.FLAGS = {
33-
settings.DISPATCHERD_FEATURE_FLAG_NAME: [
34-
{"condition": "boolean", "value": True}
35-
],
36-
settings.ANALYTICS_FEATURE_FLAG_NAME: [
37-
{"condition": "boolean", "value": False}
38-
],
39-
}
36+
AAPFlag.objects.all().delete()
37+
setattr(settings, settings.DISPATCHERD_FEATURE_FLAG_NAME, True)
38+
setattr(settings, settings.ANALYTICS_FEATURE_FLAG_NAME, False)
39+
seed_feature_flags()
4040

4141
assert features.DISPATCHERD is True
4242
assert features.ANALYTICS is False
@@ -45,40 +45,38 @@ def test_get_feature_flag(settings):
4545
@pytest.mark.django_db
4646
def test_feature_flag_caching(settings):
4747
"""Test that feature flag values are properly cached."""
48-
settings.FLAGS = {
49-
settings.DISPATCHERD_FEATURE_FLAG_NAME: [
50-
{"condition": "boolean", "value": True}
51-
]
52-
}
53-
48+
AAPFlag.objects.all().delete()
49+
setattr(settings, settings.DISPATCHERD_FEATURE_FLAG_NAME, True)
50+
seed_feature_flags()
5451
# First access - should cache the value
5552
assert features.DISPATCHERD is True
5653

5754
# Change the underlying flag value
58-
settings.FLAGS[settings.DISPATCHERD_FEATURE_FLAG_NAME][0]["value"] = False
59-
55+
setattr(settings, settings.DISPATCHERD_FEATURE_FLAG_NAME, False)
56+
seed_feature_flags()
6057
# Should still get the cached value
6158
assert features.DISPATCHERD is True
6259

6360

6461
@pytest.mark.django_db
6562
def test_cache_invalidation(settings):
6663
"""Test that cache invalidation works as expected."""
67-
settings.FLAGS = {
68-
settings.DISPATCHERD_FEATURE_FLAG_NAME: [
69-
{"condition": "boolean", "value": True}
70-
]
71-
}
64+
AAPFlag.objects.all().delete()
65+
setattr(settings, settings.DISPATCHERD_FEATURE_FLAG_NAME, True)
66+
seed_feature_flags()
7267

7368
# Populate cache
7469
assert features.DISPATCHERD is True
7570

7671
# Change the flag value and clear cache
77-
settings.FLAGS[settings.DISPATCHERD_FEATURE_FLAG_NAME][0]["value"] = False
72+
setattr(settings, settings.DISPATCHERD_FEATURE_FLAG_NAME, False)
73+
seed_feature_flags()
7874
_get_feature.cache_clear()
7975

80-
# Should get the new value after cache clear
81-
assert features.DISPATCHERD is False
76+
# Feature should remain true.
77+
# If runtime toggle, we should only be able to
78+
# update the value after toggling it via the platform gateway
79+
assert features.DISPATCHERD is True
8280

8381

8482
@pytest.mark.django_db

0 commit comments

Comments
 (0)