diff --git a/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/ContainerTagCommand.java b/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/ContainerTagCommand.java index d5aa2412d7..d57ffe30a7 100644 --- a/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/ContainerTagCommand.java +++ b/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/ContainerTagCommand.java @@ -27,8 +27,8 @@ public class ContainerTagCommand implements Runnable { @CommandLine.Option(names = "--registry-prepend-tag", defaultValue = "") String prependTag; - @CommandLine.Option(names = "--image-id", required = true) - String imageId; + @CommandLine.Option(names = "--image-digest", required = true) + String imageDigest; @CommandLine.Parameters(split = ",") List gavs; @@ -41,9 +41,9 @@ public void run() { ContainerRegistryDeployer deployer = new ContainerRegistryDeployer(host, port, owner, token.orElse(""), repository, insecure, - prependTag, imageId); + prependTag); try { - deployer.tagArchive(gavs); + deployer.tagArchive(imageDigest, gavs); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/DeployCommand.java b/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/DeployCommand.java index b0530c83e2..30444987ee 100644 --- a/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/DeployCommand.java +++ b/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/DeployCommand.java @@ -59,6 +59,7 @@ public class DeployCommand implements Runnable { private static final String DOT_POM = ".pom"; private static final String DOT = "."; private static final Set ALLOWED_CONTAMINANTS = Set.of("-tests.jar"); + public static final String IMAGE_DIGEST_OUTPUT = "Image Digest: "; final BeanManager beanManager; final ResultsUpdater resultsUpdater; @@ -133,6 +134,8 @@ public class DeployCommand implements Runnable { @CommandLine.Option(names = "--git-identity") String gitIdentity; + @CommandLine.Option(names = "--build-id") + String buildId; // Testing only ; used to disable image deployment protected boolean imageDeployment = true; @@ -214,7 +217,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO + gav.getVersion(), "rebuilt", Map.of("scm-uri", scmUri, "scm-commit", commit, "hermetic", - Boolean.toString(hermetic))), + Boolean.toString(hermetic), "build-id", buildId)), Files.newOutputStream(temp), false); Files.delete(file); Files.move(temp, file); @@ -305,6 +308,9 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { } else { Log.errorf("Skipped deploying from task run %s as all artifacts were contaminated", taskRun); } + if (imageDigest != null) { + System.out.println(IMAGE_DIGEST_OUTPUT + "sha256:" + imageDigest); + } if (taskRun != null) { List newContaminates = new ArrayList<>(); @@ -383,15 +389,15 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) th protected void doDeployment(Path sourcePath, Path logsPath, Set gavs) throws Exception { if (imageDeployment) { ContainerRegistryDeployer deployer = new ContainerRegistryDeployer(host, port, owner, token.orElse(""), repository, - insecure, prependTag, - imageId); - deployer.deployArchive(deploymentPath, sourcePath, logsPath, gavs, new BiConsumer() { - @Override - public void accept(String s, String hash) { - imageName = s; - imageDigest = hash; - } - }); + insecure, prependTag); + deployer.deployArchive(deploymentPath, sourcePath, logsPath, gavs, imageId, buildId, + new BiConsumer() { + @Override + public void accept(String s, String hash) { + imageName = s; + imageDigest = hash; + } + }); } if (isNotEmpty(mvnRepo)) { // Maven Repo Deployment diff --git a/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/DeployHermeticPreBuildImageCommand.java b/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/DeployHermeticPreBuildImageCommand.java index 6e877b63b4..632674f46f 100644 --- a/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/DeployHermeticPreBuildImageCommand.java +++ b/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/DeployHermeticPreBuildImageCommand.java @@ -49,7 +49,7 @@ public class DeployHermeticPreBuildImageCommand implements Runnable { public void run() { ContainerRegistryDeployer deployer = new ContainerRegistryDeployer(host, port, owner, token.orElse(""), repository, insecure, - prependTag, ""); + prependTag); try { deployer.deployHermeticPreBuildImage(builderImage, buildArtifactsPath, repositoryPath, imageSourcePath, imageName, new BiConsumer() { diff --git a/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/DeployPreBuildImageCommand.java b/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/DeployPreBuildImageCommand.java index 28d10c1eb0..86c425d9a0 100644 --- a/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/DeployPreBuildImageCommand.java +++ b/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/DeployPreBuildImageCommand.java @@ -48,7 +48,7 @@ public class DeployPreBuildImageCommand implements Runnable { public void run() { ContainerRegistryDeployer deployer = new ContainerRegistryDeployer(host, port, owner, token.orElse(""), repository, insecure, - prependTag, ""); + prependTag); try { deployer.deployPreBuildImage(builderImage, sourcePath, imageSourcePath, imageName, new BiConsumer() { diff --git a/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/containerregistry/ContainerRegistryDeployer.java b/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/containerregistry/ContainerRegistryDeployer.java index 18a51c6e5f..a3cfc2deef 100644 --- a/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/containerregistry/ContainerRegistryDeployer.java +++ b/java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/deploy/containerregistry/ContainerRegistryDeployer.java @@ -12,7 +12,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; @@ -56,8 +55,6 @@ public class ContainerRegistryDeployer { private final Credential credential; - final String imageId; - public ContainerRegistryDeployer( String host, int port, @@ -65,7 +62,7 @@ public ContainerRegistryDeployer( String token, String repository, boolean insecure, - String prependTag, String imageId) { + String prependTag) { if (insecure) { System.setProperty("sendCredentialsOverHttp", "true"); } @@ -76,7 +73,6 @@ public ContainerRegistryDeployer( this.repository = repository; this.insecure = insecure; this.prependTag = prependTag; - this.imageId = imageId; String fullName = host + (port == 443 ? "" : ":" + port) + "/" + owner + "/" + repository; this.credential = ContainerUtil.processToken(fullName, token); @@ -86,7 +82,7 @@ public ContainerRegistryDeployer( Log.infof("Prepend tag is %s", prependTag); } - public void deployArchive(Path deployDir, Path sourcePath, Path logsPath, Set gavs, + public void deployArchive(Path deployDir, Path sourcePath, Path logsPath, Set gavs, String imageId, String buildId, BiConsumer imageNameHashCallback) throws Exception { Log.debugf("Using Container registry %s:%d/%s/%s", host, port, owner, repository); @@ -94,10 +90,10 @@ public void deployArchive(Path deployDir, Path sourcePath, Path logsPath, Set gavNames) throws Exception { + public void tagArchive(String imageDigest, List gavNames) throws Exception { if (gavNames.isEmpty()) { throw new RuntimeException("Empty GAV list"); } @@ -107,7 +103,7 @@ public void tagArchive(List gavNames) throws Exception { gavs.push(Gav.parse(i)); } Gav first = gavs.pop(); - String existingImage = createImageName(imageId); + String existingImage = createImageNameFromDigest(imageDigest); RegistryImage existingRegistryImage = RegistryImage.named(existingImage); RegistryImage registryImage = RegistryImage.named(createImageName(first.getTag())); if (credential != null) { @@ -221,17 +217,18 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO } private void createImages(DeployData imageData, Path sourcePath, Path logsPath, - BiConsumer imageNameHashCallback) + String imageId, String buildId, BiConsumer imageNameHashCallback) throws InvalidImageReferenceException, InterruptedException, RegistryException, IOException, CacheDirectoryCreationException, ExecutionException { - String imageName = createImageName(); + String imageName = createImageName(buildId); RegistryImage registryImage = RegistryImage.named(imageName); if (credential != null) { registryImage = registryImage.addCredentialRetriever(() -> Optional.of(credential)); } Containerizer containerizer = Containerizer .to(registryImage) + .withAdditionalTag(imageId) .setAllowInsecureRegistries(insecure); Log.infof("Deploying base image %s", imageName); @@ -260,11 +257,6 @@ private void createImages(DeployData imageData, Path sourcePath, Path logsPath, } } - private String createImageName() { - String tag = imageId == null ? UUID.randomUUID().toString() : imageId; - return createImageName(tag); - } - private String createImageName(String tag) { // As the tests utilise prependTag for uniqueness so check for that // here to avoid reusing images when we want differentiation. @@ -284,6 +276,15 @@ private String createImageName(String tag) { + ":" + tag; } + private String createImageNameFromDigest(String digest) { + if (port == 443) { + return host + "/" + owner + "/" + repository + + "@" + digest; + } + return host + ":" + port + "/" + owner + "/" + repository + + "@" + digest; + } + private List getLayers(Path artifacts, Path source, Path logs) { Log.debug("\n Container details:\n" + "\t layer 1 (source) " + source.toString() + "\n" diff --git a/java-components/build-request-processor/src/test/java/com/redhat/hacbs/container/analyser/deploy/ContainerRegistryDeployerTest.java b/java-components/build-request-processor/src/test/java/com/redhat/hacbs/container/analyser/deploy/ContainerRegistryDeployerTest.java index fb6c746127..7014048e9d 100644 --- a/java-components/build-request-processor/src/test/java/com/redhat/hacbs/container/analyser/deploy/ContainerRegistryDeployerTest.java +++ b/java-components/build-request-processor/src/test/java/com/redhat/hacbs/container/analyser/deploy/ContainerRegistryDeployerTest.java @@ -11,6 +11,8 @@ import java.nio.file.Paths; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; @@ -81,6 +83,7 @@ public void testDeployArchive(QuarkusMainLauncher launcher) throws IOException { "--image-id=test-image", "--registry-host=" + container.getHost(), "--registry-port=" + port, + "--build-id=test-id", "--registry-owner=" + OWNER, "--registry-repository=" + REPOSITORY, "--source-path=" + source.toAbsolutePath().toString(), @@ -92,18 +95,24 @@ public void testDeployArchive(QuarkusMainLauncher launcher) throws IOException { Assertions.assertEquals(0, result.exitCode()); // Now we validate that the image and tags exist in the registry ContainerRegistryDetails containerRegistryDetails = getContainerRegistryDetails(); - Assertions.assertTrue(containerRegistryDetails.repoName.startsWith(OWNER + "/" + REPOSITORY)); Assertions.assertTrue(containerRegistryDetails.tags.contains("test-image")); + Assertions.assertTrue(containerRegistryDetails.tags.contains("test-id")); Assertions.assertFalse(containerRegistryDetails.tags.contains(EXPECTED_TAG_1)); Assertions.assertFalse(containerRegistryDetails.tags.contains(EXPECTED_TAG_2)); + System.out.println(result.getOutput()); + Pattern p = Pattern.compile(DeployCommand.IMAGE_DIGEST_OUTPUT + "(.*)"); + Matcher matcher = p.matcher(result.getOutput()); + Assertions.assertTrue(matcher.find()); + String digest = matcher.group(1); + result = launcher.launch("tag-container", "--registry-host=" + container.getHost(), "--registry-port=" + port, "--registry-owner=" + OWNER, "--registry-repository=" + REPOSITORY, "--registry-insecure", - "--image-id=test-image", + "--image-digest=" + digest, GROUP + ":" + FOO_BAR + ":" + VERSION + "," + GROUP + ":" + FOO_BAZ + ":" + VERSION); containerRegistryDetails = getContainerRegistryDetails(); Assertions.assertTrue(containerRegistryDetails.tags.contains(EXPECTED_TAG_1)); @@ -184,6 +193,7 @@ private URL getRegistryURL(String path) throws IOException { class ContainerRegistryDetails { String repoName; + String digest; List tags; } } diff --git a/pkg/apis/jvmbuildservice/v1alpha1/dependencybuild_types.go b/pkg/apis/jvmbuildservice/v1alpha1/dependencybuild_types.go index 19d222fa3c..6172c87134 100644 --- a/pkg/apis/jvmbuildservice/v1alpha1/dependencybuild_types.go +++ b/pkg/apis/jvmbuildservice/v1alpha1/dependencybuild_types.go @@ -80,8 +80,9 @@ type DependencyBuildList struct { } type BuildAttempt struct { - Recipe *BuildRecipe `json:"buildRecipe,omitempty"` - Build *BuildPipelineRun `json:"build,omitempty"` + BuildId string `json:"buildId,omitempty"` + Recipe *BuildRecipe `json:"buildRecipe,omitempty"` + Build *BuildPipelineRun `json:"build,omitempty"` } type BuildPipelineRun struct { diff --git a/pkg/reconciler/dependencybuild/buildrecipeyaml.go b/pkg/reconciler/dependencybuild/buildrecipeyaml.go index cdd868789a..bbcc46b3fe 100644 --- a/pkg/reconciler/dependencybuild/buildrecipeyaml.go +++ b/pkg/reconciler/dependencybuild/buildrecipeyaml.go @@ -22,6 +22,7 @@ const ( MavenArtifactsPath = "/maven-artifacts" PreBuildImageDigest = "PRE_BUILD_IMAGE_DIGEST" HermeticPreBuildImageDigest = "HERMETIC_PRE_BUILD_IMAGE_DIGEST" + DeployedImageDigest = "DEPLOYED_IMAGE_DIGEST" ) //go:embed scripts/maven-build.sh @@ -53,7 +54,7 @@ var buildEntryScript string //go:embed scripts/hermetic-entry.sh var hermeticBuildEntryScript string -func createPipelineSpec(tool string, commitTime int64, jbsConfig *v1alpha12.JBSConfig, systemConfig *v1alpha12.SystemConfig, recipe *v1alpha12.BuildRecipe, db *v1alpha12.DependencyBuild, paramValues []pipelinev1beta1.Param, buildRequestProcessorImage string) (*pipelinev1beta1.PipelineSpec, string, error) { +func createPipelineSpec(tool string, commitTime int64, jbsConfig *v1alpha12.JBSConfig, systemConfig *v1alpha12.SystemConfig, recipe *v1alpha12.BuildRecipe, db *v1alpha12.DependencyBuild, paramValues []pipelinev1beta1.Param, buildRequestProcessorImage string, buildId string) (*pipelinev1beta1.PipelineSpec, string, error) { // Rather than tagging with hash of json build recipe, buildrequestprocessor image and db.Name as the former two // could change with new image versions just use db.Name (which is a hash of scm url/tag/path so should be stable) @@ -62,7 +63,7 @@ func createPipelineSpec(tool string, commitTime int64, jbsConfig *v1alpha12.JBSC hermeticBuildRequired := jbsConfig.Spec.HermeticBuilds == v1alpha12.HermeticBuildTypeRequired verifyBuiltArtifactsArgs := verifyParameters(jbsConfig, recipe) - preBuildImageArgs, deployArgs, hermeticDeployArgs, tagArgs, createHermeticImageArgs := imageRegistryCommands(imageId, recipe, db, jbsConfig, hermeticBuildRequired) + preBuildImageArgs, deployArgs, hermeticDeployArgs, tagArgs, createHermeticImageArgs := imageRegistryCommands(imageId, recipe, db, jbsConfig, hermeticBuildRequired, buildId) gitArgs := gitArgs(db, recipe) install := additionalPackages(recipe) @@ -331,7 +332,7 @@ func createPipelineSpec(tool string, commitTime int64, jbsConfig *v1alpha12.JBSC } tagTask := pipelinev1beta1.TaskSpec{ Workspaces: []pipelinev1beta1.WorkspaceDeclaration{{Name: WorkspaceBuildSettings}, {Name: WorkspaceSource}, {Name: WorkspaceTls}}, - Params: []pipelinev1beta1.ParamSpec{{Name: "GAVS", Type: pipelinev1beta1.ParamTypeString}}, + Params: []pipelinev1beta1.ParamSpec{{Name: "GAVS", Type: pipelinev1beta1.ParamTypeString}, {Name: DeployedImageDigest, Type: pipelinev1beta1.ParamTypeString}}, Steps: []pipelinev1beta1.Step{ { Name: "tag", @@ -350,8 +351,10 @@ func createPipelineSpec(tool string, commitTime int64, jbsConfig *v1alpha12.JBSC } tagDepends := artifactbuild.BuildTaskName + tagDigest := "$(tasks." + artifactbuild.BuildTaskName + ".results." + PipelineResultImageDigest + ")" if hermeticBuildRequired { tagDepends = artifactbuild.HermeticBuildTaskName + tagDigest = "$(tasks." + artifactbuild.HermeticBuildTaskName + ".results." + PipelineResultImageDigest + ")" } hermeticBuildPipelineTask := pipelinev1beta1.PipelineTask{ Name: artifactbuild.HermeticBuildTaskName, @@ -373,7 +376,8 @@ func createPipelineSpec(tool string, commitTime int64, jbsConfig *v1alpha12.JBSC TaskSpec: &pipelinev1beta1.EmbeddedTask{ TaskSpec: tagTask, }, - Params: []pipelinev1beta1.Param{}, Workspaces: []pipelinev1beta1.WorkspacePipelineTaskBinding{ + Params: []pipelinev1beta1.Param{{Name: DeployedImageDigest, Value: pipelinev1beta1.ParamValue{Type: pipelinev1beta1.ParamTypeString, StringVal: tagDigest}}}, + Workspaces: []pipelinev1beta1.WorkspacePipelineTaskBinding{ {Name: WorkspaceBuildSettings, Workspace: WorkspaceBuildSettings}, {Name: WorkspaceSource, Workspace: WorkspaceSource}, {Name: WorkspaceTls, Workspace: WorkspaceTls}, @@ -586,7 +590,7 @@ func gitArgs(db *v1alpha12.DependencyBuild, recipe *v1alpha12.BuildRecipe) strin return gitArgs } -func imageRegistryCommands(imageId string, recipe *v1alpha12.BuildRecipe, db *v1alpha12.DependencyBuild, jbsConfig *v1alpha12.JBSConfig, hermeticBuild bool) ([]string, []string, []string, []string, []string) { +func imageRegistryCommands(imageId string, recipe *v1alpha12.BuildRecipe, db *v1alpha12.DependencyBuild, jbsConfig *v1alpha12.JBSConfig, hermeticBuild bool, buildId string) ([]string, []string, []string, []string, []string) { preBuildImageTag := imageId + "-pre-build-image" preBuildImageArgs := []string{ @@ -607,6 +611,7 @@ func imageRegistryCommands(imageId string, recipe *v1alpha12.BuildRecipe, db *v1 "--build-info-path=$(workspaces.source.path)/build-info", "--source-path=$(workspaces.source.path)/source", "--task-run-name=$(context.taskRun.name)", + "--build-id=" + buildId, "--scm-uri=" + db.Spec.ScmInfo.SCMURL, "--scm-commit=" + db.Spec.ScmInfo.CommitHash, } @@ -614,16 +619,9 @@ func imageRegistryCommands(imageId string, recipe *v1alpha12.BuildRecipe, db *v1 hermeticDeployArgs = append(hermeticDeployArgs, "--image-id="+hermeticImageId) deployArgs = append(deployArgs, "--image-id="+imageId) - var imageIdToTag string - if hermeticBuild { - imageIdToTag = hermeticImageId - } else { - imageIdToTag = imageId - } - tagArgs := []string{ "tag-container", - "--image-id=" + imageIdToTag, //TODO: hash + "--image-digest=$(params." + DeployedImageDigest + ")", } imageRegistry := jbsConfig.ImageRegistry() registryArgs := make([]string, 0) diff --git a/pkg/reconciler/dependencybuild/dependencybuild.go b/pkg/reconciler/dependencybuild/dependencybuild.go index 15a1e3fac2..235cf74259 100644 --- a/pkg/reconciler/dependencybuild/dependencybuild.go +++ b/pkg/reconciler/dependencybuild/dependencybuild.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/google/uuid" "github.com/redhat-appstudio/jvm-build-service/pkg/reconciler/jbsconfig" "github.com/tektoncd/cli/pkg/cli" "k8s.io/client-go/kubernetes" @@ -468,6 +469,7 @@ func (r *ReconcileDependencyBuild) handleStateSubmitBuild(ctx context.Context, d return reconcile.Result{}, r.client.Status().Update(ctx, db) } ba := v1alpha1.BuildAttempt{} + ba.BuildId = uuid.New().String() ba.Recipe = db.Status.PotentialBuildRecipes[0] pipelineName := currentDependencyBuildPipelineName(db) ba.Build = &v1alpha1.BuildPipelineRun{ @@ -545,7 +547,7 @@ func (r *ReconcileDependencyBuild) handleStateBuilding(ctx context.Context, log } diagnostic := "" // TODO: set owner, pass parameter to do verify if true, via an annoaton on the dependency build, may eed to wait for dep build to exist verify is an optional, use append on each step in build recipes - pr.Spec.PipelineSpec, diagnostic, err = createPipelineSpec(attempt.Recipe.Tool, db.Status.CommitTime, jbsConfig, &systemConfig, attempt.Recipe, db, paramValues, buildRequestProcessorImage) + pr.Spec.PipelineSpec, diagnostic, err = createPipelineSpec(attempt.Recipe.Tool, db.Status.CommitTime, jbsConfig, &systemConfig, attempt.Recipe, db, paramValues, buildRequestProcessorImage, attempt.BuildId) if err != nil { return reconcile.Result{}, err }