Repository: readthedocs/readthedocs.org

Issue:
Support devpi inside docker containers
Devpi[1] is a great tool to help with intermittent connections. While I am traveling (airports/airplanes/spotty cell service), I find myself wanting a local pypi server. Devpi allows for online and offline usage, and so normal day to day usage on solid internet connections should seed a cache that can be used offline.

The problem blocked this from use is that the docker container would need to be created with additional options, forwarding the host (or another docker container hosting devpi). One option around this might be to create a signal that can be caught before running the docker command to create the container, and allow for alteration of the dictionary that we pass to the docker API. This would allow for a custom RTD plugin that would add a link to a devpi container, or an additional forwarded port/etc.


Discussion:

Resolving patch:
diff --git a/readthedocs/doc_builder/environments.py b/readthedocs/doc_builder/environments.py
--- a/readthedocs/doc_builder/environments.py
+++ b/readthedocs/doc_builder/environments.py
@@ -13,6 +13,7 @@
 from datetime import datetime
 
 from readthedocs.core.utils import slugify
+from django.conf import settings
 from django.utils.translation import ugettext_lazy as _, ugettext_noop
 from docker import Client
 from docker.utils import create_host_config
@@ -662,6 +663,42 @@ def get_client(self):
                 )
             )
 
+    def get_container_host_config(self):
+        """
+        Create the ``host_config`` settings for the container.
+
+        It mainly generates the proper path bindings between the Docker
+        container and the Host by mounting them with the proper permissions.
+        Besides, it mounts the ``GLOBAL_PIP_CACHE`` if it's set and we are under
+        ``DEBUG``.
+
+        The object returned is passed to Docker function
+        ``client.create_container``.
+        """
+        binds = {
+            SPHINX_TEMPLATE_DIR: {
+                'bind': SPHINX_TEMPLATE_DIR,
+                'mode': 'ro',
+            },
+            MKDOCS_TEMPLATE_DIR: {
+                'bind': MKDOCS_TEMPLATE_DIR,
+                'mode': 'ro',
+            },
+            self.project.doc_path: {
+                'bind': self.project.doc_path,
+                'mode': 'rw',
+            },
+        }
+
+        if getattr(settings, 'GLOBAL_PIP_CACHE', False) and settings.DEBUG:
+            binds.update({
+                self.project.pip_cache_path: {
+                    'bind': self.project.pip_cache_path,
+                    'mode': 'rw',
+                }
+            })
+        return create_host_config(binds=binds)
+
     @property
     def container_id(self):
         """Return id of container if it is valid."""
@@ -715,20 +752,7 @@ def create_container(self):
                                  exit=DOCKER_TIMEOUT_EXIT_CODE)),
                 name=self.container_id,
                 hostname=self.container_id,
-                host_config=create_host_config(binds={
-                    SPHINX_TEMPLATE_DIR: {
-                        'bind': SPHINX_TEMPLATE_DIR,
-                        'mode': 'ro'
-                    },
-                    MKDOCS_TEMPLATE_DIR: {
-                        'bind': MKDOCS_TEMPLATE_DIR,
-                        'mode': 'ro'
-                    },
-                    self.project.doc_path: {
-                        'bind': self.project.doc_path,
-                        'mode': 'rw'
-                    },
-                }),
+                host_config=self.get_container_host_config(),
                 detach=True,
                 environment=self.environment,
                 mem_limit=self.container_mem_limit,
diff --git a/readthedocs/projects/models.py b/readthedocs/projects/models.py
--- a/readthedocs/projects/models.py
+++ b/readthedocs/projects/models.py
@@ -449,7 +449,7 @@ def checkout_path(self, version=LATEST):
     @property
     def pip_cache_path(self):
         """Path to pip cache."""
-        if getattr(settings, 'GLOBAL_PIP_CACHE', False):
+        if getattr(settings, 'GLOBAL_PIP_CACHE', False) and settings.DEBUG:
             return settings.GLOBAL_PIP_CACHE
         return os.path.join(self.doc_path, '.cache', 'pip')
 


Test patch: