-
Notifications
You must be signed in to change notification settings - Fork 25
Add the ability to add a docker config image #33
Changes from 13 commits
827182b
2db1cec
9f17586
d766f41
d93c81c
4d8537d
3ae0685
23cf764
91d49ef
de3e359
b597d3b
73a184a
79f78d2
8e8164e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,12 +5,8 @@ | |
from .find import find_one | ||
from .parser import ParseError | ||
from .parser import parse | ||
from .validation import validate_bool | ||
from .validation import validate_choice | ||
from .validation import validate_directory | ||
from .validation import validate_file | ||
from .validation import validate_string | ||
from .validation import ValidationError | ||
from .validation import (validate_bool, validate_choice, validate_directory, | ||
validate_file, validate_string, ValidationError) | ||
|
||
|
||
__all__ = ( | ||
|
@@ -30,6 +26,23 @@ | |
TYPE_REQUIRED = 'type-required' | ||
PYTHON_INVALID = 'python-invalid' | ||
|
||
DOCKER_DEFAULT_IMAGE = 'readthedocs/build' | ||
DOCKER_DEFAULT_VERSION = '2.0' | ||
# These map to coordisponding 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:latest': { | ||
'python': {'supported_versions': [2, 2.7, 3, 3.3, 3.4, 3.5, 3.6]}, | ||
}, | ||
} | ||
|
||
|
||
class ConfigError(Exception): | ||
|
||
|
@@ -79,6 +92,7 @@ class BuildConfig(dict): | |
'"python.extra_requirements" section must be a list.') | ||
|
||
PYTHON_SUPPORTED_VERSIONS = [2, 2.7, 3, 3.3, 3.4, 3.5, 3.6] | ||
DOCKER_SUPPORTED_VERSIONS = ['1.0', '2.0', 'latest'] | ||
|
||
def __init__(self, env_config, raw_config, source_file, source_position): | ||
self.env_config = env_config | ||
|
@@ -145,6 +159,9 @@ def validate(self): | |
# Validate env_config. | ||
self.validate_output_base() | ||
|
||
# Validate the build environment first | ||
self.validate_build() # Must happen before `validate_python`! | ||
|
||
# Validate raw_config. Order matters. | ||
self.validate_name() | ||
self.validate_type() | ||
|
@@ -205,6 +222,50 @@ def validate_base(self): | |
base = validate_directory(base, base_path) | ||
self['base'] = base | ||
|
||
def validate_build(self): | ||
""" | ||
Validate the build config settings. | ||
|
||
This is a bit complex, | ||
so here is the logic: | ||
|
||
* We take the default image & version if it's specific in the environment | ||
* Then update the _version_ from the users config | ||
* Then append the default _image_, since users can't change this | ||
* Then update the env_config with the settings for that specific image | ||
- This is currently used for a build image -> python version mapping | ||
|
||
This means we can use custom docker _images_, | ||
but can't change the supported _versions_ that users have defined. | ||
""" | ||
# Defaults | ||
if 'build' in self.env_config: | ||
build = self.env_config['build'] | ||
else: | ||
build = {'image': DOCKER_IMAGE} | ||
|
||
# User specified | ||
if 'build' in self.raw_config: | ||
_build = self.raw_config['build'] | ||
if 'image' in _build: | ||
with self.catch_validation_error('build'): | ||
build['image'] = validate_choice( | ||
str(_build['image']), | ||
self.DOCKER_SUPPORTED_VERSIONS, | ||
) | ||
if ':' not in build['image']: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it seems that this condition will be always There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the build image is passed from RTD it will be fully qualified already. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also the default (not from YAML) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if I follow you. If the image is passed from RTD or not from YAML we won't be inside |
||
# Prepend proper image name to user's image name | ||
build['image'] = '{}:{}'.format( | ||
DOCKER_DEFAULT_IMAGE, | ||
build['image'] | ||
) | ||
if build['image'] in DOCKER_IMAGE_SETTINGS: | ||
# Update docker settings from image name | ||
self.env_config.update( | ||
DOCKER_IMAGE_SETTINGS[build['image']] | ||
) | ||
self['build'] = build | ||
|
||
def validate_python(self): | ||
python = { | ||
'use_system_site_packages': False, | ||
|
@@ -280,7 +341,7 @@ def validate_python(self): | |
pass | ||
python['version'] = validate_choice( | ||
version, | ||
self.get_valid_python_versions() | ||
self.get_valid_python_versions(), | ||
) | ||
|
||
self['python'] = python | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -413,6 +413,70 @@ def it_fails_if_base_does_not_exist(tmpdir): | |
assert excinfo.value.code == INVALID_PATH | ||
|
||
|
||
def describe_validate_build(): | ||
|
||
def it_fails_if_build_is_invalid_option(tmpdir): | ||
apply_fs(tmpdir, minimal_config) | ||
build = BuildConfig( | ||
{}, | ||
{'build': {'image': 3.0}}, | ||
source_file=str(tmpdir.join('readthedocs.yml')), | ||
source_position=0) | ||
with raises(InvalidConfig) as excinfo: | ||
build.validate_build() | ||
assert excinfo.value.key == 'build' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just curious, why we don't use something like regular There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is just how this code was written originally. shrug |
||
assert excinfo.value.code == INVALID_CHOICE | ||
|
||
def it_fails_on_python_validation(tmpdir): | ||
apply_fs(tmpdir, minimal_config) | ||
build = BuildConfig( | ||
{}, | ||
{ | ||
'build': {'image': 1.0}, | ||
'python': {'version': '3.3'}, | ||
}, | ||
source_file=str(tmpdir.join('readthedocs.yml')), | ||
source_position=0) | ||
build.validate_build() | ||
with raises(InvalidConfig) as excinfo: | ||
build.validate_python() | ||
assert excinfo.value.key == 'python.version' | ||
assert excinfo.value.code == INVALID_CHOICE | ||
|
||
def it_works_on_python_validation(tmpdir): | ||
apply_fs(tmpdir, minimal_config) | ||
build = BuildConfig( | ||
{}, | ||
{ | ||
'build': {'image': 'latest'}, | ||
'python': {'version': '3.3'}, | ||
}, | ||
source_file=str(tmpdir.join('readthedocs.yml')), | ||
source_position=0) | ||
build.validate_build() | ||
build.validate_python() | ||
|
||
def it_works(tmpdir): | ||
apply_fs(tmpdir, minimal_config) | ||
build = BuildConfig( | ||
{}, | ||
{'build': {'image': 'latest'}}, | ||
source_file=str(tmpdir.join('readthedocs.yml')), | ||
source_position=0) | ||
build.validate_build() | ||
assert build['build']['image'] == 'readthedocs/build:latest' | ||
|
||
def default(tmpdir): | ||
apply_fs(tmpdir, minimal_config) | ||
build = BuildConfig( | ||
{}, | ||
{}, | ||
source_file=str(tmpdir.join('readthedocs.yml')), | ||
source_position=0) | ||
build.validate_build() | ||
assert build['build']['image'] == 'readthedocs/build:2.0' | ||
|
||
|
||
def test_build_validate_calls_all_subvalidators(tmpdir): | ||
apply_fs(tmpdir, minimal_config) | ||
build = BuildConfig( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
mock==1.3.0 | ||
pytest>=2.7 | ||
pytest-describe>=0.10.1 | ||
pytest==3.2.5 | ||
pytest-describe==0.11.0 | ||
pytest-xdist>=1.12 | ||
tox>=2.1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shoulnd't we have the same
.update()
we have in .org?https://github.com/rtfd/readthedocs.org/pull/3339/files#diff-3fbfb50cb375355069e83003f500d2c8R44
or at least, the
getattr
since otherwise there is no way to override this.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, fixed that below -- I don't want this code to depend on RTD settings, so we pass it in.