From 0161053d430b7c10136201b1bbab330d4a226068 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Trivino Date: Mon, 31 Jul 2023 13:34:48 +0200 Subject: [PATCH 1/3] feat: support global annotations Signed-off-by: Miguel Martinez Trivino --- .../core/dependency-track/v1/extension.go | 19 +++++++++++++------ .../dependency-track/v1/extension_test.go | 18 ++++++++++++++++-- .../renderer/chainloop/chainloop.go | 5 +++++ 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/app/controlplane/plugins/core/dependency-track/v1/extension.go b/app/controlplane/plugins/core/dependency-track/v1/extension.go index aab4fb7ad..a3ff3d652 100644 --- a/app/controlplane/plugins/core/dependency-track/v1/extension.go +++ b/app/controlplane/plugins/core/dependency-track/v1/extension.go @@ -182,7 +182,9 @@ func doExecute(ctx context.Context, req *sdk.ExecutionRequest, sbom *sdk.Execute return errors.New("invalid attachment configuration") } - projectName, err := resolveProjectName(attachmentConfig.ProjectName, sbom.Annotations) + // Calculate the project name based on the template + + projectName, err := resolveProjectName(attachmentConfig.ProjectName, req.Input.Attestation.Predicate.GetAnnotations(), sbom.Annotations) if err != nil { // If we can't find the annotation for example, we skip the SBOM l.Infow("msg", "failed to resolve project name, SKIPPING", "err", err, "materialName", sbom.Name) @@ -227,18 +229,23 @@ func doExecute(ctx context.Context, req *sdk.ExecutionRequest, sbom *sdk.Execute } type interpolationContext struct { - Material *interpolationContextMaterial + Material *annotations + Attestation *annotations } -type interpolationContextMaterial struct { +type annotations struct { Annotations map[string]string } // Resolve the project name template. // We currently support the following template variables: -// - material.annotations. +// - {{ .Attestation.Annotations. }} for global annotations +// - {{ .Material.Annotations. }} for material annotations // For example, project-name => {{ material.annotations.my_annotation }} -func resolveProjectName(projectNameTpl string, annotations map[string]string) (string, error) { - data := interpolationContext{&interpolationContextMaterial{annotations}} +func resolveProjectName(projectNameTpl string, attAnnotations, sbomAnnotations map[string]string) (string, error) { + data := &interpolationContext{ + Material: &annotations{sbomAnnotations}, + Attestation: &annotations{attAnnotations}, + } // The project name can contain template variables, useful to include annotations for example // We do fail if the key can't be found diff --git a/app/controlplane/plugins/core/dependency-track/v1/extension_test.go b/app/controlplane/plugins/core/dependency-track/v1/extension_test.go index c597adc14..79bfc00e1 100644 --- a/app/controlplane/plugins/core/dependency-track/v1/extension_test.go +++ b/app/controlplane/plugins/core/dependency-track/v1/extension_test.go @@ -128,17 +128,31 @@ func TestResolveProjectName(t *testing.T) { projectName: "{{.Material.Annotations.hello}}", wantErr: true, }, + { + name: "interpolated string global", + projectName: "project-{{.Attestation.Annotations.version}}", + want: "project-1.2.3", + }, + { + name: "interpolated combination global", + projectName: "project-{{.Material.Annotations.Hello}}-{{.Attestation.Annotations.version}}", + want: "project-hola-1.2.3", + }, } - data := map[string]string{ + sbomAnnotation := map[string]string{ "Hello": "hola", "World": "mundo", } + attAnnotation := map[string]string{ + "version": "1.2.3", + } + for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var err error - got, err := resolveProjectName(tc.projectName, data) + got, err := resolveProjectName(tc.projectName, attAnnotation, sbomAnnotation) if tc.wantErr { assert.Error(t, err) return diff --git a/internal/attestation/renderer/chainloop/chainloop.go b/internal/attestation/renderer/chainloop/chainloop.go index 9f0002356..947c6bbe2 100644 --- a/internal/attestation/renderer/chainloop/chainloop.go +++ b/internal/attestation/renderer/chainloop/chainloop.go @@ -35,6 +35,7 @@ const builderIDFmt = "chainloop.dev/cli/%s@%s" // NormalizablePredicate represents a common interface of how to extract materials and env vars type NormalizablePredicate interface { + GetAnnotations() map[string]string GetEnvVars() map[string]string GetMaterials() []*NormalizedMaterial GetRunLink() string @@ -195,3 +196,7 @@ func (p *ProvenancePredicateCommon) GetEnvVars() map[string]string { func (p *ProvenancePredicateCommon) GetRunLink() string { return p.RunnerURL } + +func (p *ProvenancePredicateCommon) GetAnnotations() map[string]string { + return p.Annotations +} From 1e456a642dfbebb2f89d5d5e1bb2fdd1c05ce2b8 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Trivino Date: Mon, 31 Jul 2023 14:17:03 +0200 Subject: [PATCH 2/3] feat: support global annotations Signed-off-by: Miguel Martinez Trivino --- .../core/dependency-track/v1/extension.go | 16 ++++++++++++++-- .../core/dependency-track/v1/extension_test.go | 9 +++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/app/controlplane/plugins/core/dependency-track/v1/extension.go b/app/controlplane/plugins/core/dependency-track/v1/extension.go index a3ff3d652..7e48ac26f 100644 --- a/app/controlplane/plugins/core/dependency-track/v1/extension.go +++ b/app/controlplane/plugins/core/dependency-track/v1/extension.go @@ -20,6 +20,7 @@ import ( "context" "errors" "fmt" + "strings" "text/template" schemaapi "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1" @@ -236,6 +237,17 @@ type annotations struct { Annotations map[string]string } +// Make annotations keys case insensitive +// that way you can define templates such as {{ material.annotations.myAnnotation }} or {{ material.annotations.MyAnnotation }} and they will both work +func toCaseInsensitive(in map[string]string) map[string]string { + for k, v := range in { + in[strings.Title(k)] = v + in[strings.ToLower(k)] = v + } + + return in +} + // Resolve the project name template. // We currently support the following template variables: // - {{ .Attestation.Annotations. }} for global annotations @@ -243,8 +255,8 @@ type annotations struct { // For example, project-name => {{ material.annotations.my_annotation }} func resolveProjectName(projectNameTpl string, attAnnotations, sbomAnnotations map[string]string) (string, error) { data := &interpolationContext{ - Material: &annotations{sbomAnnotations}, - Attestation: &annotations{attAnnotations}, + Material: &annotations{toCaseInsensitive(sbomAnnotations)}, + Attestation: &annotations{toCaseInsensitive(attAnnotations)}, } // The project name can contain template variables, useful to include annotations for example diff --git a/app/controlplane/plugins/core/dependency-track/v1/extension_test.go b/app/controlplane/plugins/core/dependency-track/v1/extension_test.go index 79bfc00e1..7070f9dde 100644 --- a/app/controlplane/plugins/core/dependency-track/v1/extension_test.go +++ b/app/controlplane/plugins/core/dependency-track/v1/extension_test.go @@ -124,9 +124,9 @@ func TestResolveProjectName(t *testing.T) { wantErr: true, }, { - name: "non-existing-case", + name: "lower case", projectName: "{{.Material.Annotations.hello}}", - wantErr: true, + want: "hola", }, { name: "interpolated string global", @@ -138,6 +138,11 @@ func TestResolveProjectName(t *testing.T) { projectName: "project-{{.Material.Annotations.Hello}}-{{.Attestation.Annotations.version}}", want: "project-hola-1.2.3", }, + { + name: "uppercase", + projectName: "{{.Attestation.Annotations.Version}}", + want: "1.2.3", + }, } sbomAnnotation := map[string]string{ From f3d830fb0c875f9a679b6d7520edabcc1db9c5f6 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Trivino Date: Mon, 31 Jul 2023 14:18:47 +0200 Subject: [PATCH 3/3] feat: support global annotations Signed-off-by: Miguel Martinez Trivino --- .../core/dependency-track/v1/extension.go | 1 - .../core/dependency-track/v1/extension_test.go | 16 ++++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/controlplane/plugins/core/dependency-track/v1/extension.go b/app/controlplane/plugins/core/dependency-track/v1/extension.go index 7e48ac26f..8668aa68d 100644 --- a/app/controlplane/plugins/core/dependency-track/v1/extension.go +++ b/app/controlplane/plugins/core/dependency-track/v1/extension.go @@ -242,7 +242,6 @@ type annotations struct { func toCaseInsensitive(in map[string]string) map[string]string { for k, v := range in { in[strings.Title(k)] = v - in[strings.ToLower(k)] = v } return in diff --git a/app/controlplane/plugins/core/dependency-track/v1/extension_test.go b/app/controlplane/plugins/core/dependency-track/v1/extension_test.go index 7070f9dde..ec9de6b83 100644 --- a/app/controlplane/plugins/core/dependency-track/v1/extension_test.go +++ b/app/controlplane/plugins/core/dependency-track/v1/extension_test.go @@ -112,6 +112,11 @@ func TestResolveProjectName(t *testing.T) { projectName: "{{.Material.Annotations.Hello}}", want: "hola", }, + { + name: "lower case", + projectName: "{{.Material.Annotations.hello}}", + want: "hola", + }, { name: "interpolated string", projectName: "{{.Material.Annotations.Hello}}-project", @@ -123,11 +128,6 @@ func TestResolveProjectName(t *testing.T) { want: "", wantErr: true, }, - { - name: "lower case", - projectName: "{{.Material.Annotations.hello}}", - want: "hola", - }, { name: "interpolated string global", projectName: "project-{{.Attestation.Annotations.version}}", @@ -139,15 +139,15 @@ func TestResolveProjectName(t *testing.T) { want: "project-hola-1.2.3", }, { - name: "uppercase", + name: "uppercase global", projectName: "{{.Attestation.Annotations.Version}}", want: "1.2.3", }, } sbomAnnotation := map[string]string{ - "Hello": "hola", - "World": "mundo", + "hello": "hola", + "world": "mundo", } attAnnotation := map[string]string{