Repository: readthedocs/readthedocs.org

Issue:
Use default settings for Config object
Our current `Config` object is using hardcoded values to define some DOCKER settings:

https://github.com/rtfd/readthedocs.org/blob/8466fa45a2fb1b813ef0272b1154e0c98fe62218/readthedocs/config/config.py#L60-L81

This should follow the pattern that we were using at 

https://github.com/rtfd/readthedocs.org/blob/8466fa45a2fb1b813ef0272b1154e0c98fe62218/readthedocs/doc_builder/constants.py#L37-L44

This way, we can modify them by setting the values as a setting and also allow to _override them_ from outside (like `local_settings.py`)

Related to #2140


Discussion:
The same happens at

https://github.com/rtfd/readthedocs.org/blob/8466fa45a2fb1b813ef0272b1154e0c98fe62218/readthedocs/config/config.py#L278-L279
Also, modifying `PYTHON_SUPPORTED_VERSIONS` has no effect because it then uses 

https://github.com/rtfd/readthedocs.org/blob/8466fa45a2fb1b813ef0272b1154e0c98fe62218/readthedocs/config/config.py#L283-L289

and returns at line 286 where the class variable was not used.

I found this flow kind of confusing and it's hard to know where we need to touch to get a new value supported.

It would be good if we can centralize this on only one setting/variable.

Resolving patch:
diff --git a/readthedocs/config/config.py b/readthedocs/config/config.py
--- a/readthedocs/config/config.py
+++ b/readthedocs/config/config.py
@@ -6,6 +6,8 @@
 import os
 from contextlib import contextmanager
 
+from django.conf import settings
+
 from readthedocs.config.utils import list_to_dict, to_dict
 from readthedocs.projects.constants import DOCUMENTATION_CHOICES
 
@@ -61,28 +63,16 @@
 INVALID_KEYS_COMBINATION = 'invalid-keys-combination'
 INVALID_KEY = 'invalid-key'
 
-DOCKER_DEFAULT_IMAGE = 'readthedocs/build'
-DOCKER_DEFAULT_VERSION = '2.0'
+DOCKER_DEFAULT_IMAGE = getattr(settings, 'DOCKER_DEFAULT_IMAGE', 'readthedocs/build')
+DOCKER_DEFAULT_VERSION = getattr(settings, 'DOCKER_DEFAULT_VERSION', '2.0')
 # These map to corresponding settings in the .org,
 # so they haven't been renamed.
-DOCKER_IMAGE = '{}:{}'.format(DOCKER_DEFAULT_IMAGE, DOCKER_DEFAULT_VERSION)
-DOCKER_IMAGE_SETTINGS = {
-    'readthedocs/build:1.0': {
-        'python': {'supported_versions': [2, 2.7, 3, 3.4]},
-    },
-    'readthedocs/build:2.0': {
-        'python': {'supported_versions': [2, 2.7, 3, 3.5]},
-    },
-    'readthedocs/build:3.0': {
-        'python': {'supported_versions': [2, 2.7, 3, 3.3, 3.4, 3.5, 3.6]},
-    },
-    'readthedocs/build:stable': {
-        'python': {'supported_versions': [2, 2.7, 3, 3.3, 3.4, 3.5, 3.6]},
-    },
-    'readthedocs/build:latest': {
-        'python': {'supported_versions': [2, 2.7, 3, 3.3, 3.4, 3.5, 3.6]},
-    },
-}
+DOCKER_IMAGE = getattr(
+    settings,
+    'DOCKER_IMAGE',
+    '{}:{}'.format(DOCKER_DEFAULT_IMAGE, DOCKER_DEFAULT_VERSION)
+)
+DOCKER_IMAGE_SETTINGS = getattr(settings, 'DOCKER_IMAGE_SETTINGS', {})
 
 
 class ConfigError(Exception):
diff --git a/readthedocs/settings/base.py b/readthedocs/settings/base.py
--- a/readthedocs/settings/base.py
+++ b/readthedocs/settings/base.py
@@ -271,7 +271,26 @@ def USE_PROMOS(self):  # noqa
 
     # Docker
     DOCKER_ENABLE = False
