feat(codebuild): add generic ImageBuild resource (builds + pushes an image from a supplied Dockerfile)#135
Open
JeroenSoeters wants to merge 9 commits into
Open
feat(codebuild): add generic ImageBuild resource (builds + pushes an image from a supplied Dockerfile)#135JeroenSoeters wants to merge 9 commits into
JeroenSoeters wants to merge 9 commits into
Conversation
…tom agent image Add AWS::CodeBuild::AgentImage, a synthetic build-during-apply resource that generates a Dockerfile and buildspec from a base image plus version-pinned plugins, runs an AWS CodeBuild build, and exposes the pushed image's immutable digest reference (imageRef/imageDigest) along with imageUri/imageTag as resolvable outputs a consumer can pin to. The build runs asynchronously via the existing Create->InProgress + Status-poll pattern, so no new SDK primitive is needed. The CodeBuild project and IAM service role are created and torn down internally (the role is overridable via serviceRoleArn); rebuilds are gated by a build-config hash over the build-affecting inputs so inconsequential edits do not trigger a rebuild. Also expose repositoryUri (plus arn and repositoryName) as resolvables on AWS::ECR::Repository so a repository can be wired into the build as its push target.
The AgentImage build pushes an image into the target ECR repository, but delete only removed the build project and service role, leaving the image behind. A co-managed ECR repository can then not be torn down: its delete fails because it still contains that image. Setting emptyOnDelete on the repository does not help — it is a writeOnly property and the CloudControl delete path carries only the identifier, so the value never reaches the delete handler. Mirror the S3 Object pattern: the resource that put content into a container removes it on delete. AgentImage delete now removes the image it pushed, scoped to exactly its own tag, so it never touches images pushed by anything else. A missing image or repository is treated as success.
Make the AgentImage lifecycle robust on the update and delete paths: - Update skips a rebuild only when the declared tag still resolves to the recorded digest, so a tag moved out of band is reconciled rather than reported as unchanged. - Delete tolerates an already-gone CodeBuild project so a partially completed delete can be retried through image and role cleanup. - Delete removes the internal service role only when it exists under the plugin's deterministic name, leaving a caller-supplied (BYO) role untouched and not failing teardown when the agent has no IAM access.
…resource Rename AWS::CodeBuild::AgentImage to AWS::CodeBuild::ImageBuild and decouple it from the formae agent. The resource now takes a literal `dockerfile` string (plus optional `buildArgs`) and builds whatever Dockerfile it is handed, instead of the former `baseImage` + `plugins` inputs that generated a formae-agent Dockerfile (formae plugin install, /home/pel plugin cache cleanup, chown /opt/pel, USER pel). The agent Dockerfile composition moves to consumers; the AWS plugin no longer references formae plugins, pelmgr, `formae plugin install`, or the `pel` user. - Pkl: agentimage.pkl -> imagebuild.pkl; drop the Plugin subresource and PluginChannel typealias; add required `dockerfile` and optional `buildArgs`. - Go: agentimage.go -> imagebuild.go; remove generateDockerfile(); the buildspec materializes the Dockerfile from DOCKERFILE_B64 verbatim and threads buildArgs through as repeated --build-arg flags via BUILD_ARGS_B64. - buildConfigHash now hashes dockerfile + buildArgs (canonical) + generatorVersion + computeType + buildEnvironmentImage. - All reusable orchestration is kept: CodeBuild project/role lifecycle, digest capture, async Create->Status, BYO-role handling, delete-empties- own-image, IAM policy derived from the buildspec. - Fixture and unit tests updated to the new inputs/names.
Require the ECR push target to be in the build project's own region and reject a mismatch with a clear error: the CodeBuild project, its log group, its IAM policy, and the ECR API clients all run in the target region, so a cross-region repository would build but then fail to log or to be read and deleted. Read build args into the shell's positional parameters and pass them as quoted --build-arg flags, so a value containing spaces survives intact instead of being word-split. Export the computed image reference in post_build so CodeBuild's exported-variables reliably collects it.
An in-place rebuild moves the tag to a new digest and leaves the previous manifest behind untagged. Delete only removes the current tag, so a co-managed ECR repository rebuilt in place could still hold the stale manifest and fail to be torn down. Carry the prior digest through the build and, once the new build succeeds, delete the prior manifest when it is no longer referenced by any tag.
The CodeBuild log group is created in the target account and the service role is granted logs only there, so a push target in a different account would fail once the build starts. Reject an account mismatch up front, alongside the existing region check, and document the same-account and region requirement on the schema.
The generated Dockerfile and build_args.env were written into the build context, so a Dockerfile with a broad COPY (e.g. COPY . .) would embed the build-arg values into the image. Build from an empty context directory with the Dockerfile passed via -f, keeping both helper files out of the context.
BatchDeleteImage returns an HTTP success with per-image problems reported in Failures, so an unchecked call could report a still-present image as deleted and break repository teardown. Inspect Failures (tolerating an already-gone image) in both the delete and the rebuild-prune paths. Drop the service-role-derived account check: it ran only after the internal role was created, so a cross-account rejection would orphan that role. The same-account requirement stays documented on the schema and is enforced in practice by the region check and the account-scoped IAM policy; a cross-account target fails at build time rather than silently.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a native AWS-plugin resource,
AWS::CodeBuild::ImageBuild, that builds a container image from a supplied Dockerfile via AWS CodeBuild, pushes it to ECR, and exposes the pushed image's immutable digest reference as resolvables a downstream resource (an ECS TaskDefinition, a k8s Pod) can consume. This makes an image build a declarative step inside formae instead of an out-of-banddocker build/push, so a consumer can pin to exactly the image that was produced.Create → InProgress+Status-poll →Successpattern, so it needs no new SDK primitive. Create validates its inputs and idempotently ensures an internal IAM service role + CodeBuild project (or uses a suppliedserviceRoleArn), starts the build, and returns in-progress; Status reads the digest captured from the specific push once the build succeeds.dockerfile(built verbatim),ecrRepositoryUri(a resolvable from anAWS::ECR::Repository, which gains arepositoryUriresolvable here),imageTag, optionalbuildArgs, and build tuning (computeType,timeoutMinutes,buildEnvironmentImage, and a bring-your-ownserviceRoleArn).res.accessor):imageRef(<repo>@sha256:…, the immutable primary reference and the resource identity),imageDigest,imageUri(<repo>:<tag>, a mutable convenience alias), andimageTag.buildConfigHashover the build-affecting inputs gates rebuilds: Create/Update rebuild only when that hash changes, when the recorded digest is absent from ECR, or when the declared tag no longer resolves to the recorded digest — otherwise they no-op and return the existing outputs.ecrRepositoryUriandimageTagare identity (createOnly, so changing them forces a replace); the Dockerfile, build args, and build tuning update in place.