def setUp(self): # create a build graph with 2 function definitions and 2 layer definitions self.build_graph = BuildGraph("build_dir") self.function1_1 = Mock() self.function1_1.inlinecode = None self.function1_2 = Mock() self.function1_2.inlinecode = None self.function2 = Mock() self.function2.inlinecode = None self.function_build_definition1 = FunctionBuildDefinition( "runtime", "codeuri", ZIP, {}) self.function_build_definition1.functions = [ self.function1_1, self.function1_2 ] self.function_build_definition2 = FunctionBuildDefinition( "runtime2", "codeuri", ZIP, {}) self.function_build_definition1.functions = [self.function2] self.build_graph.put_function_build_definition( self.function_build_definition1, Mock(packagetype=ZIP)) self.build_graph.put_function_build_definition( self.function_build_definition2, Mock(packagetype=ZIP)) self.layer_build_definition1 = LayerBuildDefinition( "layer1", "codeuri", "build_method", []) self.layer_build_definition2 = LayerBuildDefinition( "layer2", "codeuri", "build_method", []) self.build_graph.put_layer_build_definition( self.layer_build_definition1, Mock()) self.build_graph.put_layer_build_definition( self.layer_build_definition2, Mock())
def test_layer_build_should_fail_when_no_build_method_is_provided(self, mock_copy_tree, mock_path): given_layer = Mock() given_layer.build_method = None layer_build_definition = LayerBuildDefinition("layer1", "codeuri", "build_method", []) layer_build_definition.layer = given_layer build_graph = Mock(spec=BuildGraph) build_graph.get_layer_build_definitions.return_value = [layer_build_definition] build_graph.get_function_build_definitions.return_value = [] default_build_strategy = DefaultBuildStrategy(build_graph, "build_dir", Mock(), Mock()) self.assertRaises(MissingBuildMethodException, default_build_strategy.build)
def _get_build_graph(self): """ Converts list of functions and layers into a build graph, where we can iterate on each unique build and trigger build :return: BuildGraph, which represents list of unique build definitions """ build_graph = BuildGraph(self._build_dir) functions = self._resources_to_build.functions layers = self._resources_to_build.layers for function in functions: function_build_details = FunctionBuildDefinition( function.runtime, function.codeuri, function.packagetype, function.metadata) build_graph.put_function_build_definition(function_build_details, function) for layer in layers: layer_build_details = LayerBuildDefinition( layer.name, layer.codeuri, layer.build_method, layer.compatible_runtimes) build_graph.put_layer_build_definition(layer_build_details, layer) build_graph.clean_redundant_definitions_and_update( not self._is_building_specific_resource) return build_graph
def test_layer_build_definition_to_toml_table(self): build_definition = LayerBuildDefinition("name", "codeuri", "method", "runtime") build_definition.layer = generate_function() toml_table = _layer_build_definition_to_toml_table(build_definition) self.assertEqual(toml_table[LAYER_NAME_FIELD], build_definition.name) self.assertEqual(toml_table[CODE_URI_FIELD], build_definition.codeuri) self.assertEqual(toml_table[BUILD_METHOD_FIELD], build_definition.build_method) self.assertEqual(toml_table[COMPATIBLE_RUNTIMES_FIELD], build_definition.compatible_runtimes) self.assertEqual(toml_table[LAYER_FIELD], build_definition.layer.name) self.assertEqual(toml_table[SOURCE_MD5_FIELD], build_definition.source_md5)
def setUp(self): # create a build graph with 2 function definitions and 2 layer definitions self.build_graph = BuildGraph("build_dir") self.function1_1 = Mock() self.function1_1.inlinecode = None self.function1_1.get_build_dir = Mock() self.function1_1.full_path = Mock() self.function1_2 = Mock() self.function1_2.inlinecode = None self.function1_2.get_build_dir = Mock() self.function1_2.full_path = Mock() self.function2 = Mock() self.function2.inlinecode = None self.function2.get_build_dir = Mock() self.function2.full_path = Mock() self.function_build_definition1 = FunctionBuildDefinition( "runtime", "codeuri", ZIP, {}) self.function_build_definition2 = FunctionBuildDefinition( "runtime2", "codeuri", ZIP, {}) self.build_graph.put_function_build_definition( self.function_build_definition1, self.function1_1) self.build_graph.put_function_build_definition( self.function_build_definition1, self.function1_2) self.build_graph.put_function_build_definition( self.function_build_definition2, self.function2) self.layer1 = Mock() self.layer2 = Mock() self.layer_build_definition1 = LayerBuildDefinition( "layer1", "codeuri", "build_method", []) self.layer_build_definition2 = LayerBuildDefinition( "layer2", "codeuri", "build_method", []) self.build_graph.put_layer_build_definition( self.layer_build_definition1, self.layer1) self.build_graph.put_layer_build_definition( self.layer_build_definition2, self.layer2)
def _get_build_graph(self, inline_env_vars: Optional[Dict] = None, env_vars_file: Optional[str] = None) -> BuildGraph: """ Converts list of functions and layers into a build graph, where we can iterate on each unique build and trigger build :return: BuildGraph, which represents list of unique build definitions """ build_graph = BuildGraph(self._build_dir) functions = self._resources_to_build.functions layers = self._resources_to_build.layers file_env_vars = {} if env_vars_file: try: with open(env_vars_file, "r", encoding="utf-8") as fp: file_env_vars = json.load(fp) except Exception as ex: raise IOError( "Could not read environment variables overrides from file {}: {}" .format(env_vars_file, str(ex))) from ex for function in functions: container_env_vars = self._make_env_vars(function, file_env_vars, inline_env_vars) function_build_details = FunctionBuildDefinition( function.runtime, function.codeuri, function.packagetype, function.metadata, env_vars=container_env_vars) build_graph.put_function_build_definition(function_build_details, function) for layer in layers: container_env_vars = self._make_env_vars(layer, file_env_vars, inline_env_vars) layer_build_details = LayerBuildDefinition( layer.name, layer.codeuri, layer.build_method, layer.compatible_runtimes, env_vars=container_env_vars) build_graph.put_layer_build_definition(layer_build_details, layer) build_graph.clean_redundant_definitions_and_update( not self._is_building_specific_resource) return build_graph
def build_single_layer_definition( self, layer_definition: LayerBuildDefinition) -> Dict[str, str]: """ Builds single layer definition with caching """ code_dir = str( pathlib.Path(self._base_dir, cast(str, layer_definition.codeuri)).resolve()) source_md5 = dir_checksum(code_dir) cache_function_dir = pathlib.Path(self._cache_dir, layer_definition.uuid) layer_build_result = {} if not cache_function_dir.exists( ) or layer_definition.source_md5 != source_md5: LOG.info( "Cache is invalid, running build and copying resources to layer build definition of %s", layer_definition.uuid, ) build_result = self._delegate_build_strategy.build_single_layer_definition( layer_definition) layer_build_result.update(build_result) if cache_function_dir.exists(): shutil.rmtree(str(cache_function_dir)) layer_definition.source_md5 = source_md5 # Since all the build contents are same for a build definition, just copy any one of them into the cache for _, value in build_result.items(): osutils.copytree(value, cache_function_dir) break else: LOG.info( "Valid cache found, copying previously built resources from layer build definition of %s", layer_definition.uuid, ) # artifacts directory will be created by the builder artifacts_dir = str( pathlib.Path(self._build_dir, layer_definition.layer.full_path)) LOG.debug("Copying artifacts from %s to %s", cache_function_dir, artifacts_dir) osutils.copytree(cache_function_dir, artifacts_dir) layer_build_result[ layer_definition.layer.full_path] = artifacts_dir return layer_build_result