diff --git a/cmd/kanikoExecute.go b/cmd/kanikoExecute.go index 1937dd66af..801dda509a 100644 --- a/cmd/kanikoExecute.go +++ b/cmd/kanikoExecute.go @@ -1,7 +1,11 @@ package cmd import ( + "encoding/json" "fmt" + "github.com/SAP/jenkins-library/pkg/build" + "github.com/SAP/jenkins-library/pkg/versioning" + "path/filepath" "strings" "github.com/mitchellh/mapstructure" @@ -186,7 +190,16 @@ func runKanikoExecute(config *kanikoExecuteOptions, telemetryData *telemetry.Cus } if config.CreateBOM { // Syft for multi image, generates bom-docker-(1/2/3).xml - return syft.GenerateSBOM(config.SyftDownloadURL, "/kaniko/.docker", execRunner, fileUtils, httpClient, commonPipelineEnvironment.container.registryURL, commonPipelineEnvironment.container.imageNameTags) + err = syft.GenerateSBOM(config.SyftDownloadURL, "/kaniko/.docker", execRunner, fileUtils, httpClient, commonPipelineEnvironment.container.registryURL, commonPipelineEnvironment.container.imageNameTags) + if err != nil { + return err + } + } + if config.CreateBuildArtifactsMetadata { + if err := buildArtifactsMetadataKaniko(config, commonPipelineEnvironment); err != nil { + log.Entry().Warnf("unable to create build artifacts metadata: %v", err) + return nil + } } return nil @@ -283,7 +296,16 @@ func runKanikoExecute(config *kanikoExecuteOptions, telemetryData *telemetry.Cus if config.CreateBOM { // Syft for multi image, generates bom-docker-(1/2/3).xml - return syft.GenerateSBOM(config.SyftDownloadURL, "/kaniko/.docker", execRunner, fileUtils, httpClient, commonPipelineEnvironment.container.registryURL, commonPipelineEnvironment.container.imageNameTags) + err = syft.GenerateSBOM(config.SyftDownloadURL, "/kaniko/.docker", execRunner, fileUtils, httpClient, commonPipelineEnvironment.container.registryURL, commonPipelineEnvironment.container.imageNameTags) + if err != nil { + return err + } + } + if config.CreateBuildArtifactsMetadata { + if err := buildArtifactsMetadataKaniko(config, commonPipelineEnvironment); err != nil { + log.Entry().Warnf("unable to create build artifacts metadata: %v", err) + return nil + } } return nil @@ -362,9 +384,54 @@ func runKanikoExecute(config *kanikoExecuteOptions, telemetryData *telemetry.Cus if config.CreateBOM { // Syft for single image, generates bom-docker-0.xml - return syft.GenerateSBOM(config.SyftDownloadURL, "/kaniko/.docker", execRunner, fileUtils, httpClient, commonPipelineEnvironment.container.registryURL, commonPipelineEnvironment.container.imageNameTags) + err = syft.GenerateSBOM(config.SyftDownloadURL, "/kaniko/.docker", execRunner, fileUtils, httpClient, commonPipelineEnvironment.container.registryURL, commonPipelineEnvironment.container.imageNameTags) + if err != nil { + return err + } + } + if config.CreateBuildArtifactsMetadata { + if err := buildArtifactsMetadataKaniko(config, commonPipelineEnvironment); err != nil { + log.Entry().Warnf("unable to create build artifacts metadata: %v", err) + return nil + } + } + + return nil +} + +func buildArtifactsMetadataKaniko(config *kanikoExecuteOptions, commonPipelineEnvironment *kanikoExecuteCommonPipelineEnvironment) error { + log.Entry().Debug("creating build artifacts metadata for kaniko packages") + buildCoordinates := []versioning.Coordinates{} + options := versioning.Options{} + var utils versioning.Utils + imageBuildPaths := commonPipelineEnvironment.container.imageNameTags // This needs to be populated earlier during the image build process. + for imageIndex, _ := range imageBuildPaths { + artifact, err := versioning.GetArtifact("docker", config.DockerfilePath, &options, utils) + if err != nil { + return err + } + coordinate, err := artifact.GetCoordinates() + if err != nil { + log.Entry().Warnf("unable to get artifact coordinates for image %d: %v", imageIndex, err) + } else { + bomFilename := fmt.Sprintf("bom-docker-%d.xml", imageIndex) + dockerfilePath := filepath.Dir(config.DockerfilePath) + coordinate.BuildPath = dockerfilePath + coordinate.URL = config.ContainerRegistryURL + coordinate.PURL = piperutils.GetPurl(filepath.Join(dockerfilePath, bomFilename)) + buildCoordinates = append(buildCoordinates, coordinate) + } } + if len(buildCoordinates) == 0 { + return errors.New("unable to identify artifact coordinates for the kaniko packages published") + } + + var buildArtifacts build.BuildArtifacts + + buildArtifacts.Coordinates = buildCoordinates + jsonResult, _ := json.Marshal(buildArtifacts) + commonPipelineEnvironment.custom.kanikoExecuteArtifacts = string(jsonResult) return nil } diff --git a/cmd/kanikoExecute_generated.go b/cmd/kanikoExecute_generated.go index fd045081f2..5ba3929547 100644 --- a/cmd/kanikoExecute_generated.go +++ b/cmd/kanikoExecute_generated.go @@ -44,6 +44,7 @@ type kanikoExecuteOptions struct { ReadImageDigest bool `json:"readImageDigest,omitempty"` CreateBOM bool `json:"createBOM,omitempty"` SyftDownloadURL string `json:"syftDownloadUrl,omitempty"` + CreateBuildArtifactsMetadata bool `json:"createBuildArtifactsMetadata,omitempty"` } type kanikoExecuteCommonPipelineEnvironment struct { @@ -56,7 +57,8 @@ type kanikoExecuteCommonPipelineEnvironment struct { imageDigests []string } custom struct { - buildSettingsInfo string + buildSettingsInfo string + kanikoExecuteArtifacts string } } @@ -73,6 +75,7 @@ func (p *kanikoExecuteCommonPipelineEnvironment) persist(path, resourceName stri {category: "container", name: "imageNameTags", value: p.container.imageNameTags}, {category: "container", name: "imageDigests", value: p.container.imageDigests}, {category: "custom", name: "buildSettingsInfo", value: p.custom.buildSettingsInfo}, + {category: "custom", name: "kanikoExecuteArtifacts", value: p.custom.kanikoExecuteArtifacts}, } errCount := 0 @@ -337,6 +340,7 @@ func addKanikoExecuteFlags(cmd *cobra.Command, stepConfig *kanikoExecuteOptions) cmd.Flags().BoolVar(&stepConfig.ReadImageDigest, "readImageDigest", false, "") cmd.Flags().BoolVar(&stepConfig.CreateBOM, "createBOM", false, "Creates the bill of materials (BOM) using Syft and stores it in a file in CycloneDX 1.4 format.") cmd.Flags().StringVar(&stepConfig.SyftDownloadURL, "syftDownloadUrl", `https://github.com/anchore/syft/releases/download/v1.4.1/syft_1.4.1_linux_amd64.tar.gz`, "Specifies the download url of the Syft Linux amd64 tar binary file. This can be found at https://github.com/anchore/syft/releases/.") + cmd.Flags().BoolVar(&stepConfig.CreateBuildArtifactsMetadata, "createBuildArtifactsMetadata", false, "metadata about the artifacts that are build and published, this metadata is generally used by steps downstream in the pipeline") } @@ -578,6 +582,15 @@ func kanikoExecuteMetadata() config.StepData { Aliases: []config.Alias{}, Default: `https://github.com/anchore/syft/releases/download/v1.4.1/syft_1.4.1_linux_amd64.tar.gz`, }, + { + Name: "createBuildArtifactsMetadata", + ResourceRef: []config.ResourceReference{}, + Scope: []string{"STEPS", "STAGES", "PARAMETERS"}, + Type: "bool", + Mandatory: false, + Aliases: []config.Alias{}, + Default: false, + }, }, }, Containers: []config.Container{ @@ -596,6 +609,7 @@ func kanikoExecuteMetadata() config.StepData { {"name": "container/imageNameTags", "type": "[]string"}, {"name": "container/imageDigests", "type": "[]string"}, {"name": "custom/buildSettingsInfo"}, + {"name": "custom/kanikoExecuteArtifacts"}, }, }, { diff --git a/pkg/piperutils/cyclonedxBom.go b/pkg/piperutils/cyclonedxBom.go index 51c8685235..d6db8677f6 100644 --- a/pkg/piperutils/cyclonedxBom.go +++ b/pkg/piperutils/cyclonedxBom.go @@ -49,6 +49,7 @@ func GetBom(absoluteBomPath string) (Bom, error) { } func GetPurl(bomFilePath string) string { + log.Entry().Debugf("Getting purl from bom file %s", bomFilePath) bom, err := GetBom(bomFilePath) if err != nil { log.Entry().Warnf("unable to get bom metadata: %v", err) diff --git a/resources/metadata/kanikoExecute.yaml b/resources/metadata/kanikoExecute.yaml index 5efad0931c..4deb85b301 100644 --- a/resources/metadata/kanikoExecute.yaml +++ b/resources/metadata/kanikoExecute.yaml @@ -306,6 +306,14 @@ spec: - PARAMETERS - STEPS default: "https://github.com/anchore/syft/releases/download/v1.4.1/syft_1.4.1_linux_amd64.tar.gz" + - name: createBuildArtifactsMetadata + type: bool + default: false + description: metadata about the artifacts that are build and published, this metadata is generally used by steps downstream in the pipeline + scope: + - STEPS + - STAGES + - PARAMETERS outputs: resources: - name: commonPipelineEnvironment @@ -321,6 +329,7 @@ spec: - name: container/imageDigests type: "[]string" - name: custom/buildSettingsInfo + - name: custom/kanikoExecuteArtifacts - name: reports type: reports params: