ftl/common/builder.py (98 lines of code) (raw):

# Copyright 2017 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import abc import datetime import tarfile import logging import httplib2 from containerregistry.client import docker_creds from containerregistry.client import docker_name from containerregistry.client.v2_2 import docker_image from containerregistry.client.v2_2 import docker_session from containerregistry.client.v2_2 import save from containerregistry.transport import transport_pool from ftl.common import cache from ftl.common import constants from ftl.common import ftl_util # Do not Remove. Fix for strptime not being thread safe. # Initialize datetime in the base class RuntimeBase. The Build calls # datetime.datetime.strptime in threads. # See http://bugs.python.org/issue7980 datetime.datetime.strptime('', '') class Base(object): """Base is an abstract base class representing a container builder. It provides methods for generating runtime layers and an application layer. """ __metaclass__ = abc.ABCMeta # For enforcing that methods are overriden. def __init__(self, ctx): self._ctx = ctx @abc.abstractmethod def Build(self): """Build method encapsulates all layer building and image creation. """ class JustApp(Base): """JustApp is an implementation of a builder that has logic to build an application layer. """ def __init__(self, ctx): super(JustApp, self).__init__(ctx) def Build(self): """Override.""" # this can't be abstract as it is instantiated in tests return class RuntimeBase(JustApp): """RuntimeBase is an abstract base class representing a container builder for runtime applications with dependencies. It provides methods for generating appending layers and caching images """ def __init__(self, ctx, cache_namespace, args, descriptor_files): super(RuntimeBase, self).__init__(ctx) self._cache_namespace = cache_namespace if args.entrypoint: args.entrypoint = args.entrypoint.split(" ") if args.sh_c_prefix: args.entrypoint = ['bash', '-c', " ".join(args.entrypoint)] if args.exposed_ports: args.exposed_ports = args.exposed_ports.split(",") args.cache_key_version = "%s %s" % (args.cache_key_version, args.cache_salt) self._args = args self._base_name = docker_name.Tag(self._args.base, strict=False) self._base_creds = docker_creds.DefaultKeychain.Resolve( self._base_name) self._target_image = docker_name.Tag(self._args.name, strict=False) self._target_creds = docker_creds.DefaultKeychain.Resolve( self._target_image) self._transport = transport_pool.Http( httplib2.Http, size=constants.THREADS) if args.tar_base_image_path: self._base_image = docker_image.FromTarball( args.tar_base_image_path) else: self._base_image = docker_image.FromRegistry( self._base_name, self._base_creds, self._transport) self._base_image.__enter__() cache_repo = args.cache_repository if not cache_repo: cache_repo = self._target_image.as_repository() if args.ttl: ttl = args.ttl else: ttl = ftl_util.get_ttl(descriptor_files, ctx) self._cache = cache.Registry( repo=cache_repo, namespace=self._cache_namespace, creds=self._target_creds, transport=self._transport, ttl=ttl, threads=constants.THREADS, mount=[self._base_name], use_global=args.global_cache, export_stats=args.export_cache_stats, export_location=args.builder_output_path, should_cache=args.cache, should_upload=args.upload) self._descriptor_files = descriptor_files def Build(self): return def StoreImage(self, result_image): with ftl_util.Timing('Uploading final image'): if self._args.output_path: with ftl_util.Timing('Saving tarball image'): with tarfile.open( name=self._args.output_path, mode='w') as tar: save.tarball(self._target_image, result_image, tar) logging.info('{0} tarball located at {1}'.format( str(self._target_image), self._args.output_path)) return if self._args.upload: with ftl_util.Timing('Pushing image to Docker registry'): with docker_session.Push( self._target_image, self._target_creds, self._transport, threads=constants.THREADS, mount=[self._base_name]) as session: logging.info('Pushing final image...') session.upload(result_image) return