-    DOCKER_IMAGE = 'readthedocs/build:2.0'
+    DOCKER_DEFAULT_IMAGE = 'readthedocs/build'
+    DOCKER_DEFAULT_VERSION = '2.0'
+    DOCKER_IMAGE = '{}:{}'.format(DOCKER_DEFAULT_IMAGE, DOCKER_DEFAULT_VERSION)
+    DOCKER_IMAGE_SETTINGS = {
+        'readthedocs/build:1.0': {
+            'python': {'supported_versions': [2, 2.7, 3, 3.4]},
+        },
+        'readthedocs/build:2.0': {
+            'python': {'supported_versions': [2, 2.7, 3, 3.5]},
+        },
+        'readthedocs/build:3.0': {
+            'python': {'supported_versions': [2, 2.7, 3, 3.3, 3.4, 3.5, 3.6]},
+        },
+        'readthedocs/build:stable': {
+            'python': {'supported_versions': [2, 2.7, 3, 3.3, 3.4, 3.5, 3.6]},
+        },
+        'readthedocs/build:latest': {
+            'python': {'supported_versions': [2, 2.7, 3, 3.3, 3.4, 3.5, 3.6]},
+        },
+    }
 
     # All auth
     ACCOUNT_ADAPTER = 'readthedocs.core.adapters.AccountAdapter'


Test patch:
diff --git a/readthedocs/rtd_tests/tests/test_config_integration.py b/readthedocs/rtd_tests/tests/test_config_integration.py
--- a/readthedocs/rtd_tests/tests/test_config_integration.py
+++ b/readthedocs/rtd_tests/tests/test_config_integration.py
@@ -24,6 +24,7 @@
 from readthedocs.projects import tasks
 from readthedocs.projects.models import Feature, Project
 from readthedocs.rtd_tests.utils import create_git_submodule, make_git_repo
+from doc_builder.constants import DOCKER_IMAGE_SETTINGS
 
 
 def create_load(config=None):
@@ -84,30 +85,34 @@ def test_python_supported_versions_default_image_1_0(self, load_config):
         self.project.enable_pdf_build = True
         self.project.save()
         config = load_yaml_config(self.version)
-        self.assertEqual(load_config.call_count, 1)
-        load_config.assert_has_calls([
-            mock.call(
+
+        expected_env_config = {
+            'allow_v2': mock.ANY,
+            'build': {'image': 'readthedocs/build:1.0'},
+            'defaults': {
+                'install_project': self.project.install_project,
+                'formats': [
+                    'htmlzip',
+                    'epub',
+                    'pdf'
+                ],
+                'use_system_packages': self.project.use_system_packages,
+                'requirements_file': self.project.requirements_file,
+                'python_version': 2,
+                'sphinx_configuration': mock.ANY,
+                'build_image': 'readthedocs/build:1.0',
+                'doctype': self.project.documentation_type,
+            },
+        }
+
+        img_settings = DOCKER_IMAGE_SETTINGS.get(self.project.container_image, None)
+        if img_settings:
+            expected_env_config.update(img_settings)
+
+        load_config.assert_called_once_with(
                 path=mock.ANY,
-                env_config={
-                    'allow_v2': mock.ANY,
-                    'build': {'image': 'readthedocs/build:1.0'},
-                    'defaults': {
-                        'install_project': self.project.install_project,
-                        'formats': [
-                            'htmlzip',
-                            'epub',
-                            'pdf',
-                        ],
-                        'use_system_packages': self.project.use_system_packages,
-                        'requirements_file': self.project.requirements_file,
-                        'python_version': 2,
-                        'sphinx_configuration': mock.ANY,
-                        'build_image': 'readthedocs/build:1.0',
-                        'doctype': self.project.documentation_type,
-                    },
-                },
-            ),
-        ])
+                env_config=expected_env_config,
+        )
         self.assertEqual(config.python.version, 2)
 
     @mock.patch('readthedocs.doc_builder.config.load_config')