From 2731deee077671eddd7aa85ee24c7da6ecb37634 Mon Sep 17 00:00:00 2001 From: Kevin Meinhardt Date: Mon, 3 Feb 2025 18:03:34 +0100 Subject: [PATCH] Remove DEV_MODE and USE_FAKE_FXA_AUTH, replace with PROD_MODE and FXA_CONFIG This commit introduces several changes to simplify and improve the authentication and environment configuration: - Replaced `DEV_MODE` with `PROD_MODE` derived from version JSON instead of environment variable - Removed `USE_FAKE_FXA_AUTH` setting replaced with `FXA_CONFIG` to control fake authentication - Simplified conditional logic around development and production environments to rely on stable build time variables --- docker-compose.yml | 3 +- docs/topics/development/static-files.md | 5 +- .../troubleshooting_and_debugging.md | 16 - settings.py | 24 +- settings_test.py | 13 +- src/olympia/accounts/tests/test_utils.py | 9 +- src/olympia/accounts/tests/test_verify.py | 27 +- src/olympia/accounts/tests/test_views.py | 6 +- src/olympia/accounts/utils.py | 2 +- src/olympia/accounts/verify.py | 3 +- src/olympia/accounts/views.py | 2 +- .../templates/amo/fake_fxa_authorization.html | 7 +- src/olympia/amo/tests/__init__.py | 4 +- src/olympia/amo/tests/test_views.py | 38 +- src/olympia/amo/utils.py | 4 +- src/olympia/amo/views.py | 3 +- src/olympia/api/urls.py | 2 +- src/olympia/core/apps.py | 6 +- src/olympia/core/tests/test_apps.py | 465 +++++++++++------- .../devhub/templates/devhub/index.html | 2 +- .../landfill/management/commands/__init__.py | 11 + .../management/commands/fetch_prod_addons.py | 10 +- .../commands/fetch_prod_versions.py | 10 +- .../management/commands/generate_addons.py | 7 +- .../generate_default_addons_for_frontend.py | 5 +- .../management/commands/generate_themes.py | 7 +- src/olympia/lib/jingo_minify_helpers.py | 4 +- src/olympia/lib/settings_base.py | 24 +- src/olympia/lib/storage.py | 2 +- src/olympia/templates/base.html | 2 +- src/olympia/urls.py | 10 +- 31 files changed, 431 insertions(+), 302 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index ba0501056957..243ca54e1ae5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,7 +21,8 @@ x-env-mapping: &env - HISTCONTROL=erasedups - CIRCLECI - DATA_BACKUP_SKIP - + - FXA_CLIENT_ID + - FXA_CLIENT_SECRET x-olympia: &olympia <<: *env image: ${DOCKER_TAG:-} diff --git a/docs/topics/development/static-files.md b/docs/topics/development/static-files.md index 46a4e3ffc377..f5aebe0d5638 100644 --- a/docs/topics/development/static-files.md +++ b/docs/topics/development/static-files.md @@ -48,13 +48,10 @@ To better visualize the impact of the various settings, here is a reference: Given a static file 'js/devhub/my-file.js': -In `DEV_MODE` the url will look like `/static/js/devhub/my-file.js` no matter what. +On development images, the url will look like `/static/js/devhub/my-file.js` no matter what. However, in production, if `DEBUG` is `False`, the url will append the content hash like this, `/static/js/devhub/my-file.1234567890.js`. Finally, if `DEBUG` is true, this file will be minified and concatenated with other files and probably look something like this `/static/js/devhub-all.min.1234567890.js`. -The true `production` mode is then when `DEBUG` is `False` and `DEV_MODE` is `False`. But it makes sense -to make these individually toggleable so you can better "debug" js files from a production image. - ### Project Static Files Static files specific to the addons-server project are stored in the `./static` directory. These include CSS, JavaScript, images, and other assets used by the application. diff --git a/docs/topics/development/troubleshooting_and_debugging.md b/docs/topics/development/troubleshooting_and_debugging.md index 86443cef935f..b00e92482be3 100644 --- a/docs/topics/development/troubleshooting_and_debugging.md +++ b/docs/topics/development/troubleshooting_and_debugging.md @@ -2,22 +2,6 @@ Effective troubleshooting and debugging practices are essential for maintaining and improving the **addons-server** project. This section covers common issues, their solutions, and tools for effective debugging. -## DEV_MODE vs DEBUG - -In our project, `DEV_MODE` and `DEBUG` serve distinct but complementary purposes. -`DEV_MODE` is directly tied to the `DOCKER_TARGET` environment variable and is used to enable or disable behaviors -based on whether we are running a production image or not. - -For instance, production images always disables certain features like using fake fxa authentication. Additionally, -certain dependencies are only installed in [dev.txt](../../../requirements/dev.txt) and so must be disabled in production. - -Conversely, `DEBUG` controls the activation of debugging tools and utilities, such as the debug_toolbar, -which are useful for troubleshooting. Unlike DEV_MODE, DEBUG is independent -and can be toggled in both development and production environments as needed. - -This separation ensures that essential behaviors are managed according to the deployment target (DEV_MODE), -while allowing flexibility to enable or disable debugging features (DEBUG) in production or development images. - ## Common Issues and Solutions 1. **Containers Not Starting**: diff --git a/settings.py b/settings.py index f149f6bb5bb4..647386353f9c 100644 --- a/settings.py +++ b/settings.py @@ -9,16 +9,9 @@ import os from urllib.parse import urlparse -from olympia.core.utils import get_version_json from olympia.lib.settings_base import * # noqa -# "production" is a named docker stage corresponding to the production image. -# when we build the production image, the stage to use is determined -# via the "DOCKER_TARGET" variable which is also passed into the image. -# So if the value is anything other than "production" we are in development mode. -DEV_MODE = DOCKER_TARGET != 'production' - HOST_UID = os.environ.get('HOST_UID') WSGI_APPLICATION = 'olympia.wsgi.application' @@ -63,7 +56,7 @@ def insert_debug_toolbar_middleware(middlewares): # We can only add these dependencies if we have development dependencies -if os.environ.get('OLYMPIA_DEPS', '') == 'development': +if OLYMPIA_DEPS == 'development': INSTALLED_APPS += ( 'debug_toolbar', 'dbbackup', @@ -118,14 +111,6 @@ def insert_debug_toolbar_middleware(middlewares): FXA_OAUTH_HOST = 'https://oauth.stage.mozaws.net/v1' FXA_PROFILE_HOST = 'https://profile.stage.mozaws.net/v1' -# When USE_FAKE_FXA_AUTH and settings.DEV_MODE are both True, we serve a fake -# authentication page, bypassing FxA. To disable this behavior, set -# USE_FAKE_FXA = False in your local settings. -# You will also need to specify `client_id` and `client_secret` in your -# local_settings.py or environment variables - you must contact the FxA team to get your -# own credentials for FxA stage. -USE_FAKE_FXA_AUTH = True - # CSP report endpoint which returns a 204 from addons-nginx in local dev. CSP_REPORT_URI = '/csp-report' RESTRICTED_DOWNLOAD_CSP['REPORT_URI'] = CSP_REPORT_URI @@ -199,8 +184,11 @@ def insert_debug_toolbar_middleware(middlewares): ENABLE_ADMIN_MLBF_UPLOAD = True -# Use dev mode if we are on a non production imqage and debug is enabled. -if get_version_json().get('target') != 'production' and DEBUG: +# In non production images, we should enable dev mode. +# The 'bundle' prefix is used to control routing behavior in nginx, +# ensuring static files are not served by nginx +# but redirected to the vite dev server. +if not PROD_MODE: DJANGO_VITE = { 'default': { 'dev_mode': True, diff --git a/settings_test.py b/settings_test.py index f9353ab4d70b..0e29765965c3 100644 --- a/settings_test.py +++ b/settings_test.py @@ -23,8 +23,6 @@ IN_TEST_SUITE = True DEBUG = False -# We should default to production mode unless otherwise specified -DEV_MODE = False # We won't actually send an email. SEND_REAL_EMAIL = True @@ -113,3 +111,14 @@ # This is a testing environment TESTING_ENV = True + +FXA_CONFIG = { + 'default': { + 'client_id': 'amodefault', + 'client_secret': 'amodefault', + }, + 'skip': { + 'client_id': '.', + 'client_secret': '.', + }, +} diff --git a/src/olympia/accounts/tests/test_utils.py b/src/olympia/accounts/tests/test_utils.py index 6f92c123636f..669504e14b35 100644 --- a/src/olympia/accounts/tests/test_utils.py +++ b/src/olympia/accounts/tests/test_utils.py @@ -15,6 +15,10 @@ 'client_id': 'foo', 'client_secret': 'bar', }, + 'fake': { + 'client_id': '.', + 'client_secret': '.', + }, 'other': {'client_id': 'foo_other', 'client_secret': 'bar_other'}, } @@ -292,13 +296,12 @@ def test_redirect_for_login_with_2fa_enforced_and_config(): assert request.session['enforce_2fa'] is True -@override_settings(DEV_MODE=True, USE_FAKE_FXA_AUTH=True) def test_fxa_login_url_when_faking_fxa_auth(): path = '/en-US/addons/abp/?source=ddg' request = RequestFactory().get(path) request.session = {'fxa_state': 'myfxastate'} raw_url = utils.fxa_login_url( - config=FXA_CONFIG['default'], + config=FXA_CONFIG['fake'], state=request.session['fxa_state'], next_path=path, ) @@ -309,7 +312,7 @@ def test_fxa_login_url_when_faking_fxa_auth(): query = parse_qs(url.query) next_path = urlsafe_b64encode(path.encode('utf-8')).rstrip(b'=') assert query == { - 'client_id': ['foo'], + 'client_id': ['.'], 'scope': ['profile openid'], 'state': [f'myfxastate:{force_str(next_path)}'], 'access_type': ['offline'], diff --git a/src/olympia/accounts/tests/test_verify.py b/src/olympia/accounts/tests/test_verify.py index 0b0af2129933..93cc9c3fe2fa 100644 --- a/src/olympia/accounts/tests/test_verify.py +++ b/src/olympia/accounts/tests/test_verify.py @@ -13,6 +13,18 @@ from olympia.amo.tests import TestCase +FXA_CONFIG = { + 'default': { + 'client_id': 'test-client-id', + 'client_secret': "don't look", + }, + 'fake': { + 'client_id': '.', + 'client_secret': '.', + }, +} + + class TestProfile(TestCase): def setUp(self): patcher = mock.patch('olympia.accounts.verify.requests.get') @@ -234,17 +246,18 @@ def test_with_id_token(self): self.get_profile.assert_called_with('cafe') -@override_settings(USE_FAKE_FXA_AUTH=False, DEV_MODE=True, VERIFY_FXA_ACCESS_TOKEN=True) +@override_settings(FXA_CONFIG=FXA_CONFIG, VERIFY_FXA_ACCESS_TOKEN=True) class TestCheckAndUpdateFxaAccessToken(TestCase): def setUp(self): super().setUp() self.get_fxa_token_mock = self.patch('olympia.accounts.verify.get_fxa_token') - def get_request(self, expiry_timestamp=None): + def get_request(self, expiry_timestamp=None, config_name='default'): expiry_timestamp = ( expiry_timestamp or (datetime.now() - timedelta(days=1)).timestamp() ) request = mock.Mock() + request.GET = {'config': config_name} request.session = { SESSION_KEY: '1', 'fxa_access_token_expiry': expiry_timestamp, @@ -253,12 +266,12 @@ def get_request(self, expiry_timestamp=None): return request def test_use_fake_fxa_auth(self): - request = self.get_request() - with override_settings(USE_FAKE_FXA_AUTH=True): - verify.check_and_update_fxa_access_token(request) - self.get_fxa_token_mock.assert_not_called() + verify.check_and_update_fxa_access_token(self.get_request(config_name='fake')) + self.get_fxa_token_mock.assert_not_called() - verify.check_and_update_fxa_access_token(request) + verify.check_and_update_fxa_access_token( + self.get_request(config_name='default') + ) self.get_fxa_token_mock.assert_called() def test_verify_access_token_setting_false(self): diff --git a/src/olympia/accounts/tests/test_views.py b/src/olympia/accounts/tests/test_views.py index 42c274b6cf2a..2eee49d2c92a 100644 --- a/src/olympia/accounts/tests/test_views.py +++ b/src/olympia/accounts/tests/test_views.py @@ -161,7 +161,7 @@ def has_cors_headers(response, origin='https://addons-frontend'): class TestLoginStartView(TestCase): - @override_settings(DEV_MODE=True, USE_FAKE_FXA_AUTH=True) + @override_settings(FXA_CONFIG={'default': {'client_id': '.'}}) def test_redirect_url_fake_fxa_auth(self): response = self.client.get(reverse_ns('accounts.login_start')) assert response.status_code == 302 @@ -689,7 +689,7 @@ def test_waffle_flag_off_enforced_2fa_should_have_no_effect(self): self.request.session['enforce_2fa'] = True self._test_should_continue_without_redirect_for_two_factor_auth() - @override_settings(DEV_MODE=True, USE_FAKE_FXA_AUTH=True) + @override_settings(FXA_CONFIG={'default': {'client_id': '.'}}) def test_fake_fxa_auth(self): self.user = user_factory() self.find_user.return_value = self.user @@ -710,7 +710,7 @@ def test_fake_fxa_auth(self): assert kwargs['next_path'] == '/a/path/?' assert self.fxa_identify.call_count == 0 - @override_settings(DEV_MODE=True, USE_FAKE_FXA_AUTH=True) + @override_settings(FXA_CONFIG={'default': {'client_id': '.'}}) def test_fake_fxa_auth_with_2fa(self): self.user = user_factory() self.find_user.return_value = self.user diff --git a/src/olympia/accounts/utils.py b/src/olympia/accounts/utils.py index a53e1f51cc4e..4d5ee59967e5 100644 --- a/src/olympia/accounts/utils.py +++ b/src/olympia/accounts/utils.py @@ -59,7 +59,7 @@ def fxa_login_url( elif login_hint: query['prompt'] = 'none' query['login_hint'] = login_hint - if use_fake_fxa(): + if use_fake_fxa(config): base_url = reverse('fake-fxa-authorization') else: base_url = f'{settings.FXA_OAUTH_HOST}/authorization' diff --git a/src/olympia/accounts/verify.py b/src/olympia/accounts/verify.py index 9e4b00eb9bf6..07e9883ab42b 100644 --- a/src/olympia/accounts/verify.py +++ b/src/olympia/accounts/verify.py @@ -6,6 +6,7 @@ from django_statsd.clients import statsd import olympia.core.logger +from olympia.accounts.utils import get_fxa_config from olympia.amo.utils import use_fake_fxa @@ -117,7 +118,7 @@ def check_and_update_fxa_access_token(request): otherwise.""" if ( - not use_fake_fxa() + not use_fake_fxa(get_fxa_config(request)) and settings.VERIFY_FXA_ACCESS_TOKEN and (request.session.get('fxa_access_token_expiry') or 0) < time.time() ): diff --git a/src/olympia/accounts/views.py b/src/olympia/accounts/views.py index 0ab4c27a209e..820924d355d0 100644 --- a/src/olympia/accounts/views.py +++ b/src/olympia/accounts/views.py @@ -264,7 +264,7 @@ def inner(self, request): elif request.user.is_authenticated and not enforce_2fa_for_this_session: return safe_redirect(request, next_path, ERROR_AUTHENTICATED) try: - if use_fake_fxa() and 'fake_fxa_email' in data: + if use_fake_fxa(fxa_config) and 'fake_fxa_email' in data: # Bypassing real authentication, we take the email provided # and generate a random fxa id. identity = { diff --git a/src/olympia/amo/templates/amo/fake_fxa_authorization.html b/src/olympia/amo/templates/amo/fake_fxa_authorization.html index 80dd0bf2a680..2c82975d7c57 100644 --- a/src/olympia/amo/templates/amo/fake_fxa_authorization.html +++ b/src/olympia/amo/templates/amo/fake_fxa_authorization.html @@ -4,7 +4,12 @@

