diff options
Diffstat (limited to 'docker/api/build.py')
-rw-r--r-- | docker/api/build.py | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/docker/api/build.py b/docker/api/build.py new file mode 100644 index 0000000..b303ba6 --- /dev/null +++ b/docker/api/build.py @@ -0,0 +1,132 @@ +import logging +import os +import re + +from .. import constants +from .. import errors +from .. import auth +from .. import utils + + +log = logging.getLogger(__name__) + + +class BuildApiMixin(object): + def build(self, path=None, tag=None, quiet=False, fileobj=None, + nocache=False, rm=False, stream=False, timeout=None, + custom_context=False, encoding=None, pull=False, + forcerm=False, dockerfile=None, container_limits=None, + decode=False): + remote = context = headers = None + container_limits = container_limits or {} + if path is None and fileobj is None: + raise TypeError("Either path or fileobj needs to be provided.") + + for key in container_limits.keys(): + if key not in constants.CONTAINER_LIMITS_KEYS: + raise errors.DockerException( + 'Invalid container_limits key {0}'.format(key) + ) + + if custom_context: + if not fileobj: + raise TypeError("You must specify fileobj with custom_context") + context = fileobj + elif fileobj is not None: + context = utils.mkbuildcontext(fileobj) + elif path.startswith(('http://', 'https://', + 'git://', 'github.com/', 'git@')): + remote = path + elif not os.path.isdir(path): + raise TypeError("You must specify a directory to build in path") + else: + dockerignore = os.path.join(path, '.dockerignore') + exclude = None + if os.path.exists(dockerignore): + with open(dockerignore, 'r') as f: + exclude = list(filter(bool, f.read().splitlines())) + context = utils.tar(path, exclude=exclude, dockerfile=dockerfile) + + if utils.compare_version('1.8', self._version) >= 0: + stream = True + + if dockerfile and utils.compare_version('1.17', self._version) < 0: + raise errors.InvalidVersion( + 'dockerfile was only introduced in API version 1.17' + ) + + if utils.compare_version('1.19', self._version) < 0: + pull = 1 if pull else 0 + + u = self._url('/build') + params = { + 't': tag, + 'remote': remote, + 'q': quiet, + 'nocache': nocache, + 'rm': rm, + 'forcerm': forcerm, + 'pull': pull, + 'dockerfile': dockerfile, + } + params.update(container_limits) + + if context is not None: + headers = {'Content-Type': 'application/tar'} + if encoding: + headers['Content-Encoding'] = encoding + + if utils.compare_version('1.9', self._version) >= 0: + self._set_auth_headers(headers) + + response = self._post( + u, + data=context, + params=params, + headers=headers, + stream=stream, + timeout=timeout, + ) + + if context is not None and not custom_context: + context.close() + + if stream: + return self._stream_helper(response, decode=decode) + else: + output = self._result(response) + srch = r'Successfully built ([0-9a-f]+)' + match = re.search(srch, output) + if not match: + return None, output + return match.group(1), output + + def _set_auth_headers(self, headers): + log.debug('Looking for auth config') + + # If we don't have any auth data so far, try reloading the config + # file one more time in case anything showed up in there. + if not self._auth_configs: + log.debug("No auth config in memory - loading from filesystem") + self._auth_configs = auth.load_config() + + # Send the full auth configuration (if any exists), since the build + # could use any (or all) of the registries. + if self._auth_configs: + log.debug( + 'Sending auth config ({0})'.format( + ', '.join(repr(k) for k in self._auth_configs.keys()) + ) + ) + if headers is None: + headers = {} + if utils.compare_version('1.19', self._version) >= 0: + headers['X-Registry-Config'] = auth.encode_header( + self._auth_configs + ) + else: + headers['X-Registry-Config'] = auth.encode_header({ + 'configs': self._auth_configs + }) + else: + log.debug('No auth config found') |