From 006ade3086665fade9bbd3d1124ec3e3a01a3391 Mon Sep 17 00:00:00 2001 From: Rob Hudson Date: Thu, 19 Sep 2024 16:28:14 -0700 Subject: [PATCH 1/2] =?UTF-8?q?Import=20config=20waffles=20to=20django=20w?= =?UTF-8?q?affles=20=F0=9F=A7=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../commands/import_waffle_switches.py | 29 ++++++ bedrock/firefox/firefox_details.py | 27 +----- bedrock/firefox/templatetags/helpers.py | 25 +---- bedrock/firefox/tests/test_firefox_details.py | 86 ----------------- bedrock/mozorg/context_processors.py | 17 ---- .../mozorg/tests/test_context_processors.py | 43 --------- bedrock/mozorg/tests/test_views.py | 21 ---- bedrock/settings/base.py | 10 +- docs/funnelcake.rst | 96 ------------------- docs/index.rst | 1 - requirements/dev.txt | 5 + requirements/prod.in | 11 ++- requirements/prod.txt | 5 + 13 files changed, 51 insertions(+), 325 deletions(-) create mode 100644 bedrock/base/management/commands/import_waffle_switches.py delete mode 100644 bedrock/mozorg/tests/test_context_processors.py delete mode 100644 docs/funnelcake.rst diff --git a/bedrock/base/management/commands/import_waffle_switches.py b/bedrock/base/management/commands/import_waffle_switches.py new file mode 100644 index 00000000000..4e0260461f5 --- /dev/null +++ b/bedrock/base/management/commands/import_waffle_switches.py @@ -0,0 +1,29 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +from django.core.management.base import BaseCommand + +from waffle.models import Switch + +from bedrock.base.models import ConfigValue +from bedrock.utils.management.decorators import alert_sentry_on_exception + + +@alert_sentry_on_exception +class Command(BaseCommand): + def handle(self, *args, **options): + prefix = "SWITCH_" + + for config in ConfigValue.objects.all(): + # Ignore funnelcakes and other yummy things. + if not config.name.startswith(prefix): + continue + + # Remove prefix. + name = config.name[len(prefix) :] + # Set active to boolean. + active = config.value == "on" + + switch, created = Switch.objects.update_or_create(name=name, defaults={"active": active}) + print(f"{'Created new' if created else 'Updated'} switch: {name} = {'on' if active else 'off'}") diff --git a/bedrock/firefox/firefox_details.py b/bedrock/firefox/firefox_details.py index 35c6063e73b..7047339b58e 100644 --- a/bedrock/firefox/firefox_details.py +++ b/bedrock/firefox/firefox_details.py @@ -2,8 +2,6 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. -# coding=utf-8 - import re from collections import OrderedDict from operator import itemgetter @@ -11,11 +9,8 @@ from django.conf import settings -from everett.manager import ListOf from product_details import ProductDetails -from bedrock.base.waffle import config - # TODO: port this to django-mozilla-product-details class _ProductDetails(ProductDetails): @@ -225,8 +220,6 @@ def get_download_url( locale, force_direct=False, force_full_installer=False, - force_funnelcake=False, - funnelcake_id=None, locale_in_transition=False, ): """ @@ -239,9 +232,6 @@ def get_download_url( always True for non-release URLs. :param force_full_installer: Force the installer download to not be the stub installer (for aurora). - :param force_funnelcake: Force the download version for en-US Windows to be - 'latest', which bouncer will translate to the funnelcake build. - :param funnelcake_id: ID for the the funnelcake build. :param locale_in_transition: Include the locale in the transition URL :return: string url """ @@ -253,7 +243,6 @@ def get_download_url( force_direct = True if channel != "release" else force_direct stub_platforms = ["win", "win64"] esr_channels = ["esr", "esr_next"] - include_funnelcake_param = False # support optional MSI installer downloads # bug 1493205 @@ -261,21 +250,11 @@ def get_download_url( if is_msi: platform = platform[:-4] - # Bug 1345467 - Only allow specifically configured funnelcake builds - if funnelcake_id: - fc_platforms = config(f"FUNNELCAKE_{funnelcake_id}_PLATFORMS", default="", parser=ListOf(str)) - fc_locales = config(f"FUNNELCAKE_{funnelcake_id}_LOCALES", default="", parser=ListOf(str)) - include_funnelcake_param = platform in fc_platforms and _locale in fc_locales - # Check if direct download link has been requested # if not just use transition URL if not force_direct: # build a link to the transition page transition_url = self.download_base_url_transition - if funnelcake_id: - # include funnelcake in scene 2 URL - transition_url += f"?f={funnelcake_id}" - if locale_in_transition: transition_url = f"/{locale}{transition_url}" @@ -294,11 +273,7 @@ def get_download_url( prod_name = "firefox-esr-next" elif platform in stub_platforms and not is_msi and not force_full_installer: # Use the stub installer for approved platforms - # append funnelcake id to version if we have one - if include_funnelcake_param: - suffix = f"stub-f{funnelcake_id}" - else: - suffix = "stub" + suffix = "stub" elif channel == "nightly" and locale != "en-US": # Nightly uses a different product name for localized builds, # and is the only one ಠ_ಠ diff --git a/bedrock/firefox/templatetags/helpers.py b/bedrock/firefox/templatetags/helpers.py index e32406bbf12..24e28c73a3c 100644 --- a/bedrock/firefox/templatetags/helpers.py +++ b/bedrock/firefox/templatetags/helpers.py @@ -24,8 +24,6 @@ def desktop_builds( locale=None, force_direct=False, force_full_installer=False, - force_funnelcake=False, - funnelcake_id=False, locale_in_transition=False, classified=False, ): @@ -65,8 +63,6 @@ def desktop_builds( locale, force_direct=force_direct, force_full_installer=force_full_installer, - force_funnelcake=force_funnelcake, - funnelcake_id=funnelcake_id, locale_in_transition=locale_in_transition, ) @@ -83,8 +79,6 @@ def desktop_builds( locale, force_direct=True, force_full_installer=force_full_installer, - force_funnelcake=force_funnelcake, - funnelcake_id=funnelcake_id, ) if download_link_direct == download_link: download_link_direct = False @@ -120,7 +114,6 @@ def download_firefox( locale=None, force_direct=False, force_full_installer=False, - force_funnelcake=False, alt_copy=None, button_class="mzp-t-xl", locale_in_transition=False, @@ -136,8 +129,6 @@ def download_firefox( :param force_direct: Force the download URL to be direct. :param force_full_installer: Force the installer download to not be the stub installer (for aurora). - :param force_funnelcake: Force the download version for en-US Windows to be - 'latest', which bouncer will translate to the funnelcake build. :param alt_copy: Specifies alternate copy to use for download buttons. :param button_class: Classes to add to the download button, contains size mzp-t-xl by default :param locale_in_transition: Include the page locale in transitional download link. @@ -149,7 +140,6 @@ def download_firefox( show_ios = platform in ["all", "ios"] alt_channel = "" if channel == "release" else channel locale = locale or get_locale(ctx["request"]) - funnelcake_id = ctx.get("funnelcake_id", False) dom_id = dom_id or f"download-button-{'desktop' if platform == 'all' else platform}-{channel}" # Gather data about the build for each platform @@ -157,7 +147,7 @@ def download_firefox( if show_desktop: version = firefox_desktop.latest_version(channel) - builds = desktop_builds(channel, builds, locale, force_direct, force_full_installer, force_funnelcake, funnelcake_id, locale_in_transition) + builds = desktop_builds(channel, builds, locale, force_direct, force_full_installer, locale_in_transition) if show_android: version = firefox_android.latest_version(channel) @@ -208,16 +198,10 @@ def download_firefox_thanks(ctx, dom_id=None, locale=None, alt_copy=None, button channel = "release" locale = locale or get_locale(ctx["request"]) - funnelcake_id = ctx.get("funnelcake_id", False) dom_id = dom_id or "download-button-thanks" transition_url = "/firefox/download/thanks/" version = firefox_desktop.latest_version(channel) - # if there's a funnelcake param in the page URL e.g. ?f=123 - if funnelcake_id: - # include param in transitional URL e.g. /firefox/download/thanks/?f=123 - transition_url += f"?f={funnelcake_id}" - if locale_in_transition: transition_url = f"/{locale}{transition_url}" @@ -228,8 +212,6 @@ def download_firefox_thanks(ctx, dom_id=None, locale=None, alt_copy=None, button locale, force_direct=True, force_full_installer=False, - force_funnelcake=False, - funnelcake_id=funnelcake_id, ) data = { @@ -262,8 +244,9 @@ def download_firefox_desktop_list(ctx, channel="release", dom_id=None, locale=No dom_id = dom_id or f"download-platform-list-{channel}" locale = locale or get_locale(ctx["request"]) - # Make sure funnelcake_id is not passed as builds are often Windows only. - builds = desktop_builds(channel, None, locale, True, force_full_installer, False, False, False, True) + builds = desktop_builds( + channel, builds=None, locale=locale, force_direct=True, force_full_installer=force_full_installer, locale_in_transition=False, classified=True + ) recommended_builds = [] traditional_builds = [] diff --git a/bedrock/firefox/tests/test_firefox_details.py b/bedrock/firefox/tests/test_firefox_details.py index 14c3d14ee7d..43d53ae71ec 100644 --- a/bedrock/firefox/tests/test_firefox_details.py +++ b/bedrock/firefox/tests/test_firefox_details.py @@ -655,28 +655,6 @@ def test_get_download_url_nightly_l10n(self): ], ) - def test_get_download_url_scene2_funnelcake(self): - scene2 = self.firefox_desktop.download_base_url_transition - url = self.firefox_desktop.get_download_url("release", "45.0", "win", "en-US") - self.assertEqual(url, scene2) - url = self.firefox_desktop.get_download_url("release", "45.0", "win", "en-US", funnelcake_id="64") - self.assertEqual(url, scene2 + "?f=64") - - def test_get_download_url_scene2_with_locale(self): - scene2 = self.firefox_desktop.download_base_url_transition - url = self.firefox_desktop.get_download_url("release", "45.0", "win", "de", locale_in_transition=True) - self.assertEqual(url, "/de" + scene2) - - url = self.firefox_desktop.get_download_url( - "release", - "45.0", - "win", - "fr", - locale_in_transition=True, - funnelcake_id="64", - ) - self.assertEqual(url, "/fr" + scene2 + "?f=64") - def get_download_url_ssl(self): """ SSL-enabled links should always be used except Windows stub installers. @@ -810,70 +788,6 @@ def test_latest_major_version_no_int(self): ): assert self.firefox_desktop.latest_major_version("release") == 0 - @patch.dict(os.environ, FUNNELCAKE_64_LOCALES="en-US", FUNNELCAKE_64_PLATFORMS="win") - def test_funnelcake_direct_links_en_us_win_only(self): - """ - Ensure funnelcake params are included for Windows en-US builds only. - """ - url = self.firefox_desktop.get_download_url("release", "45.0", "win", "en-US", force_direct=True, funnelcake_id="64") - assert "product=firefox-stub-f64" in url - - url = self.firefox_desktop.get_download_url("release", "45.0", "win64", "en-US", force_direct=True, funnelcake_id="64") - assert "product=firefox-stub-f64" not in url - - url = self.firefox_desktop.get_download_url("release", "45.0", "win", "de", force_direct=True, funnelcake_id="64") - assert "product=firefox-stub-f64" not in url - - url = self.firefox_desktop.get_download_url("release", "45.0", "osx", "en-US", force_direct=True, funnelcake_id="64") - assert "product=firefox-stub-f64" not in url - - def test_no_funnelcake_direct_links_if_not_configured(self): - """ - Ensure funnelcake params are included for Linux and OSX en-US builds only. - """ - url = self.firefox_desktop.get_download_url("release", "45.0", "win", "en-US", force_direct=True, funnelcake_id="64") - assert "-f64" not in url - - url = self.firefox_desktop.get_download_url( - "release", - "45.0", - "win", - "en-US", - force_direct=True, - force_full_installer=True, - funnelcake_id="64", - ) - assert "-f64" not in url - - url = self.firefox_desktop.get_download_url( - "release", - "45.0", - "win", - "en-US", - force_direct=True, - force_funnelcake=True, - funnelcake_id="64", - ) - assert "-f64" not in url - - url = self.firefox_desktop.get_download_url("release", "45.0", "osx", "de", force_direct=True, funnelcake_id="64") - assert "-f64" not in url - - url = self.firefox_desktop.get_download_url("release", "45.0", "osx", "en-US", force_direct=True, funnelcake_id="64") - assert "-f64" not in url - - url = self.firefox_desktop.get_download_url("release", "45.0", "osx", "fr", force_direct=True, funnelcake_id="64") - assert "-f64" not in url - - url = self.firefox_desktop.get_download_url("release", "45.0", "linux", "de", force_direct=True, funnelcake_id="64") - assert "-f64" not in url - - url = self.firefox_desktop.get_download_url("release", "45.0", "linux", "en-US", force_direct=True, funnelcake_id="64") - assert "-f64" not in url - - url = self.firefox_desktop.get_download_url("release", "45.0", "linux", "fr", force_direct=True, funnelcake_id="64") - assert "-f64" not in url - def test_stub_installer_win_only(self): """ Ensure that builds not in the setting don't get stub. diff --git a/bedrock/mozorg/context_processors.py b/bedrock/mozorg/context_processors.py index 2604f75c48a..d55dad26f86 100644 --- a/bedrock/mozorg/context_processors.py +++ b/bedrock/mozorg/context_processors.py @@ -6,7 +6,6 @@ from datetime import datetime from django.conf import settings -from django.urls import reverse # match 1 - 4 digits only FC_RE = re.compile(r"^\d{1,4}$") @@ -26,21 +25,5 @@ def current_year(request): return {"current_year": datetime.today().year} -def funnelcake_param(request): - """If a query param for a funnelcake is sent, add it to the context.""" - fc_id = request.GET.get("f", None) - context = {} - - if fc_id and FC_RE.match(fc_id): - # special case for installer-help page - # bug 933852 - installer_help_url = reverse("firefox.installer-help") - if installer_help_url in request.path_info: - fc_id = str(int(fc_id) + 1) - context["funnelcake_id"] = fc_id - - return context - - def contrib_numbers(request): return settings.CONTRIBUTE_NUMBERS diff --git a/bedrock/mozorg/tests/test_context_processors.py b/bedrock/mozorg/tests/test_context_processors.py deleted file mode 100644 index cc20972b14f..00000000000 --- a/bedrock/mozorg/tests/test_context_processors.py +++ /dev/null @@ -1,43 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -from django.test.client import RequestFactory - -from bedrock.base.urlresolvers import reverse -from bedrock.mozorg.context_processors import funnelcake_param -from bedrock.mozorg.tests import TestCase - - -class TestFunnelcakeParam(TestCase): - def setUp(self): - self.rf = RequestFactory() - - def _funnelcake(self, url="/", **kwargs): - return funnelcake_param(self.rf.get(url, kwargs)) - - def test_funnelcake_param_noop(self): - """Should return an empty dict normally.""" - assert self._funnelcake() == {} - - def test_funnelcake_param_f(self): - """Should inject funnelcake into context.""" - assert self._funnelcake(f="5") == {"funnelcake_id": "5"} - assert self._funnelcake(f="234") == {"funnelcake_id": "234"} - - def test_funnelcake_param_bad(self): - """Should not inject bad funnelcake into context.""" - assert self._funnelcake(f="5dude") == {} - assert self._funnelcake(f="123456") == {} - - def test_funnelcake_param_increment_installer_help(self): - """FC param should be +1 on the firefox/installer-help/ page. - - Bug 933852. - """ - url = reverse("firefox.installer-help") - ctx = self._funnelcake(url, f="20") - assert ctx["funnelcake_id"] == "21" - - ctx = self._funnelcake(url, f="10") - assert ctx["funnelcake_id"] == "11" diff --git a/bedrock/mozorg/tests/test_views.py b/bedrock/mozorg/tests/test_views.py index 51d31eddba3..71331430cd1 100644 --- a/bedrock/mozorg/tests/test_views.py +++ b/bedrock/mozorg/tests/test_views.py @@ -3,7 +3,6 @@ # file, You can obtain one at https://mozilla.org/MPL/2.0/. import json -import os from unittest.mock import Mock, patch from django.core import mail @@ -19,26 +18,6 @@ from bedrock.mozorg.tests import TestCase -class TestViews(TestCase): - @patch.dict(os.environ, FUNNELCAKE_5_LOCALES="en-US", FUNNELCAKE_5_PLATFORMS="win") - def test_download_button_funnelcake(self): - """The download button should have the funnelcake ID.""" - with self.activate_locale("en-US"): - resp = self.client.get(reverse("firefox.download.thanks"), {"f": "5"}) - assert b"product=firefox-stub-f5&" in resp.content - - def test_download_button_bad_funnelcake(self): - """The download button should not have a bad funnelcake ID.""" - with self.activate_locale("en-US"): - resp = self.client.get(reverse("firefox.download.thanks"), {"f": "5dude"}) - assert b"product=firefox-stub&" in resp.content - assert b"product=firefox-stub-f5dude&" not in resp.content - - resp = self.client.get(reverse("firefox.download.thanks"), {"f": "999999999"}) - assert b"product=firefox-stub&" in resp.content - assert b"product=firefox-stub-f999999999&" not in resp.content - - class TestRobots(TestCase): def setUp(self): self.rf = RequestFactory() diff --git a/bedrock/settings/base.py b/bedrock/settings/base.py index 6ab551876c3..3a2a2e4e4d7 100644 --- a/bedrock/settings/base.py +++ b/bedrock/settings/base.py @@ -742,6 +742,7 @@ def get_app_name(hostname): # third-party apps "django_jinja_markdown", "django_jinja", + "waffle", "watchman", # Wagtail CMS and related, necessary apps "wagtail.contrib.redirects", @@ -860,7 +861,6 @@ def _is_bedrock_custom_app(app_name): "bedrock.mozorg.context_processors.canonical_path", "bedrock.mozorg.context_processors.contrib_numbers", "bedrock.mozorg.context_processors.current_year", - "bedrock.mozorg.context_processors.funnelcake_param", "bedrock.firefox.context_processors.latest_firefox_versions", ], "extensions": [ @@ -1270,14 +1270,6 @@ def before_send(event, hint): # Django-CSP settings are in settings/__init__.py, where they are # set according to site mode -# Bug 1345467: Funnelcakes are now explicitly configured in the environment. -# Set experiment specific variables like the following: -# -# FUNNELCAKE_103_PLATFORMS=win,win64 -# FUNNELCAKE_103_LOCALES=de,fr,en-US -# -# where "103" in the variable name is the funnelcake ID. - # Countries that need to see cookie banner # See https://www.gov.uk/eu-eea diff --git a/docs/funnelcake.rst b/docs/funnelcake.rst deleted file mode 100644 index f16a67a6ccf..00000000000 --- a/docs/funnelcake.rst +++ /dev/null @@ -1,96 +0,0 @@ -.. This Source Code Form is subject to the terms of the Mozilla Public -.. License, v. 2.0. If a copy of the MPL was not distributed with this -.. file, You can obtain one at https://mozilla.org/MPL/2.0/. - -.. _funnelcake: - -=============================== -Funnel cakes and Partner Builds -=============================== - -Funnel cakes ------------- - -In addition to being `an American delicacy `_ -funnel cakes are what we call special builds of Firefox. They can come with -extensions preinstalled and/or a custom first-run experience. - - "The whole funnelcake system is so marred by history at this point I don't - know if anyone fully understands what it's supposed to do in all situations" - - pmac - -Funnelcakes are configured by the Release Engineering team. You can see the -configs in the `funnelcake git repo `_ - -Currently bedrock only supports funnelcakes for "stub installer platforms". Which -means they are windows only. However, funnelcakes can be made for all platforms -so `bedrock support may expand `_. - -We signal to bedrock that we want a funnelcake when linking to the download -page by appending the query variable `f` with a value equal to the funnelcake -number being requested. - -.. code-block:: text - - https://www.mozilla.org/en-US/firefox/download/thanks/?f=137 - -Bedrock checks to see if the funnelcake is configured (this is handled in the -`www-config repo `_) - -.. code-block:: bash - - FUNNELCAKE_135_LOCALES=en-US - FUNNELCAKE_135_PLATFORMS=win,win64 - -Bedrock then converts that into a request to download a file like so: - -Windows: - -.. code-block:: text - - https://download.mozilla.org/?product=firefox-stub-f137&os=win&lang=en-US - -Mac (You can see the mac one does not pass the funnelcake number along.): - -.. code-block:: text - - https://download.mozilla.org/?product=firefox-latest-ssl&os=osx&lang=en-US - -Someone in Release Engineering needs to set up the redirects on their side to -take the request from here. - -Places things can go wrong -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -As with many technical things, the biggest potential problems are with people: - -- Does it have executive approval? -- Did legal sign off? -- Has it had a security review? - -On the technical side: - -- Is the switch enabled? -- Is the variable being passed? - -Partner builds --------------- - -Bedrock does not have an automated way of handling these, so you'll have to -craft your own download button: - -.. code-block:: html - - - Download - ------------- - -Bugs that might have useful info: - -- https://bugzilla.mozilla.org/show_bug.cgi?id=1450463 -- https://bugzilla.mozilla.org/show_bug.cgi?id=1495050 - -PRs that might have useful code: - -- https://github.com/mozilla/bedrock/pull/5555 diff --git a/docs/index.rst b/docs/index.rst index 0323e62ec43..8657bb539b2 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -41,7 +41,6 @@ Contents send-to-device download-buttons mozilla-accounts - funnelcake abtest vpn-subscriptions attribution diff --git a/requirements/dev.txt b/requirements/dev.txt index 1ff70d6c82f..1fced800535 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -502,6 +502,7 @@ django==4.2.16 \ # django-storages # django-taggit # django-treebeard + # django-waffle # django-watchman # djangorestframework # laces @@ -588,6 +589,10 @@ django-treebeard==4.7.1 \ # via # -r requirements/prod.txt # wagtail +django-waffle==4.1.0 \ + --hash=sha256:5979a2f3dd674ef7086480525b39651fc2045427f6d8e6a614192656d3402c5b \ + --hash=sha256:e49d7d461d89f3bd8e53f20efe39310acca8f275c9888495e68e195345bf18b1 + # via -r requirements/prod.txt django-watchman==1.3.0 \ --hash=sha256:33b5fc734d689b83cb96fc17beda624ae2955f4cede0856897d990c363eac962 \ --hash=sha256:5f04300bd7fbdd63b8a883b2730ed1e4d9b0f9991133b33a1281134b81f466eb diff --git a/requirements/prod.in b/requirements/prod.in index 16806eb8dbf..8ee9f390b03 100644 --- a/requirements/prod.in +++ b/requirements/prod.in @@ -20,10 +20,11 @@ django-jinja==2.11.0 django-jsonview==2.0.0 django-memoize==2.3.1 django-mozilla-product-details==1.0.3 +django-rq==2.10.2 django-storages[google]==1.14.4 +django-waffle==4.1.0 django-watchman==1.3.0 Django==4.2.16 -django-rq==2.10.2 docutils==0.21.2 envcat==0.1.1 everett==3.3.0 @@ -50,11 +51,11 @@ qrcode==7.4.2 querystringsafe-base64==1.1.1 # Pinned to maintain stub attribution signatures https://github.com/mozilla/bedrock/issues/11156 requests>=2.32.3 # min secure version rich-text-renderer==0.2.8 -sentry-sdk==2.14.0 sentry-processor==0.0.1 +sentry-sdk==2.14.0 supervisor==4.2.5 timeago==1.0.16 -whitenoise==6.7.0 -Wagtail==6.1.3 -wagtail-localize==1.9.1 wagtail-localize-smartling==0.3.0 +wagtail-localize==1.9.1 +Wagtail==6.1.3 +whitenoise==6.7.0 diff --git a/requirements/prod.txt b/requirements/prod.txt index a8d91e599b2..eeb02c1fdb3 100644 --- a/requirements/prod.txt +++ b/requirements/prod.txt @@ -329,6 +329,7 @@ django==4.2.16 \ # django-storages # django-taggit # django-treebeard + # django-waffle # django-watchman # djangorestframework # laces @@ -405,6 +406,10 @@ django-treebeard==4.7.1 \ --hash=sha256:846e462904b437155f76e04907ba4e48480716855f88b898df4122bdcfbd6e98 \ --hash=sha256:995c7120153ab999898fe3043bbdcd8a0fc77cc106eb94de7350e9d02c885135 # via wagtail +django-waffle==4.1.0 \ + --hash=sha256:5979a2f3dd674ef7086480525b39651fc2045427f6d8e6a614192656d3402c5b \ + --hash=sha256:e49d7d461d89f3bd8e53f20efe39310acca8f275c9888495e68e195345bf18b1 + # via -r requirements/prod.in django-watchman==1.3.0 \ --hash=sha256:33b5fc734d689b83cb96fc17beda624ae2955f4cede0856897d990c363eac962 \ --hash=sha256:5f04300bd7fbdd63b8a883b2730ed1e4d9b0f9991133b33a1281134b81f466eb From ba611bd14ffc4f4d6e48f221aea53b58a709524a Mon Sep 17 00:00:00 2001 From: Rob Hudson Date: Mon, 30 Sep 2024 17:41:23 -0700 Subject: [PATCH 2/2] =?UTF-8?q?Move=20waffle=20switches=20to=20django-waff?= =?UTF-8?q?le=20=F0=9F=A7=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../management/commands/update_www_config.py | 131 ------------------ bedrock/base/models.py | 4 - bedrock/base/templatetags/helpers.py | 14 +- bedrock/base/tests/test_waffle.py | 35 ++++- bedrock/base/tests/test_waffle_config.py | 82 ----------- bedrock/base/waffle.py | 37 +++-- bedrock/base/waffle_config.py | 78 ----------- bedrock/firefox/tests/test_base.py | 5 +- bedrock/products/tests/test_views.py | 25 ++-- bedrock/settings/base.py | 6 + bin/export-db-to-sqlite.sh | 2 +- bin/run-db-update.sh | 1 - 12 files changed, 77 insertions(+), 343 deletions(-) delete mode 100644 bedrock/base/management/commands/update_www_config.py delete mode 100644 bedrock/base/tests/test_waffle_config.py delete mode 100644 bedrock/base/waffle_config.py diff --git a/bedrock/base/management/commands/update_www_config.py b/bedrock/base/management/commands/update_www_config.py deleted file mode 100644 index f37e38563f3..00000000000 --- a/bedrock/base/management/commands/update_www_config.py +++ /dev/null @@ -1,131 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -import os - -from django.conf import settings -from django.core.management.base import BaseCommand - -import requests -from envcat import get_env_vars - -from bedrock.base.models import ConfigValue -from bedrock.utils.git import GitRepo -from bedrock.utils.management.decorators import alert_sentry_on_exception - - -def get_config_file_name(app_name=None): - app_name = app_name or settings.APP_NAME or "bedrock-dev" - return os.path.join(settings.WWW_CONFIG_PATH, "waffle_configs", f"{app_name}.env") - - -def get_config_values(): - return get_env_vars(get_config_file_name()) - - -def refresh_db_values(extra=None): - """ - Refresh the database with the values from the config file. - - :param extra: A list of `ConfigValue` objects to add to the database. - :return: The number of configs successfully loaded. - - """ - values = get_config_values() - - ConfigValue.objects.all().delete() - count = 0 - - for name, value in values.items(): - if value: - ConfigValue.objects.create(name=name, value=value) - count += 1 - - if extra: - for obj in extra: - if obj: - ConfigValue.objects.create(name=obj.name, value=obj.value) - count += 1 - - return count - - -@alert_sentry_on_exception -class Command(BaseCommand): - def add_arguments(self, parser): - parser.add_argument("-q", "--quiet", action="store_true", dest="quiet", default=False, help="If no error occurs, swallow all output.") - parser.add_argument("-f", "--force", action="store_true", dest="force", default=False, help="Load the data even if nothing new from git.") - - def output(self, msg): - if not self.quiet: - print(msg) - - def handle(self, *args, **options): - self.quiet = options["quiet"] - repo = GitRepo(settings.WWW_CONFIG_PATH, settings.WWW_CONFIG_REPO, branch_name=settings.WWW_CONFIG_BRANCH, name="WWW Config") - self.output("Updating git repo") - repo.update() - - # Check monitor API and set the waitlist switch as appropriate. - # Note: Doing this here since we want to run this regardless of whether or not there are changes in the config repo. - self.output("Checking monitor API...") - monitor_waitlist_config = self.set_monitor_waitlist() - - if not (options["force"] or repo.has_changes()): - self.output("No config updates") - return - - self.output("Loading configs into database") - count = refresh_db_values(extra=[monitor_waitlist_config]) - - if count: - self.output(f"{count} configs successfully loaded") - else: - self.output("No configs found. Please try again later.") - - repo.set_db_latest() - - self.output("Saved latest git repo state to database") - self.output("Done!") - - def set_monitor_waitlist(self): - # Grab the existing monitor waitlist value so if something fails with the API, we don't lose it. - try: - obj = ConfigValue.objects.get(name=settings.MONITOR_SWITCH_WAITLIST) - except ConfigValue.DoesNotExist: - obj = None - - if not settings.MONITOR_ENDPOINT or not settings.MONITOR_TOKEN: - # Nothing to do, leave everything as-is. - return obj - - resp = requests.get( - settings.MONITOR_ENDPOINT, headers={"Content-Type": "application/json", "Authorization": f"Bearer {settings.MONITOR_TOKEN}"} - ) - if resp.status_code != 200: - self.output(f"Error getting monitor data: {repr(resp)}") - return obj - - monitor_waitlist_value = settings.MONITOR_SWITCH_WAITLIST_DEFAULT - data = resp.json() - if data.get("success") is True and (message := data.get("message")): - if (scans := message.get("scans")) and (subscribers := message.get("subscribers")): - # If either of these are exceeded, we want to turn the waitlist on. - scans_exceeded = scans.get("count") > scans.get("quota") - subscribers_exceeded = subscribers.get("count") > subscribers.get("quota") - if scans_exceeded or subscribers_exceeded: - monitor_waitlist_value = "on" - else: - monitor_waitlist_value = "off" - - if obj and obj.value == monitor_waitlist_value: - # No change, nothing to do. - self.output(f"No change. Monitor waitlist switch is already set to '{monitor_waitlist_value}'") - return obj - - obj, created = ConfigValue.objects.update_or_create(name=settings.MONITOR_SWITCH_WAITLIST, defaults={"value": monitor_waitlist_value}) - self.output(f"{'Created' if created else 'Updated'} switch: {settings.MONITOR_SWITCH_WAITLIST}={monitor_waitlist_value}") - - # Return the `ConfigValue` object so if the repo has changes, we can make sure to persist this record. - return obj diff --git a/bedrock/base/models.py b/bedrock/base/models.py index 706cb59b1aa..ccbabfce362 100644 --- a/bedrock/base/models.py +++ b/bedrock/base/models.py @@ -14,7 +14,3 @@ class Meta: def __str__(self): return f"{self.name}={self.value}" - - -def get_config_dict(): - return {c.name: c.value for c in ConfigValue.objects.all()} diff --git a/bedrock/base/templatetags/helpers.py b/bedrock/base/templatetags/helpers.py index 607e63b57c3..ff94ebb3300 100644 --- a/bedrock/base/templatetags/helpers.py +++ b/bedrock/base/templatetags/helpers.py @@ -28,18 +28,8 @@ @library.global_function @jinja2.pass_context def switch(cxt, name, locales=None): - """A template helper that replaces waffle - - * All calls default to True when DEV setting is True (for the listed locales). - * If the env var is explicitly false it will be false even when DEV = True. - * Otherwise the call is False by default and True is a specific env var exists and is truthy. - - For example: - - {% if switch('dude-and-walter') %} - - would check for an environment variable called `SWITCH_DUDE_AND_WALTER`. The string from the - `switch()` call is converted to uppercase and dashes replaced with underscores. + """ + A template helper around `base.waffle.switch`. See docs there for details. If the `locales` argument is a list of locales then it will only check the switch in those locales, and return False otherwise. The `locales` argument could also contain a "locale group", diff --git a/bedrock/base/tests/test_waffle.py b/bedrock/base/tests/test_waffle.py index 8556368f72b..5f4daf06a74 100644 --- a/bedrock/base/tests/test_waffle.py +++ b/bedrock/base/tests/test_waffle.py @@ -2,14 +2,35 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. -from unittest.mock import patch +from django.test import override_settings -from django.conf import settings +import pytest +from waffle.testutils import override_switch -from bedrock.base import waffle +from bedrock.base.waffle import switch +pytestmark = [ + pytest.mark.django_db, +] -@patch("bedrock.base.waffle.config") -def test_switch_helper(config_mock): - waffle.switch("dude-and-walter") - config_mock.assert_called_with("DUDE_AND_WALTER", namespace="SWITCH", default=str(settings.DEV), parser=bool) + +@override_settings(DEV=True) +def test_switch_helper_dev_true(): + # When no switch exists, we return the value of `settings.DEV`. + assert switch("dude-and-walter") is True + # Then test explicityly set switch values. + with override_switch("DUDE_AND_WALTER", active=True): + assert switch("dude-and-walter") is True + with override_switch("DUDE_AND_WALTER", active=False): + assert switch("dude-and-walter") is False + + +@override_settings(DEV=False) +def test_switch_helper_dev_false(): + # When no switch exists, we return the value of `settings.DEV`. + assert switch("dude-and-walter") is False + # Then test explicityly set switch values. + with override_switch("DUDE_AND_WALTER", active=True): + assert switch("dude-and-walter") is True + with override_switch("DUDE_AND_WALTER", active=False): + assert switch("dude-and-walter") is False diff --git a/bedrock/base/tests/test_waffle_config.py b/bedrock/base/tests/test_waffle_config.py deleted file mode 100644 index 6b5931b4f4d..00000000000 --- a/bedrock/base/tests/test_waffle_config.py +++ /dev/null @@ -1,82 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -from unittest.mock import patch - -from everett.manager import ConfigManager, ConfigurationMissingError - -from bedrock.base import waffle_config -from bedrock.mozorg.tests import TestCase - -GOOD_CONFIG = { - "THE_DUDE": "abides", - "BOWLING": "true", -} - - -@patch.object(waffle_config, "get_config_dict", return_value=GOOD_CONFIG) -class TestConfigDBEnv(TestCase): - def setUp(self): - self.cdbe = waffle_config.ConfigDBEnv() - self.config = ConfigManager([self.cdbe]) - - def test_db_config(self, gcd_mock): - self.assertEqual("abides", self.config("the_dude")) - self.assertEqual("abides", self.config("dude", namespace="the")) - self.assertTrue(self.config("bowling", parser=bool)) - self.assertTrue(self.config("BOWLING", parser=bool)) - with self.assertRaises(ConfigurationMissingError): - self.config("donnie") - - def test_db_config_cache(self, gcd_mock): - self.assertEqual("abides", self.config("the_dude")) - self.assertEqual("abides", self.config("the_dude")) - self.assertEqual(gcd_mock.call_count, 1) - self.cdbe.last_update -= self.cdbe.timeout + 1 - self.assertEqual("abides", self.config("the_dude")) - self.assertEqual("abides", self.config("the_dude")) - self.assertEqual(gcd_mock.call_count, 2) - - -class TestDictOf(TestCase): - def test_dict_of(self): - parser = waffle_config.DictOf(int) - assert parser("dude:1,walter:2,donnie:3") == { - "dude": 1, - "walter": 2, - "donnie": 3, - } - - def test_dict_of_whitespace(self): - parser = waffle_config.DictOf(int) - assert parser(" dude:1, walter: 2 , donnie : 3 ") == { - "dude": 1, - "walter": 2, - "donnie": 3, - } - - def test_dict_of_floats(self): - parser = waffle_config.DictOf(float) - assert parser("dude:1,walter:2,donnie:3.3") == { - "dude": 1.0, - "walter": 2.0, - "donnie": 3.3, - } - - def test_dict_of_strings(self): - parser = waffle_config.DictOf(str) - assert parser("dude:abides,walter:rages,donnie:bowls") == { - "dude": "abides", - "walter": "rages", - "donnie": "bowls", - } - - def test_wrong_type(self): - parser = waffle_config.DictOf(int) - with self.assertRaises(ValueError): - parser("dude:abides,walter:2,donnie:3") - - def test_empty(self): - parser = waffle_config.DictOf(int) - assert parser("") == {} diff --git a/bedrock/base/waffle.py b/bedrock/base/waffle.py index d9e7119fc3d..f05e04e4b42 100644 --- a/bedrock/base/waffle.py +++ b/bedrock/base/waffle.py @@ -2,28 +2,39 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. -""" -Replacement library (function really) for Waffle that uses environment variables. -""" - from django.conf import settings -from bedrock.base.waffle_config import config +from waffle import switch_is_active def switch(name): - """A template helper that replaces waffle + """ + A wrapper around django-waffle's `switch_is_active`. - * All calls default to True when DEV setting is True. - * If the env var is explicitly false it will be false even when DEV = True. - * Otherwise the call is False by default and True is a specific env var exists and is truthy. + It will take whatever name you pass to it (must be only numbers, letters, and dashes), convert + it to uppercase, convert dashes to underscores, then call waffle's `switch_is_active`. + + * When DEV=True + - When switch doesn't exist -> True + - When switch exists and is True -> True + - When switch exists and is False -> False + * When Dev=False + - When switch doesn't exist -> False + - When switch exists and is True -> True + - When switch exists and is False -> False For example: {% if switch('dude-and-walter') %} - would check for an environment variable called `SWITCH_DUDE_AND_WALTER`. The string from the - `switch()` call is converted to uppercase and dashes replaced with underscores. + would check for a waffle switch called `DUDE_AND_WALTER` and return True if active, else False. + """ - env_name = name.upper().replace("-", "_") - return config(env_name, default=str(settings.DEV), parser=bool, namespace="SWITCH") + switch_name = name.upper().replace("-", "_") + active = switch_is_active(switch_name) + # With `settings.WAFFLE_SWITCH_DEFAULT` set to `None`, we can test if a switch is explicitly set or not. + # Here, active is None we know the switch doesn't exist so we default to what `settings.DEV` is. + if active is None: + return settings.DEV + # Otherwise we return the defined switch value. + return active diff --git a/bedrock/base/waffle_config.py b/bedrock/base/waffle_config.py deleted file mode 100644 index 529415a35c1..00000000000 --- a/bedrock/base/waffle_config.py +++ /dev/null @@ -1,78 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -from time import time - -from everett.manager import ( - ConfigDictEnv, - ConfigEnvFileEnv, - ConfigManager, - ConfigOSEnv, - get_parser, -) - -from bedrock.base.models import get_config_dict - - -class DictOf: - """Parser class that returns values as a dict with string keys and values of the chosen type. - - >>> parser = DictOf(int) - >>> parser('en:10,de:20') - {'en': 10, 'de': 20} - """ - - def __init__(self, val_parser): - self.val_parser = val_parser - - def __call__(self, val): - val = val.strip() - val_parser = get_parser(self.val_parser) - out = {} - if not val: - return out - - for part in val.split(","): - k, v = part.split(":") - out[k.strip()] = val_parser(v.strip()) - return out - - -class ConfigDBEnv(ConfigDictEnv): - def __init__(self): - # have to use this directly since settings aren't yet setup - # when we use this in the settings file - self._data = None - self.timeout = 300 - self.last_update = 0 - - def get_cache(self): - if time() > self.last_update + self.timeout: - return None - - return self._data - - def set_cache(self, data): - self._data = data - self.last_update = time() - - @property - def cfg(self): - # this is the method called by the get method - # of the superclass - configs = self.get_cache() - if not configs: - configs = get_config_dict() - self.set_cache(configs) - - return configs - - -config = ConfigManager( - [ - ConfigOSEnv(), - ConfigEnvFileEnv(".env"), - ConfigDBEnv(), - ] -) diff --git a/bedrock/firefox/tests/test_base.py b/bedrock/firefox/tests/test_base.py index fcaa887c937..5877a8a6ca0 100644 --- a/bedrock/firefox/tests/test_base.py +++ b/bedrock/firefox/tests/test_base.py @@ -10,6 +10,7 @@ from django_jinja.backend import Jinja2 from markupsafe import Markup +from waffle.testutils import override_switch from bedrock.base.urlresolvers import reverse from bedrock.firefox import views as fx_views @@ -293,7 +294,7 @@ def test_fx_dev_browser_57_0_a2_whatsnew(self, render_mock): assert template == ["firefox/developer/whatsnew.html"] @override_settings(DEV=True) - @patch.dict(os.environ, SWITCH_FIREFOX_DEVELOPER_WHATSNEW_MDNPLUS="False") + @override_switch("FIREFOX_DEVELOPER_WHATSNEW_MDNPLUS", active=False) def test_fx_dev_browser_102_0_a2_whatsnew_off(self, render_mock): """Should show regular dev browser whatsnew template""" req = self.rf.get("/en-US/firefox/whatsnew/") @@ -302,7 +303,7 @@ def test_fx_dev_browser_102_0_a2_whatsnew_off(self, render_mock): assert template == ["firefox/developer/whatsnew.html"] @override_settings(DEV=True) - @patch.dict(os.environ, SWITCH_FIREFOX_DEVELOPER_WHATSNEW_MDNPLUS="True") + @override_switch("FIREFOX_DEVELOPER_WHATSNEW_MDNPLUS", active=True) def test_fx_dev_browser_102_0_a2_whatsnew_mdnplus(self, render_mock): """Should show MDN Plus dev browser whatsnew template when switch is on""" req = self.rf.get("/en-US/firefox/whatsnew/") diff --git a/bedrock/products/tests/test_views.py b/bedrock/products/tests/test_views.py index feafa401ded..8c228eead7e 100644 --- a/bedrock/products/tests/test_views.py +++ b/bedrock/products/tests/test_views.py @@ -2,7 +2,6 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. -import os from unittest.mock import Mock, patch from django.http import HttpResponse @@ -10,6 +9,8 @@ from django.test.client import RequestFactory from django.urls import reverse +from waffle.testutils import override_switch + from bedrock.contentful.constants import ( CONTENT_CLASSIFICATION_VPN, CONTENT_TYPE_PAGE_RESOURCE_CENTER, @@ -64,7 +65,7 @@ def test_vpn_landing_page_geo_not_available(self, render_mock): self.assertFalse(ctx["vpn_available"]) @override_settings(DEV=False) - @patch.dict(os.environ, SWITCH_VPN_AFFILIATE_ATTRIBUTION="True") + @override_switch("VPN_AFFILIATE_ATTRIBUTION", active=True) def test_vpn_landing_page_geo_available_affiliate_flow_enabled(self, render_mock): req = RequestFactory().get("/products/vpn/", HTTP_CF_IPCOUNTRY="us") req.locale = "en-US" @@ -75,7 +76,7 @@ def test_vpn_landing_page_geo_available_affiliate_flow_enabled(self, render_mock self.assertTrue(ctx["vpn_affiliate_attribution_enabled"]) @override_settings(DEV=False) - @patch.dict(os.environ, SWITCH_VPN_AFFILIATE_ATTRIBUTION="False") + @override_switch("VPN_AFFILIATE_ATTRIBUTION", active=False) def test_vpn_landing_page_geo_available_affiliate_flow_disabled(self, render_mock): req = RequestFactory().get("/products/vpn/", HTTP_CF_IPCOUNTRY="us") req.locale = "en-US" @@ -86,7 +87,7 @@ def test_vpn_landing_page_geo_available_affiliate_flow_disabled(self, render_moc self.assertFalse(ctx["vpn_affiliate_attribution_enabled"]) @override_settings(DEV=False) - @patch.dict(os.environ, SWITCH_VPN_AFFILIATE_ATTRIBUTION="True") + @override_switch("VPN_AFFILIATE_ATTRIBUTION", active=True) def test_vpn_landing_page_geo_not_available_affiliate_flow_enabled(self, render_mock): req = RequestFactory().get("/products/vpn/", HTTP_CF_IPCOUNTRY="cn") req.locale = "en-US" @@ -97,7 +98,7 @@ def test_vpn_landing_page_geo_not_available_affiliate_flow_enabled(self, render_ self.assertFalse(ctx["vpn_affiliate_attribution_enabled"]) @override_settings(DEV=False) - @patch.dict(os.environ, SWITCH_VPN_AFFILIATE_ATTRIBUTION="True") + @override_switch("VPN_AFFILIATE_ATTRIBUTION", active=True) def test_vpn_landing_page_geo_available_affiliate_not_supported_in_country(self, render_mock): req = RequestFactory().get("/products/vpn/", HTTP_CF_IPCOUNTRY="it") req.locale = "en-US" @@ -108,7 +109,7 @@ def test_vpn_landing_page_geo_available_affiliate_not_supported_in_country(self, self.assertFalse(ctx["vpn_affiliate_attribution_enabled"]) @override_settings(DEV=False) - @patch.dict(os.environ, SWITCH_VPN_RELAY_BUNDLE="True") + @override_switch("VPN_RELAY_BUNDLE", active=True) def test_vpn_landing_page_relay_bundle_available(self, render_mock): req = RequestFactory().get("/products/vpn/", HTTP_CF_IPCOUNTRY="us") req.locale = "en-US" @@ -118,7 +119,7 @@ def test_vpn_landing_page_relay_bundle_available(self, render_mock): self.assertTrue(ctx["relay_bundle_available_in_country"]) @override_settings(DEV=False) - @patch.dict(os.environ, SWITCH_VPN_RELAY_BUNDLE="True") + @override_switch("VPN_RELAY_BUNDLE", active=True) def test_vpn_landing_page_relay_bundle_not_available(self, render_mock): req = RequestFactory().get("/products/vpn/", HTTP_CF_IPCOUNTRY="gb") req.locale = "en-US" @@ -128,7 +129,7 @@ def test_vpn_landing_page_relay_bundle_not_available(self, render_mock): self.assertFalse(ctx["relay_bundle_available_in_country"]) @override_settings(DEV=False) - @patch.dict(os.environ, SWITCH_VPN_RELAY_BUNDLE="False") + @override_switch("VPN_RELAY_BUNDLE", active=False) def test_vpn_landing_page_relay_bundle_disabled(self, render_mock): req = RequestFactory().get("/products/vpn/", HTTP_CF_IPCOUNTRY="us") req.locale = "en-US" @@ -175,7 +176,7 @@ def test_vpn_pricing_page_geo_not_available(self, render_mock): self.assertFalse(ctx["vpn_available"]) @override_settings(DEV=False) - @patch.dict(os.environ, SWITCH_VPN_AFFILIATE_ATTRIBUTION="True") + @override_switch("VPN_AFFILIATE_ATTRIBUTION", active=True) def test_vpn_pricing_page_geo_available_affiliate_flow_enabled(self, render_mock): req = RequestFactory().get("/products/vpn/pricing/", HTTP_CF_IPCOUNTRY="us") req.locale = "en-US" @@ -186,7 +187,7 @@ def test_vpn_pricing_page_geo_available_affiliate_flow_enabled(self, render_mock self.assertTrue(ctx["vpn_affiliate_attribution_enabled"]) @override_settings(DEV=False) - @patch.dict(os.environ, SWITCH_VPN_AFFILIATE_ATTRIBUTION="False") + @override_switch("VPN_AFFILIATE_ATTRIBUTION", active=False) def test_vpn_pricing_page_geo_available_affiliate_flow_disabled(self, render_mock): req = RequestFactory().get("/products/vpn/pricing/", HTTP_CF_IPCOUNTRY="us") req.locale = "en-US" @@ -197,7 +198,7 @@ def test_vpn_pricing_page_geo_available_affiliate_flow_disabled(self, render_moc self.assertFalse(ctx["vpn_affiliate_attribution_enabled"]) @override_settings(DEV=False) - @patch.dict(os.environ, SWITCH_VPN_AFFILIATE_ATTRIBUTION="True") + @override_switch("VPN_AFFILIATE_ATTRIBUTION", active=True) def test_vpn_pricing_page_geo_not_available_affiliate_flow_enabled(self, render_mock): req = RequestFactory().get("/products/vpn/pricing/", HTTP_CF_IPCOUNTRY="cn") req.locale = "en-US" @@ -208,7 +209,7 @@ def test_vpn_pricing_page_geo_not_available_affiliate_flow_enabled(self, render_ self.assertFalse(ctx["vpn_affiliate_attribution_enabled"]) @override_settings(DEV=False) - @patch.dict(os.environ, SWITCH_VPN_AFFILIATE_ATTRIBUTION="True") + @override_switch("VPN_AFFILIATE_ATTRIBUTION", active=True) def test_vpn_pricing_page_geo_available_affiliate_not_supported_in_country(self, render_mock): req = RequestFactory().get("/products/vpn/pricing/", HTTP_CF_IPCOUNTRY="it") req.locale = "en-US" diff --git a/bedrock/settings/base.py b/bedrock/settings/base.py index 3a2a2e4e4d7..e9d032d5256 100644 --- a/bedrock/settings/base.py +++ b/bedrock/settings/base.py @@ -2194,3 +2194,9 @@ def lazy_wagtail_langs(): CMS_ALLOWED_PAGE_MODELS = ["__all__"] else: CMS_ALLOWED_PAGE_MODELS = _allowed_page_models + + +# Our use of django-waffle relies on the following 2 settings to be set this way so that if a switch +# doesn't exist, we get `None` back from `switch_is_active`. +WAFFLE_SWITCH_DEFAULT = None +WAFFLE_CREATE_MISSING_SWITCHES = False diff --git a/bin/export-db-to-sqlite.sh b/bin/export-db-to-sqlite.sh index acc2094edf6..c02ac3c5412 100755 --- a/bin/export-db-to-sqlite.sh +++ b/bin/export-db-to-sqlite.sh @@ -153,7 +153,7 @@ python manage.py dumpdata \ wagtailcore.GroupApprovalTask \ taggit.Tag \ taggit.TaggedItem \ - base.ConfigValue \ + waffle.Switch \ cms.StructuralPage \ cms.SimpleRichTextPage \ cms.BedrockImage \ diff --git a/bin/run-db-update.sh b/bin/run-db-update.sh index e78058ff31f..c57850749a9 100755 --- a/bin/run-db-update.sh +++ b/bin/run-db-update.sh @@ -39,7 +39,6 @@ python manage.py update_release_notes --quiet || failure_detected=true python manage.py update_content_cards --quiet || failure_detected=true python manage.py update_externalfiles --quiet || failure_detected=true python manage.py update_newsletter_data --quiet || failure_detected=true -python manage.py update_www_config --quiet || failure_detected=true python manage.py update_legal_docs --quiet || failure_detected=true python manage.py update_webvision_docs --quiet || failure_detected=true python manage.py update_sitemaps_data --quiet || failure_detected=true