def record_progress(self, build: Build, content: str, layer_id: str, build_id: str = None) -> Tuple[str, str]: """ record build progress to the database :param build: :param content: str or None :param layer_id: :param build_id: :return: """ if build_id: build = self.db.get_build(build_id) base_image_id = build.get_top_layer_id() was_cached = False if not layer_id: # skipped task, it was cached if content: layer_id = self.get_layer(content, base_image_id) builder = self.get_builder(build) if not builder.is_image_present(layer_id): logger.info("layer %s for content %s does not exist", layer_id, content) layer_id = None if not layer_id: return None, None was_cached = True build.record_layer(content, layer_id, base_image_id, cached=was_cached) self.db.record_build(build) return base_image_id, layer_id
def build(self, build: Build): """ build container image :param build: instance of Build """ if not os.path.isfile(build.playbook_path): raise RuntimeError("No such file or directory: %s" % build.playbook_path) build.validate() build.metadata.validate() build.debug = self.debug build.verbose = self.verbose # we have to record as soon as possible self.db.record_build(build) try: builder = self.get_builder(build) builder.sanity_check() # before we start messing with the base image, we need to check for its presence first if not builder.is_base_image_present(): builder.pull() build.pulled = True builder.check_container_creation() # let's record base image as a first layer base_image_id = builder.get_image_id(build.base_image) build.record_layer(None, base_image_id, None, cached=True) a_runner = AnsibleRunner(build.playbook_path, builder, build, debug=self.debug) # we are about to perform the build build.build_start_time = datetime.datetime.now() self.db.record_build(build, build_state=BuildState.IN_PROGRESS) build.python_interpreter = build.python_interpreter or self.db.load_python_interpreter(base_image_id) if not build.python_interpreter: build.python_interpreter = builder.find_python_interpreter() self.db.record_python_interpreter(base_image_id, build.python_interpreter) builder.create() except Exception: self.db.record_build( None, build_id=build.build_id, build_state=BuildState.FAILED, set_finish_time=True ) raise try: try: output = a_runner.build(self.db_path) except ABBuildUnsuccesful as ex: b = self.db.record_build(None, build_id=build.build_id, build_state=BuildState.FAILED, set_finish_time=True) b.log_lines = ex.output.split("\n") self.db.record_build(b) timestamp = datetime.datetime.now().strftime(TIMESTAMP_FORMAT) image_name = build.target_image + "-" + timestamp + "-failed" b.target_image = image_name image_id = builder.commit(image_name) b.final_layer_id = image_id self.record_progress(b, None, image_id) out_logger.info("Image build failed /o\\") out_logger.info("The progress is saved into image '%s'", image_name) raise b = self.db.record_build(None, build_id=build.build_id, build_state=BuildState.DONE, set_finish_time=True) b.log_lines = output # commit the final image and apply all metadata b.final_layer_id = builder.commit(build.target_image, final_image=True) if b.squash: logger.debug("Squashing metadata into a single layer") # reset layers if squashing b.wipe_layers() self.record_progress(b, None, b.final_layer_id) if not b.is_layering_on(): self.record_progress(b, None, b.final_layer_id) else: self.db.record_build(b) out_logger.info("Image '%s' was built successfully \\o/", build.target_image) finally: builder.clean()