{{ _('Register or Log in') }}

Don't panic!

-

You're seeing this page instead of the regular authentication page because both settings.DEBUG and settings.USE_FAKE_FXA_AUTH are set to True.

+

+ You're seeing this page instead of the regular authentication page because the + FXA_CONFIG being used has the client_id set to .. + This is used to trigger fake authentication without redirecting to an actual + authentication server. +

Because this is a fake authentication page, you don't need to enter a password, and you also don't need to control the email address. This can be be used to log in to the admin pages which normally require an account with @mozilla.com email address.

{% for key, value in request.GET.items() %} diff --git a/src/olympia/amo/tests/__init__.py b/src/olympia/amo/tests/__init__.py index b1bb0d50af36..2580efde542c 100644 --- a/src/olympia/amo/tests/__init__.py +++ b/src/olympia/amo/tests/__init__.py @@ -37,7 +37,7 @@ from olympia import amo from olympia.access.models import Group, GroupUser -from olympia.accounts.utils import fxa_login_url +from olympia.accounts.utils import fxa_login_url, get_fxa_config from olympia.addons.indexers import AddonIndexer from olympia.addons.models import ( Addon, @@ -304,7 +304,7 @@ def create_session(self, user, **overrides): # this is pretty much what django.contrib.auth.login does to initialize session fxa_details = ( {'fxa_access_token_expiry': time.time() + 1000} - if not use_fake_fxa() + if not use_fake_fxa(get_fxa_config(request)) else {} ) initialize_session( diff --git a/src/olympia/amo/tests/test_views.py b/src/olympia/amo/tests/test_views.py index 7f8b54571f55..c9d5b00d23cb 100644 --- a/src/olympia/amo/tests/test_views.py +++ b/src/olympia/amo/tests/test_views.py @@ -509,35 +509,27 @@ def test_allow_mozilla_collections(self): @pytest.mark.django_db +@override_settings(FXA_CONFIG={'default': {'client_id': '.'}}) def test_fake_fxa_authorization_correct_values_passed(): - with override_settings(DEV_MODE=True): # USE_FAKE_FXA_AUTH is already True - url = reverse('fake-fxa-authorization') - response = test.Client().get(url, {'state': 'foobar'}) - assert response.status_code == 200 - doc = pq(response.content) - form = doc('#fake_fxa_authorization')[0] - assert form.attrib['action'] == reverse('auth:accounts.authenticate') - elm = doc('#fake_fxa_authorization input[name=code]')[0] - assert elm.attrib['value'] == 'fakecode' - elm = doc('#fake_fxa_authorization input[name=state]')[0] - assert elm.attrib['value'] == 'foobar' - elm = doc('#fake_fxa_authorization input[name=fake_fxa_email]') - assert elm # No value yet, should just be present. + url = reverse('fake-fxa-authorization') + response = test.Client().get(url, {'state': 'foobar'}) + assert response.status_code == 200 + doc = pq(response.content) + form = doc('#fake_fxa_authorization')[0] + assert form.attrib['action'] == reverse('auth:accounts.authenticate') + elm = doc('#fake_fxa_authorization input[name=code]')[0] + assert elm.attrib['value'] == 'fakecode' + elm = doc('#fake_fxa_authorization input[name=state]')[0] + assert elm.attrib['value'] == 'foobar' + elm = doc('#fake_fxa_authorization input[name=fake_fxa_email]') + assert elm # No value yet, should just be present. @pytest.mark.django_db +@override_settings(FXA_CONFIG={'default': {'client_id': 'amodefault'}}) def test_fake_fxa_authorization_deactivated(): url = reverse('fake-fxa-authorization') - with override_settings(DEV_MODE=False, USE_FAKE_FXA_AUTH=False): - response = test.Client().get(url) - assert response.status_code == 404 - - with override_settings(DEV_MODE=False, USE_FAKE_FXA_AUTH=True): - response = test.Client().get(url) - assert response.status_code == 404 - - with override_settings(DEV_MODE=True, USE_FAKE_FXA_AUTH=False): - response = test.Client().get(url) + response = test.Client().get(url) assert response.status_code == 404 diff --git a/src/olympia/amo/utils.py b/src/olympia/amo/utils.py index 3b92968580c9..84d3dab95be2 100644 --- a/src/olympia/amo/utils.py +++ b/src/olympia/amo/utils.py @@ -1161,10 +1161,10 @@ def extract_colors_from_image(path): return colors -def use_fake_fxa(): +def use_fake_fxa(config): """Return whether or not to use a fake FxA server for authentication. Should always return False in production""" - return settings.DEV_MODE and settings.USE_FAKE_FXA_AUTH + return config.get('client_id') == '.' class AMOJSONEncoder(JSONEncoder): diff --git a/src/olympia/amo/views.py b/src/olympia/amo/views.py index 2e8a42f9d597..3c59ca163261 100644 --- a/src/olympia/amo/views.py +++ b/src/olympia/amo/views.py @@ -19,6 +19,7 @@ from rest_framework.views import APIView from olympia import amo +from olympia.accounts.utils import get_fxa_config from olympia.amo.utils import HttpResponseXSendFile, use_fake_fxa from olympia.api.exceptions import base_500_data from olympia.api.serializers import SiteStatusSerializer @@ -194,7 +195,7 @@ def frontend_view(*args, **kwargs): def fake_fxa_authorization(request): """Fake authentication page to bypass FxA in local development envs.""" - if not use_fake_fxa(): + if not use_fake_fxa(get_fxa_config(request)): raise Http404() interesting_accounts = UserProfile.objects.exclude(groups=None).exclude( deleted=True diff --git a/src/olympia/api/urls.py b/src/olympia/api/urls.py index 7ff96027ade7..2d73fed1ef1e 100644 --- a/src/olympia/api/urls.py +++ b/src/olympia/api/urls.py @@ -27,7 +27,7 @@ def get_versioned_api_routes(version, url_patterns): routes = url_patterns # For now, this feature is only enabled in dev mode - if settings.DEV_MODE: + if not settings.PROD_MODE: routes.extend( [ re_path( diff --git a/src/olympia/core/apps.py b/src/olympia/core/apps.py index 3d7a6aebdd22..0b9c0b034a79 100644 --- a/src/olympia/core/apps.py +++ b/src/olympia/core/apps.py @@ -88,10 +88,9 @@ def version_check(app_configs, **kwargs): def static_check(app_configs, **kwargs): errors = [] output = StringIO() - version = get_version_json() # We only run this check in production images. - if version.get('target') != 'production': + if not settings.PROD_MODE: return [] try: @@ -188,9 +187,8 @@ def db_charset_check(app_configs, **kwargs): @register(CustomTags.custom_setup) def nginx_check(app_configs, **kwargs): errors = [] - version = get_version_json() - if version.get('target') == 'production': + if settings.PROD_MODE: return [] configs = [ diff --git a/src/olympia/core/tests/test_apps.py b/src/olympia/core/tests/test_apps.py index 8c6823aeca15..a0bab34ef9a4 100644 --- a/src/olympia/core/tests/test_apps.py +++ b/src/olympia/core/tests/test_apps.py @@ -1,217 +1,352 @@ +import json import os import tempfile from unittest import mock from django.core.management import CommandError, call_command from django.core.management.base import SystemCheckError -from django.test import TestCase -from django.test.utils import override_settings +from django.test import TestCase, override_settings import responses from olympia.core.utils import REQUIRED_VERSION_KEYS -class SystemCheckIntegrationTest(TestCase): +class BaseCheckTestCase(TestCase): + """Base class for all system check tests with common setup and utilities.""" + def setUp(self): + super().setUp() + # Create temporary directories + self.temp_dirs = { + 'media_root': tempfile.mkdtemp(prefix='media-root-'), + 'static_root': tempfile.mkdtemp(prefix='static-root-'), + 'static_build': tempfile.mkdtemp(prefix='static-build-'), + } + + # Setup default version.json data self.default_version_json = { 'tag': 'mozilla/addons-server:1.0', - 'target': 'production', + 'target': 'development', 'commit': 'abc', 'version': '1.0', 'build': 'http://example.com/build', 'source': 'https://github.com/mozilla/addons-server', } - patch = mock.patch( - 'olympia.core.apps.get_version_json', + + # Setup mocks + self._setup_mocks() + + # Create some default files + self.create_test_files() + + # Setup responses for nginx checks + self._setup_nginx_responses() + + def _setup_mocks(self): + """Setup all required mocks.""" + # Version.json mock + patcher = mock.patch('olympia.core.apps.get_version_json') + self.mock_version = patcher.start() + self.mock_version.return_value = self.default_version_json + self.addCleanup(patcher.stop) + + # Database cursor mock + patcher = mock.patch('olympia.core.apps.connection.cursor') + self.mock_db = patcher.start() + self.mock_db.return_value.__enter__.return_value.fetchone.return_value = ( + 'character_set_database', + 'utf8mb4', ) - self.mock_get_version_json = patch.start() - self.mock_get_version_json.return_value = self.default_version_json - self.addCleanup(patch.stop) + self.addCleanup(patcher.stop) - mock_dir = tempfile.mkdtemp(prefix='static-root') - self.fake_css_file = os.path.join(mock_dir, 'foo.css') - with open(self.fake_css_file, 'w') as f: - f.write('body { background: red; }') + # uWSGI mock + patcher = mock.patch('olympia.core.apps.subprocess') + self.mock_uwsgi = patcher.start() + self.mock_uwsgi.run.return_value.returncode = 0 + self.addCleanup(patcher.stop) + + # User ID mock + patcher = mock.patch('olympia.core.apps.getpwnam') + self.mock_uid = patcher.start() + self.mock_uid.return_value.pw_uid = 9500 + self.addCleanup(patcher.stop) + + # Compress assets mock + patcher = mock.patch('olympia.core.apps.call_command') + self.mock_compress = patcher.start() + self.mock_compress.return_value = None + self.addCleanup(patcher.stop) + + def _setup_nginx_responses(self): + """Setup responses for nginx checks.""" + responses.reset() + + # Setup successful responses for all nginx endpoints + test_urls = [ + 'http://nginx/user-media/test.txt', + 'http://nginx/static/test.txt', + ] - patch_command = mock.patch('olympia.core.apps.call_command') - self.mock_call_command = patch_command.start() - self.mock_call_command.side_effect = ( - lambda command, dry_run, stdout: stdout.write(f'{self.fake_css_file}\n') + for url in test_urls: + responses.add( + responses.GET, + url, + status=200, + body=self.temp_dirs['media_root'], + headers={'X-Served-By': 'nginx'}, + ) + + def create_test_files(self): + """Create default test files in temporary directories.""" + # Create static build manifest + self.manifest_path = os.path.join( + self.temp_dirs['static_build'], 'static-build.json' ) - self.addCleanup(patch_command.stop) + with open(self.manifest_path, 'w') as f: + json.dump({'app.js': {'file': 'app.123.js'}}, f) - self.media_root = tempfile.mkdtemp(prefix='media-root') + # Create compressed CSS file + self.css_file = os.path.join(self.temp_dirs['static_root'], 'foo.css') + with open(self.css_file, 'w') as f: + f.write('body { background: red; }') - @mock.patch('olympia.core.apps.connection.cursor') - def test_db_charset_check(self, mock_cursor): - mock_cursor.return_value.__enter__.return_value.fetchone.return_value = ( - 'character_set_database', - 'utf8mb3', + # Create the app.js file referenced in manifest + self.js_file = os.path.join(self.temp_dirs['static_root'], 'app.123.js') + with open(self.js_file, 'w') as f: + f.write('console.log("test");') + + self.mock_compress.side_effect = lambda command, dry_run, stdout: stdout.write( + f'{self.css_file}\n{self.js_file}' ) - with self.assertRaisesMessage( - SystemCheckError, - 'Database charset invalid. Expected utf8mb4, recieved utf8mb3', - ): - call_command('check') - @mock.patch('olympia.core.apps.connection.cursor') - def test_db_unavailable_check(self, mock_cursor): - mock_cursor.side_effect = Exception('Database is unavailable') - with self.assertRaisesMessage( - SystemCheckError, - 'Failed to connect to database: Database is unavailable', - ): + def assertSystemCheckRaises(self, message): + """Assert that the system check raises an exception with the given message.""" + with self.assertRaisesMessage(SystemCheckError, message): call_command('check') - def test_uwsgi_check(self): - call_command('check') + def tearDown(self): + """Clean up temporary directories.""" + responses.reset() + for temp_dir in self.temp_dirs.values(): + try: + for root, dirs, files in os.walk(temp_dir, topdown=False): + for name in files: + os.remove(os.path.join(root, name)) + for name in dirs: + os.rmdir(os.path.join(root, name)) + os.rmdir(temp_dir) + except (OSError, IOError): + pass + super().tearDown() - with mock.patch('olympia.core.apps.subprocess') as subprocess: - subprocess.run.return_value.returncode = 127 - with self.assertRaisesMessage( - SystemCheckError, 'uwsgi --version returned a non-zero value' - ): - call_command('check') - - def test_missing_version_keys_check(self): - """ - We expect all required version keys to be set during the docker build. - """ - for broken_key in REQUIRED_VERSION_KEYS: - with self.subTest(broken_key=broken_key): - del self.mock_get_version_json.return_value[broken_key] - with self.assertRaisesMessage( - SystemCheckError, - f'{broken_key} is missing from version.json', - ): - call_command('check') - @override_settings(HOST_UID=None) - @mock.patch('olympia.core.apps.getpwnam') - def test_illegal_override_uid_check(self, mock_getpwnam): - """ - In production, or when HOST_UID is not set, we expect to not override - the default uid of 9500 for the olympia user. - """ - mock_getpwnam.return_value.pw_uid = 1000 - with self.assertRaisesMessage( - SystemCheckError, - 'Expected user uid to be 9500', +class FileSystemCheckTests(BaseCheckTestCase): + """Tests focusing on file system states affecting checks.""" + + @responses.activate + def test_all_files_present_development(self): + """Test when all required files exist in development mode.""" + with override_settings( + PROD_MODE=True, + MEDIA_ROOT=self.temp_dirs['media_root'], + STATIC_ROOT=self.temp_dirs['static_root'], + STATIC_BUILD_MANIFEST_PATH=self.manifest_path, + HOST_UID='9500', ): + # If the compress_assets command returns the correct files -> None call_command('check') + # If the compress_assets comamand raises -> SystemCheckError + self.mock_compress.side_effect = CommandError('Unexpected error') + self.assertSystemCheckRaises( + 'Error running compress_assets command: Unexpected error' + ) + # If compress_assets command returns no files -> SystemCheckError + self.mock_compress.side_effect = lambda command, dry_run, stdout: None + self.assertSystemCheckRaises('No compressed asset files were found.') - with override_settings(HOST_UID=1000): - call_command('check') + def test_missing_static_build_manifest(self): + """Test behavior when static build manifest is missing.""" + os.remove(self.manifest_path) + with override_settings( + MEDIA_ROOT=self.temp_dirs['media_root'], + STATIC_ROOT=self.temp_dirs['static_root'], + STATIC_BUILD_MANIFEST_PATH=self.manifest_path, + PROD_MODE=True, + HOST_UID='9500', + ): + self.assertSystemCheckRaises('Static build manifest file does not exist') - def test_static_check_no_assets_found(self): - """ - Test static_check fails if compress_assets reports no files. - """ - self.mock_get_version_json.return_value['target'] = 'production' - # Simulate "compress_assets" returning no file paths. - self.mock_call_command.side_effect = ( - lambda command, dry_run, stdout: stdout.write('') - ) - with self.assertRaisesMessage( - SystemCheckError, 'No compressed asset files were found.' + def test_missing_media_root(self): + """Test behavior when media root directory is missing.""" + with override_settings( + PROD_MODE=False, + MEDIA_ROOT='/fake/not/real/directory', + STATIC_ROOT=self.temp_dirs['static_root'], + STATIC_BUILD_MANIFEST_PATH=self.manifest_path, + HOST_UID='9500', ): - call_command('check') + self.assertSystemCheckRaises('/fake/not/real/directory does not exist') - @mock.patch('os.path.exists') - def test_static_check_missing_assets(self, mock_exists): - """ - Test static_check fails if at least one specified compressed - asset file does not exist. - """ - self.mock_get_version_json.return_value['target'] = 'production' - # Simulate "compress_assets" returning a couple of files. - self.mock_call_command.side_effect = ( - lambda command, dry_run, stdout: stdout.write( - f'{self.fake_css_file}\nfoo.js\n' - ) - ) - # Pretend neither file exists on disk. - mock_exists.return_value = False + def test_invalid_manifest_content(self): + """Test behavior with invalid manifest content.""" + with open(self.manifest_path, 'w') as f: + json.dump({'app.js': {'file': 'missing.js'}}, f) - with self.assertRaisesMessage( - SystemCheckError, - # Only the first missing file triggers the AssertionError message check - 'Compressed asset file does not exist: foo.js', + with override_settings( + MEDIA_ROOT=self.temp_dirs['media_root'], + STATIC_ROOT=self.temp_dirs['static_root'], + STATIC_BUILD_MANIFEST_PATH=self.manifest_path, + PROD_MODE=True, + HOST_UID='9500', ): - call_command('check') + self.assertSystemCheckRaises( + 'Static asset app.js does not exist at ' 'expected path: ' + ) - def test_static_check_command_error(self): - """ - Test static_check fails if there's an error during compress_assets. - """ - self.mock_get_version_json.return_value['target'] = 'production' - self.mock_call_command.side_effect = CommandError('Oops') - with self.assertRaisesMessage( - SystemCheckError, 'Error running compress_assets command: Oops' - ): - call_command('check') - def test_static_check_command_success(self): - """ - Test static_check succeeds if compress_assets runs without errors. - """ - self.mock_get_version_json.return_value['target'] = 'production' - self.mock_call_command.side_effect = ( - lambda command, dry_run, stdout: stdout.write(f'{self.fake_css_file}\n') +class ServiceStateTests(BaseCheckTestCase): + """Tests focusing on service availability and responses.""" + + def test_database_unavailable(self): + """Test behavior when database is unavailable.""" + self.mock_db.side_effect = Exception('Database is unavailable') + with override_settings(HOST_UID='9500'): + self.assertSystemCheckRaises( + 'Failed to connect to database: Database is unavailable' + ) + + def test_invalid_database_charset(self): + """Test behavior with wrong database charset.""" + self.mock_db.return_value.__enter__.return_value.fetchone.return_value = ( + 'character_set_database', + 'utf8mb3', ) - call_command('check') + with override_settings(HOST_UID='9500'): + self.assertSystemCheckRaises('Database charset invalid') - def test_nginx_skips_check_on_production_target(self): - fake_media_root = '/fake/not/real' - with override_settings(MEDIA_ROOT=fake_media_root): - call_command('check') + def test_uwsgi_unavailable(self): + """Test behavior when uWSGI is not available.""" + self.mock_uwsgi.run.return_value.returncode = 1 + with override_settings(HOST_UID='9500'): + self.assertSystemCheckRaises('uwsgi --version returned a non-zero value') - def test_nginx_raises_missing_directory(self): - self.mock_get_version_json.return_value['target'] = 'development' - fake_media_root = '/fake/not/real' - with override_settings(MEDIA_ROOT=fake_media_root): - with self.assertRaisesMessage( - SystemCheckError, - f'{fake_media_root} does not exist', + @responses.activate + def test_nginx_responses(self): + """Test various nginx response scenarios.""" + self.default_version_json['target'] = 'development' + + # Test various response scenarios + test_cases = [ + (404, 'content', 'nginx'), + (200, 'wrong', 'nginx'), + (200, 'content', 'apache'), + ] + + for status, content, server in test_cases: + responses.reset() + responses.add( + responses.GET, + 'http://nginx/user-media/test.txt', + status=status, + body=content, + headers={'X-Served-By': server}, + ) + responses.add( + responses.GET, + 'http://nginx/static/test.txt', + status=status, + body=content, + headers={'X-Served-By': server}, + ) + + with override_settings( + MEDIA_ROOT=self.temp_dirs['media_root'], + STATIC_ROOT=self.temp_dirs['static_root'], + STATIC_BUILD_MANIFEST_PATH=self.manifest_path, + HOST_UID='9500', ): - call_command('check') - - def _test_nginx_response( - self, base_url, status_code=200, response_text='', served_by='nginx' - ): - self.mock_get_version_json.return_value['target'] = 'development' - url = f'{base_url}/test.txt' - - responses.add( - responses.GET, - url, - status=status_code, - body=response_text, - headers={'X-Served-By': served_by}, - ) + self.assertSystemCheckRaises('Failed to access') - expected_config = ( - (status_code, 200), - (response_text, self.media_root), - (served_by, 'nginx'), - ) - with override_settings(MEDIA_ROOT=self.media_root): - with self.assertRaisesMessage( - SystemCheckError, - f'Failed to access {url}. {expected_config}', +class ConfigurationTests(BaseCheckTestCase): + """Tests focusing on different configuration combinations.""" + + def test_host_uid_configurations(self): + """Test various HOST_UID configurations.""" + test_cases = [ + (None, 9500, None), # Default production + (None, 1000, 'Expected user uid to be 9500'), # Wrong UID in prod + ('1000', 1000, None), # Correct custom UID + ('1000', 2000, 'Expected user uid to be 1000'), # Wrong custom UID + ] + + for host_uid, actual_uid, expected_error in test_cases: + self.mock_uid.return_value.pw_uid = actual_uid + with override_settings( + PROD_MODE=True, + HOST_UID=host_uid, + MEDIA_ROOT=self.temp_dirs['media_root'], + STATIC_ROOT=self.temp_dirs['static_root'], + STATIC_BUILD_MANIFEST_PATH=self.manifest_path, ): - call_command('check') + if expected_error: + self.assertSystemCheckRaises(expected_error) + else: + call_command('check') + + def test_version_json_requirements(self): + """Test version.json requirements.""" + # Test missing each required key + for key in REQUIRED_VERSION_KEYS: + version_data = self.default_version_json.copy() + del version_data[key] + self.mock_version.return_value = version_data - def test_nginx_raises_non_200_status_code(self): - """Test that files return a 200 status code.""" - self._test_nginx_response('http://nginx/user-media', status_code=404) + with override_settings(HOST_UID='9500'): + with self.assertRaisesRegex( + Exception, rf'{key} is missing from version.json' + ): + call_command('check') - def test_nginx_raises_unexpected_content(self): - """Test that files return the expected content.""" - self._test_nginx_response('http://nginx/user-media', response_text='foo') - def test_nginx_raises_unexpected_served_by(self): - """Test that files are served by nginx and not redirected elsewhere.""" - self._test_nginx_response('http://nginx/user-media', served_by='wow') +class CombinedScenariosTests(BaseCheckTestCase): + """Tests multiple failing checks together.""" + + def test_multiple_service_failures(self): + """Test behavior when multiple services are unavailable.""" + # Make database unavailable + self.mock_db.side_effect = Exception('Database is unavailable') + # Make uWSGI unavailable + self.mock_uwsgi.run.return_value.returncode = 1 + + with override_settings(HOST_UID='9500'): + self.assertSystemCheckRaises( + 'Failed to connect to database: Database is unavailable' + ) + + def test_multiple_file_issues(self): + """Test behavior with multiple missing files.""" + + with override_settings( + MEDIA_ROOT='/fake/not/real/directory', + STATIC_ROOT=self.temp_dirs['static_root'], + STATIC_BUILD_MANIFEST_PATH='/fake/not/real.json', + PROD_MODE=True, + HOST_UID='9500', + ): + self.assertSystemCheckRaises( + 'Static build manifest file does not exist: ' '/fake/not/real.json' + ) + + def test_configuration_and_service_issues(self): + """Test combination of configuration and service issues.""" + # Invalid HOST_UID and database charset + self.mock_uid.return_value.pw_uid = 1000 + self.mock_db.return_value.__enter__.return_value.fetchone.return_value = ( + 'character_set_database', + 'utf8mb3', + ) + + with override_settings(HOST_UID=None): + self.assertSystemCheckRaises('Expected user uid to be 9500') diff --git a/src/olympia/devhub/templates/devhub/index.html b/src/olympia/devhub/templates/devhub/index.html index 1402492474c1..8d2782466c96 100644 --- a/src/olympia/devhub/templates/devhub/index.html +++ b/src/olympia/devhub/templates/devhub/index.html @@ -34,7 +34,7 @@ {% endif %} - {% if settings.DEV_MODE %} + {% if settings.OLYMPIA_DEPS == 'development' %} {% if settings.LESS_LIVE_REFRESH %} {% endif %} diff --git a/src/olympia/landfill/management/commands/__init__.py b/src/olympia/landfill/management/commands/__init__.py index e69de29bb2d1..82b9a13c2c5f 100644 --- a/src/olympia/landfill/management/commands/__init__.py +++ b/src/olympia/landfill/management/commands/__init__.py @@ -0,0 +1,11 @@ +from django.core.management.base import BaseCommand, CommandError + +from olympia.core.utils import get_version_json + + +class BaseLandfillCommand(BaseCommand): + def assert_local_dev_mode(self): + if get_version_json().get('version') != 'local': + raise CommandError( + 'This command is only available in local development mode.' + ) diff --git a/src/olympia/landfill/management/commands/fetch_prod_addons.py b/src/olympia/landfill/management/commands/fetch_prod_addons.py index 8bd59cd0c9b2..25172bf768a4 100644 --- a/src/olympia/landfill/management/commands/fetch_prod_addons.py +++ b/src/olympia/landfill/management/commands/fetch_prod_addons.py @@ -1,7 +1,5 @@ import uuid -from django.conf import settings -from django.core.management.base import BaseCommand, CommandError from django.db.transaction import atomic import requests @@ -10,6 +8,7 @@ from olympia.addons.models import Addon from olympia.amo.tests import addon_factory, user_factory from olympia.constants.categories import CATEGORIES +from olympia.landfill.management.commands import BaseLandfillCommand from olympia.users.models import UserProfile @@ -17,7 +16,7 @@ class KeyboardInterruptError(Exception): pass -class Command(BaseCommand): +class Command(BaseLandfillCommand): """Download and save all AMO add-ons public data.""" SEARCH_API_URL = 'https://addons.mozilla.org/api/v5/addons/search/' @@ -43,10 +42,7 @@ def add_arguments(self, parser): ) def handle(self, *args, **options): - if not settings.DEV_MODE: - raise CommandError( - 'As a safety precaution this command only works in DEV_MODE.' - ) + self.assert_local_dev_mode() self.fetch_addon_data(options) def get_max_pages(self, params=None): diff --git a/src/olympia/landfill/management/commands/fetch_prod_versions.py b/src/olympia/landfill/management/commands/fetch_prod_versions.py index 61df3c8ef964..aaf76dcdf9fe 100644 --- a/src/olympia/landfill/management/commands/fetch_prod_versions.py +++ b/src/olympia/landfill/management/commands/fetch_prod_versions.py @@ -1,6 +1,4 @@ -from django.conf import settings from django.core.files.base import ContentFile -from django.core.management.base import BaseCommand, CommandError from django.db.transaction import atomic import requests @@ -8,13 +6,14 @@ from olympia import amo from olympia.addons.models import Addon from olympia.amo.tests import version_factory +from olympia.landfill.management.commands import BaseLandfillCommand class KeyboardInterruptError(Exception): pass -class Command(BaseCommand): +class Command(BaseLandfillCommand): """Download versions for a particular add-on from AMO public data.""" VERSIONS_API_URL = ( @@ -28,10 +27,7 @@ def add_arguments(self, parser): ) def handle(self, *args, **options): - if not settings.DEV_MODE: - raise CommandError( - 'As a safety precaution this command only works in DEV_MODE.' - ) + self.assert_local_dev_mode() self.options = options self.fetch_versions_data() diff --git a/src/olympia/landfill/management/commands/generate_addons.py b/src/olympia/landfill/management/commands/generate_addons.py index 78889a83babe..5bb4f91b4523 100644 --- a/src/olympia/landfill/management/commands/generate_addons.py +++ b/src/olympia/landfill/management/commands/generate_addons.py @@ -1,11 +1,11 @@ from django.conf import settings -from django.core.management.base import BaseCommand, CommandError from django.utils import translation from olympia.landfill.generators import generate_addons +from olympia.landfill.management.commands import BaseLandfillCommand -class Command(BaseCommand): +class Command(BaseLandfillCommand): """ Generate example addons for development/testing purpose. @@ -46,8 +46,7 @@ def add_arguments(self, parser): ) def handle(self, *args, **kwargs): - if not settings.DEV_MODE: - raise CommandError('You can only run this command in DEV_MODE.') + self.assert_local_dev_mode() num = int(kwargs.get('num')) email = kwargs.get('email') diff --git a/src/olympia/landfill/management/commands/generate_default_addons_for_frontend.py b/src/olympia/landfill/management/commands/generate_default_addons_for_frontend.py index d361505d8def..78e15ed5bf15 100644 --- a/src/olympia/landfill/management/commands/generate_default_addons_for_frontend.py +++ b/src/olympia/landfill/management/commands/generate_default_addons_for_frontend.py @@ -1,9 +1,9 @@ -from django.core.management.base import BaseCommand from django.db.models.signals import post_save from django.test.utils import override_settings from django.utils import translation from olympia.addons.models import Addon, update_search_index +from olympia.landfill.management.commands import BaseLandfillCommand from olympia.landfill.serializers import GenerateAddonsSerializer @@ -53,7 +53,7 @@ ] -class Command(BaseCommand): +class Command(BaseLandfillCommand): """ Generate addons used specifically for the Integration Tests. @@ -68,6 +68,7 @@ class Command(BaseCommand): """ def handle(self, *args, **kwargs): + self.assert_local_dev_mode() # Disconnect reindexing for every save, we'll reindex # once all addons were generated post_save.disconnect( diff --git a/src/olympia/landfill/management/commands/generate_themes.py b/src/olympia/landfill/management/commands/generate_themes.py index 2f3f6da40c7f..55554e59ac0e 100644 --- a/src/olympia/landfill/management/commands/generate_themes.py +++ b/src/olympia/landfill/management/commands/generate_themes.py @@ -1,11 +1,11 @@ from django.conf import settings -from django.core.management.base import BaseCommand, CommandError from django.utils import translation from olympia.landfill.generators import generate_themes +from olympia.landfill.management.commands import BaseLandfillCommand -class Command(BaseCommand): +class Command(BaseLandfillCommand): """ Generate example themes for development/testing purpose. @@ -37,8 +37,7 @@ def add_arguments(self, parser): ) def handle(self, *args, **kwargs): - if not settings.DEV_MODE: - raise CommandError('You can only run this command in DEV_MODE.') + self.assert_local_dev_mode() num = int(kwargs.get('num')) email = kwargs.get('email') diff --git a/src/olympia/lib/jingo_minify_helpers.py b/src/olympia/lib/jingo_minify_helpers.py index e27bba689f09..066e803ec5f6 100644 --- a/src/olympia/lib/jingo_minify_helpers.py +++ b/src/olympia/lib/jingo_minify_helpers.py @@ -38,7 +38,7 @@ def get_js_urls(bundle, debug=None): If True, return URLs for individual files instead of the minified bundle. """ - if debug or settings.DEBUG or settings.DEV_MODE: + if debug or settings.DEBUG or not settings.PROD_MODE: return [static(item) for item in settings.MINIFY_BUNDLES['js'][bundle]] else: return [static(f'js/{bundle}-min.js')] @@ -55,7 +55,7 @@ def get_css_urls(bundle, debug=None): If True, return URLs for individual files instead of the minified bundle. """ - if debug or settings.DEBUG or settings.DEV_MODE: + if debug or settings.DEBUG: items = [] for item in settings.MINIFY_BUNDLES['css'][bundle]: should_compile = item.endswith('.less') and getattr( diff --git a/src/olympia/lib/settings_base.py b/src/olympia/lib/settings_base.py index 756056f33a79..9885d4a36285 100644 --- a/src/olympia/lib/settings_base.py +++ b/src/olympia/lib/settings_base.py @@ -15,6 +15,7 @@ import olympia.core.logger import olympia.core.sentry +from olympia.core.utils import get_version_json env = environ.Env() @@ -62,15 +63,6 @@ def path(*folders): return os.path.join(ROOT, *folders) -DEBUG = env('DEBUG', default=False) - -# Do NOT provide a default value, this should be explicitly -# set during the docker image build. If it is not set, -# we want to raise an error. -DOCKER_TARGET = env('DOCKER_TARGET') - -DEV_MODE = False - # Host info that is hard coded for production images. HOST_UID = None @@ -80,9 +72,17 @@ def path(*folders): # In production, nginx serves these files from a CDN. SERVE_STATIC_FILES = False +# Used to determine if we are running on a production image. +# In production environments, this should ALWAYS be True. +PROD_MODE = get_version_json().get('target') == 'production' +# Debug is configurable but defaults to the opposite of prod mode. +DEBUG = env('DEBUG', default=not PROD_MODE) +# Used to determine which set of dependencies are installed. +OLYMPIA_DEPS = env('OLYMPIA_DEPS', default=get_version_json().get('target')) + DEBUG_TOOLBAR_CONFIG = { # Deactivate django debug toolbar by default. - 'SHOW_TOOLBAR_CALLBACK': lambda request: DEBUG, + 'SHOW_TOOLBAR_CALLBACK': lambda request: False, } # Ensure that exceptions aren't re-raised. @@ -727,7 +727,8 @@ def get_db_config(environ_var, atomic_requests=True): 'js/stats/table.js', 'js/stats/stats.js', ), - # This is included when DEV_MODE is True. Bundle in . + # This is included when running on a development image. + # Bundle in . 'debug': ( 'js/debug/less_setup.js', 'less/dist/less.js', @@ -1514,7 +1515,6 @@ def read_only_mode(env): FXA_OAUTH_HOST = 'https://oauth.accounts.firefox.com/v1' FXA_PROFILE_HOST = 'https://profile.accounts.firefox.com/v1' -USE_FAKE_FXA_AUTH = False # Should only be True for local development envs. VERIFY_FXA_ACCESS_TOKEN = True # List all jobs that should be callable with cron here. diff --git a/src/olympia/lib/storage.py b/src/olympia/lib/storage.py index 046f1480aeb3..9c518d471412 100644 --- a/src/olympia/lib/storage.py +++ b/src/olympia/lib/storage.py @@ -24,5 +24,5 @@ class ManifestStaticFilesStorageNotMaps(ManifestStaticFilesStorage): OlympiaStaticFilesStorage = ( - StaticFilesStorage if settings.DEV_MODE else ManifestStaticFilesStorageNotMaps + ManifestStaticFilesStorageNotMaps if settings.PROD_MODE else StaticFilesStorage ) diff --git a/src/olympia/templates/base.html b/src/olympia/templates/base.html index 44c3f842a27f..f5f3d521537a 100644 --- a/src/olympia/templates/base.html +++ b/src/olympia/templates/base.html @@ -32,7 +32,7 @@ - {% if settings.DEV_MODE %} + {% if settings.OLYMPIA_DEPS == 'development' %} {% if settings.LESS_LIVE_REFRESH %} {% endif %} diff --git a/src/olympia/urls.py b/src/olympia/urls.py index 16c081e0b860..90cc96ab92ab 100644 --- a/src/olympia/urls.py +++ b/src/olympia/urls.py @@ -115,14 +115,14 @@ from django.contrib.staticfiles.views import serve as static_serve def serve_static_files(request, path, **kwargs): - if settings.DEV_MODE: - return static_serve( - request, path, insecure=True, show_indexes=True, **kwargs - ) - else: + if settings.PROD_MODE: return serve_static( request, path, document_root=settings.STATIC_ROOT, **kwargs ) + else: + return static_serve( + request, path, insecure=True, show_indexes=True, **kwargs + ) def serve_javascript_catalog(request, locale, **kwargs): with translation.override(locale):