def _upload_templates(self): """Upload all templates to artifacts bucket.""" self._upload_single_template("Inputs", self._templates.inputs) self._upload_single_template("Iam", self._templates.iam) self._upload_single_template("Pipeline", self._templates.pipeline) for name, stage in self._templates.codebuild.items(): self._upload_single_template(VALUE_SEPARATOR.join(("CodeBuild", "Stage", name)), stage)
def _wait_condition( type_name: str, base_name: str ) -> (cloudformation.WaitCondition, cloudformation.WaitConditionHandle): """Construct a wait condition and handle. :param type_name: :param base_name: :return: """ handle = cloudformation.WaitConditionHandle( VALUE_SEPARATOR.join(("Upload", type_name, base_name))) condition = cloudformation.WaitCondition(VALUE_SEPARATOR.join( ("WaitFor", handle.title)), Handle=handle.ref(), Count=1, Timeout=3600) return condition, handle
def reference_name(name: str, value_type: str) -> str: """Build the reference name for a resource. Used in stack outputs and parameters. :param name: Resource name :param value_type: Value type :return: Specific reference name """ return VALUE_SEPARATOR.join((name, value_type))
def resource_name(resource_type: AWSObject, name: str) -> str: """Build the resource logical name for use in stacks. :param resource_type: Resource type :param name: Naive logical name :return: Specific logical name """ type_name = resource_type.resource_type.split("::")[-1] return VALUE_SEPARATOR.join((type_name, name))
def _upload_single_template(self, template_type: str, template: Template): """Upload one template to the artifacts bucket. :param template_type: Template type name :param template: Template to upload """ bucket_name = self._cache.physical_resource_name(self._artifacts_bucket_logical_name()) _LOGGER.debug('Uploading %s template to bucket "%s"', template_type, bucket_name) key = f"templates/{uuid.uuid4()}" body = template.to_json() self._s3.put_object(Bucket=bucket_name, Key=key, Body=body) self._template_urls[VALUE_SEPARATOR.join(("Upload", "Template", template_type))] = key
def build(project: Config, inputs_template: Template, iam_template: Template, pipeline_templates: Pipeline) -> Template: """Construct a core stack template for a stand-alone deployment. :param project: PipeFormer config :param inputs_template: Inputs stack template :param iam_template: IAM stack template :param pipeline_templates: CodePipeline templates :return: Core stack template """ default_tags = project_tags(project) core = Template( Description= f"Core resources for pipeformer-managed project: {project.name}") # Project CMK cmk = core.add_resource(_project_key(project)) # Artifacts Bucket artifacts_bucket = core.add_resource( _bucket(name="Artifacts", cmk_arn=cmk.get_att("Arn"), tags=default_tags)) # Project Bucket project_bucket = core.add_resource( _bucket(name="ProjectResources", cmk_arn=cmk.get_att("Arn"), tags=default_tags)) # Inputs Stack inputs_stack = _wait_condition_stack( base_name="Inputs", parameters={reference_name(cmk.title, "Arn"): cmk.get_att("Arn")}, artifacts_bucket=artifacts_bucket, tags=default_tags, ) core.add_resource(inputs_stack.condition) core.add_resource(inputs_stack.handle) core.add_resource(inputs_stack.stack) # IAM Stack iam_stack = _wait_condition_stack( base_name="Iam", parameters={ reference_name(artifacts_bucket.title, "Arn"): artifacts_bucket.get_att("Arn"), reference_name(project_bucket.title, "Arn"): project_bucket.get_att("Arn"), reference_name(cmk.title, "Arn"): cmk.get_att("Arn"), }, artifacts_bucket=artifacts_bucket, tags=default_tags, ) core.add_resource(iam_stack.condition) core.add_resource(iam_stack.handle) core.add_resource(iam_stack.stack) # Pipeline Stack and Prerequisites pipeline_parameters = { # Buckets reference_name(artifacts_bucket.title, "Name"): artifacts_bucket.ref(), reference_name(project_bucket.title, "Name"): project_bucket.ref(), } pipeline_depends_on = [] # Pass on Inputs and Roles for nested_template, nested_stack in ((inputs_template, inputs_stack), (iam_template, iam_stack)): pipeline_depends_on.append(nested_stack.stack.title) for name in nested_template.outputs.keys(): pipeline_parameters[name] = GetAtt(nested_stack.stack.title, f"Outputs.{name}") # Add waiters for each pipeline stage resource stack template for name in pipeline_templates.stage_templates: stage_name = VALUE_SEPARATOR.join(("CodeBuild", "Stage", name)) condition, handle = _wait_condition("Template", stage_name) core.add_resource(condition) core.add_resource(handle) pipeline_depends_on.append(condition.title) pipeline_parameters[reference_name( VALUE_SEPARATOR.join(("Template", stage_name)), "Url")] = _wait_condition_data_to_s3_url(condition, artifacts_bucket) input_condition, input_handle = _wait_condition("Input", "Values") pipeline_depends_on.append(input_condition.title) core.add_resource(input_condition) core.add_resource(input_handle) pipeline_stack = _wait_condition_stack( base_name="Pipeline", parameters=pipeline_parameters, artifacts_bucket=artifacts_bucket, tags=default_tags, depends_on=pipeline_depends_on, ) core.add_resource(pipeline_stack.condition) core.add_resource(pipeline_stack.handle) core.add_resource(pipeline_stack.stack) return core
def _report_input_values_saved(self): """Report that the input values have all been saved.""" self._succeed_wait_condition(VALUE_SEPARATOR.join(("Upload", "Input", "Values")), "Inputs saved", "complete")
def _wait_for_inputs_stack(self): """Wait until the inputs stack is created.""" self._cache.wait_until_resource_is_complete(VALUE_SEPARATOR.join(("WaitFor", "Upload", "Template", "Inputs"))) self._cache.wait_until_resource_is_complete(self._inputs_stack_logical_name())