Skip to content

Commit

Permalink
Support for Manifest annotations in bundle push
Browse files Browse the repository at this point in the history
Adds `-a` or `--annotate` parameter to `tkn bundle push` to set any OCI
Manifest annotations on the bundle image.

Fixes #1933
  • Loading branch information
zregvart committed Aug 24, 2023
1 parent 78bcb31 commit cf57384
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 13 deletions.
1 change: 1 addition & 0 deletions docs/cmd/tkn_bundle_push.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Input:
### Options

```
-a, --annotate strings OCI Manifest annotations in the form of key=value to be added to the OCI image
-f, --filenames strings List of fully-qualified file paths containing YAML or JSON defined Tekton objects to include in this bundle
-h, --help help for push
--remote-bearer string A Bearer token to authenticate against the repository
Expand Down
4 changes: 4 additions & 0 deletions docs/man/man1/tkn-bundle-push.1
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ Input:


.SH OPTIONS
.PP
\fB\-a\fP, \fB\-\-annotate\fP=[]
OCI Manifest annotations in the form of key=value to be added to the OCI image

.PP
\fB\-f\fP, \fB\-\-filenames\fP=[]
List of fully\-qualified file paths containing YAML or JSON defined Tekton objects to include in this bundle
Expand Down
4 changes: 2 additions & 2 deletions pkg/bundle/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import (

// BuildTektonBundle will return a complete OCI Image usable as a Tekton Bundle built by parsing, decoding, and
// compressing the provided contents as Tekton objects.
func BuildTektonBundle(contents []string, log io.Writer) (v1.Image, error) {
img := empty.Image
func BuildTektonBundle(contents []string, annotations map[string]string, log io.Writer) (v1.Image, error) {
img := mutate.Annotations(empty.Image, annotations).(v1.Image)

if len(contents) > tkremote.MaximumBundleObjects {
return nil, fmt.Errorf("bundle contains more than the maximum %d allow objects", tkremote.MaximumBundleObjects)
Expand Down
16 changes: 11 additions & 5 deletions pkg/bundle/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ func TestBuildTektonBundle(t *testing.T) {
return
}

img, err := BuildTektonBundle([]string{string(raw)}, &bytes.Buffer{})
annotations := map[string]string{"a1": "v1", "a2": "v2"}
img, err := BuildTektonBundle([]string{string(raw)}, annotations, &bytes.Buffer{})
if err != nil {
t.Error(err)
}
Expand All @@ -54,6 +55,11 @@ func TestBuildTektonBundle(t *testing.T) {
return
}

ann := manifest.Annotations
if len(ann) != len(annotations) || fmt.Sprint(ann) != fmt.Sprint(annotations) {
t.Errorf("Requested annotations were not set wanted: %s, got %s", annotations, ann)
}

if len(manifest.Layers) != 1 {
t.Errorf("Unexpected number of layers %d", len(manifest.Layers))
}
Expand Down Expand Up @@ -123,7 +129,7 @@ func TestBadObj(t *testing.T) {
t.Error(err)
return
}
_, err = BuildTektonBundle([]string{string(raw)}, &bytes.Buffer{})
_, err = BuildTektonBundle([]string{string(raw)}, nil, &bytes.Buffer{})
noNameErr := errors.New("kubernetes resources should have a name")
if err == nil {
t.Errorf("expected error: %v", noNameErr)
Expand All @@ -146,7 +152,7 @@ func TestLessThenMaxBundle(t *testing.T) {
return
}
// no error for less then max
_, err = BuildTektonBundle([]string{string(raw)}, &bytes.Buffer{})
_, err = BuildTektonBundle([]string{string(raw)}, nil, &bytes.Buffer{})
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -174,7 +180,7 @@ func TestJustEnoughBundleSize(t *testing.T) {
justEnoughObj = append(justEnoughObj, string(raw))
}
// no error for the max
_, err := BuildTektonBundle(justEnoughObj, &bytes.Buffer{})
_, err := BuildTektonBundle(justEnoughObj, nil, &bytes.Buffer{})
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -203,7 +209,7 @@ func TestTooManyInBundle(t *testing.T) {
}

// expect error when we hit the max
_, err := BuildTektonBundle(toMuchObj, &bytes.Buffer{})
_, err := BuildTektonBundle(toMuchObj, nil, &bytes.Buffer{})
if err == nil {
t.Errorf("expected error: %v", toManyObjErr)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/bundle/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func TestListCommand(t *testing.T) {
t.Fatal(err)
}

img, err := bundle.BuildTektonBundle([]string{examplePullTask, examplePullPipeline}, &bytes.Buffer{})
img, err := bundle.BuildTektonBundle([]string{examplePullTask, examplePullPipeline}, nil, &bytes.Buffer{})
if err != nil {
t.Fatal(err)
}
Expand Down
15 changes: 14 additions & 1 deletion pkg/cmd/bundle/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"fmt"
"io"
"os"
"strings"

"github.com/google/go-containerregistry/pkg/name"
"github.com/spf13/cobra"
Expand All @@ -31,6 +32,8 @@ type pushOptions struct {
bundleContents []string
bundleContentPaths []string
remoteOptions bundle.RemoteOptions
annotationParams []string
annotations map[string]string
}

func pushCommand(_ cli.Params) *cobra.Command {
Expand Down Expand Up @@ -80,6 +83,7 @@ Input:
},
}
c.Flags().StringSliceVarP(&opts.bundleContentPaths, "filenames", "f", []string{}, "List of fully-qualified file paths containing YAML or JSON defined Tekton objects to include in this bundle")
c.Flags().StringSliceVarP(&opts.annotationParams, "annotate", "a", []string{}, "OCI Manifest annotations in the form of key=value to be added to the OCI image")
bundle.AddRemoteFlags(c.Flags(), &opts.remoteOptions)

return c
Expand Down Expand Up @@ -109,6 +113,15 @@ func (p *pushOptions) parseArgsAndFlags(args []string) error {
p.bundleContents = append(p.bundleContents, string(contents))
}

p.annotations = map[string]string{}
for _, annParam := range p.annotationParams {
if k, v, ok := strings.Cut(annParam, "="); ok {
p.annotations[strings.TrimSpace(k)] = strings.TrimSpace(v)
} else {
return fmt.Errorf("annotation parameter not in key=value syntax: %q", annParam)
}
}

return nil
}

Expand All @@ -118,7 +131,7 @@ func (p *pushOptions) Run(args []string) error {
return err
}

img, err := bundle.BuildTektonBundle(p.bundleContents, p.stream.Out)
img, err := bundle.BuildTektonBundle(p.bundleContents, p.annotations, p.stream.Out)
if err != nil {
return err
}
Expand Down
27 changes: 23 additions & 4 deletions pkg/cmd/bundle/push_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,12 @@ var (

func TestPushCommand(t *testing.T) {
testcases := []struct {
name string
files map[string]string
stdin string
expectedContents map[string]expected
name string
files map[string]string
stdin string
annotations []string
expectedContents map[string]expected
expectedAnnotations map[string]string
}{
{
name: "single-input",
Expand All @@ -71,6 +73,18 @@ func TestPushCommand(t *testing.T) {
stdin: exampleTask,
expectedContents: map[string]expected{exampleTaskExpected.name: exampleTaskExpected},
},
{
name: "with-annotations",
files: map[string]string{
"simple.yaml": exampleTask,
},
annotations: []string{"a1=k1", "a2 = k2"},
expectedContents: map[string]expected{exampleTaskExpected.name: exampleTaskExpected},
expectedAnnotations: map[string]string{
"a1": "k1",
"a2": "k2",
},
},
}

for _, tc := range testcases {
Expand Down Expand Up @@ -112,6 +126,7 @@ func TestPushCommand(t *testing.T) {
Err: &bytes.Buffer{},
},
bundleContentPaths: paths,
annotationParams: tc.annotations,
remoteOptions: bundle.RemoteOptions{},
}
if err := opts.Run([]string{ref}); err != nil {
Expand All @@ -138,6 +153,10 @@ func TestPushCommand(t *testing.T) {
t.Errorf("Expected %d layers but found %d", len(tc.expectedContents), len(manifest.Layers))
}

if len(manifest.Annotations) != len(tc.expectedAnnotations) || fmt.Sprint(manifest.Annotations) != fmt.Sprint(tc.expectedAnnotations) {
t.Errorf("Requested annotations were not set wanted: %s, got %s", tc.expectedAnnotations, manifest.Annotations)
}

for i, l := range manifest.Layers {
title, ok := l.Annotations[tkremote.TitleAnnotation]
if !ok {
Expand Down

0 comments on commit cf57384

Please sign in to comment.