Skip to content

Commit

Permalink
Add labeling support to tkn bundle push
Browse files Browse the repository at this point in the history
When pushing the bundle `-l` or `--label` can be provided to set labels
in the config JSON of the image.

Fixes #2256
  • Loading branch information
zregvart authored and tekton-robot committed Feb 27, 2024
1 parent 42249b9 commit 4877295
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 11 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 @@ -36,6 +36,7 @@ Created time:
--ctime string YYYY-MM-DD, YYYY-MM-DDTHH:MM:SS or RFC3339 formatted created time to set, defaults to current time. In non RFC3339 syntax dates are in UTC timezone.
-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
--label strings OCI Config labels in the form of key=value to be added to the OCI image. Can be provided multiple times to add multiple labels.
--remote-bearer string A Bearer token to authenticate against the repository
--remote-password string A password to pass to the registry for basic auth. Must be used with --remote-username
--remote-skip-tls If set to true, skips TLS check when connecting to the registry
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 @@ -61,6 +61,10 @@ Created time:
\fB\-h\fP, \fB\-\-help\fP[=false]
help for push

.PP
\fB\-\-label\fP=[]
OCI Config labels in the form of key=value to be added to the OCI image. Can be provided multiple times to add multiple labels.

.PP
\fB\-\-remote\-bearer\fP=""
A Bearer token to authenticate against the repository
Expand Down
10 changes: 8 additions & 2 deletions pkg/bundle/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,14 @@ 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, annotations map[string]string, ctime time.Time, log io.Writer) (v1.Image, error) {
func BuildTektonBundle(contents []string, annotations, labels map[string]string, ctime time.Time, log io.Writer) (v1.Image, error) {
img := mutate.Annotations(empty.Image, annotations).(v1.Image)
img, err := mutate.Config(img, v1.Config{
Labels: labels,
})
if err != nil {
return nil, fmt.Errorf("setting labels: %w", err)
}

if len(contents) > tkremote.MaximumBundleObjects {
return nil, fmt.Errorf("bundle contains more than the maximum %d allow objects", tkremote.MaximumBundleObjects)
Expand Down Expand Up @@ -96,7 +102,7 @@ func BuildTektonBundle(contents []string, annotations map[string]string, ctime t
}

// Set created time for bundle image
img, err := mutate.CreatedAt(img, v1.Time{Time: ctime})
img, err = mutate.CreatedAt(img, v1.Time{Time: ctime})
if err != nil {
return nil, fmt.Errorf("failed to add created time to image: %w", err)
}
Expand Down
18 changes: 11 additions & 7 deletions pkg/bundle/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ func TestBuildTektonBundle(t *testing.T) {
}

annotations := map[string]string{"org.opencontainers.image.license": "Apache-2.0", "org.opencontainers.image.url": "https://example.org"}
img, err := BuildTektonBundle([]string{string(raw)}, annotations, time.Now(), &bytes.Buffer{})
labels := map[string]string{"version": "1.0", "quay.expires-after": "7d"}
img, err := BuildTektonBundle([]string{string(raw)}, annotations, labels, time.Now(), &bytes.Buffer{})
if err != nil {
t.Error(err)
}
Expand All @@ -82,6 +83,9 @@ func TestBuildTektonBundle(t *testing.T) {
if cfg.Created.IsZero() {
t.Error("Created time of image was not set")
}
if !cmp.Equal(cfg.Config.Labels, labels) {
t.Errorf("Expected labels were not set got: %+v", cfg.Config.Labels)
}

manifest, err := img.Manifest()
if err != nil {
Expand Down Expand Up @@ -163,7 +167,7 @@ func TestBadObj(t *testing.T) {
t.Error(err)
return
}
_, err = BuildTektonBundle([]string{string(raw)}, nil, time.Now(), &bytes.Buffer{})
_, err = BuildTektonBundle([]string{string(raw)}, nil, nil, time.Now(), &bytes.Buffer{})
noNameErr := errors.New("kubernetes resources should have a name")
if err == nil {
t.Errorf("expected error: %v", noNameErr)
Expand All @@ -186,7 +190,7 @@ func TestLessThenMaxBundle(t *testing.T) {
return
}
// no error for less then max
_, err = BuildTektonBundle([]string{string(raw)}, nil, time.Now(), &bytes.Buffer{})
_, err = BuildTektonBundle([]string{string(raw)}, nil, nil, time.Now(), &bytes.Buffer{})
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -214,7 +218,7 @@ func TestJustEnoughBundleSize(t *testing.T) {
justEnoughObj = append(justEnoughObj, string(raw))
}
// no error for the max
_, err := BuildTektonBundle(justEnoughObj, nil, time.Now(), &bytes.Buffer{})
_, err := BuildTektonBundle(justEnoughObj, nil, nil, time.Now(), &bytes.Buffer{})
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -243,14 +247,14 @@ func TestTooManyInBundle(t *testing.T) {
}

// expect error when we hit the max
_, err := BuildTektonBundle(toMuchObj, nil, time.Now(), &bytes.Buffer{})
_, err := BuildTektonBundle(toMuchObj, nil, nil, time.Now(), &bytes.Buffer{})
if err == nil {
t.Errorf("expected error: %v", toManyObjErr)
}
}

func TestDeterministicLayers(t *testing.T) {
img, err := BuildTektonBundle(threeTasks, nil, time.Now(), &bytes.Buffer{})
img, err := BuildTektonBundle(threeTasks, nil, nil, time.Now(), &bytes.Buffer{})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
Expand Down Expand Up @@ -282,7 +286,7 @@ func TestDeterministicLayers(t *testing.T) {
}

func TestDeterministicManifest(t *testing.T) {
img, err := BuildTektonBundle(threeTasks, nil, time.Time{}, &bytes.Buffer{})
img, err := BuildTektonBundle(threeTasks, nil, nil, time.Time{}, &bytes.Buffer{})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
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 @@ -105,7 +105,7 @@ func TestListCommand(t *testing.T) {
t.Fatal(err)
}

img, err := bundle.BuildTektonBundle([]string{examplePullTask, examplePullPipeline}, nil, time.Now(), &bytes.Buffer{})
img, err := bundle.BuildTektonBundle([]string{examplePullTask, examplePullPipeline}, nil, nil, time.Now(), &bytes.Buffer{})
if err != nil {
t.Fatal(err)
}
Expand Down
9 changes: 8 additions & 1 deletion pkg/cmd/bundle/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ type pushOptions struct {
remoteOptions bundle.RemoteOptions
annotationParams []string
annotations map[string]string
labelParams []string
labels map[string]string
ctimeParam string
ctime time.Time
}
Expand Down Expand Up @@ -101,6 +103,7 @@ Created time:
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", "", []string{}, "OCI Manifest annotation in the form of key=value to be added to the OCI image. Can be provided multiple times to add multiple annotations.")
c.Flags().StringVar(&opts.ctimeParam, "ctime", "", "YYYY-MM-DD, YYYY-MM-DDTHH:MM:SS or RFC3339 formatted created time to set, defaults to current time. In non RFC3339 syntax dates are in UTC timezone.")
c.Flags().StringSliceVarP(&opts.labelParams, "label", "", []string{}, "OCI Config labels in the form of key=value to be added to the OCI image. Can be provided multiple times to add multiple labels.")
bundle.AddRemoteFlags(c.Flags(), &opts.remoteOptions)

return c
Expand Down Expand Up @@ -137,6 +140,10 @@ func (p *pushOptions) parseArgsAndFlags(args []string) (err error) {
return err
}

if p.labels, err = params.ParseParams(p.labelParams); err != nil {
return err
}

if p.ctime, err = determineCTime(p.ctimeParam); err != nil {
return err
}
Expand All @@ -150,7 +157,7 @@ func (p *pushOptions) Run(args []string) error {
return err
}

img, err := bundle.BuildTektonBundle(p.bundleContents, p.annotations, p.ctime, p.stream.Out)
img, err := bundle.BuildTektonBundle(p.bundleContents, p.annotations, p.labels, p.ctime, p.stream.Out)
if err != nil {
return err
}
Expand Down
17 changes: 17 additions & 0 deletions pkg/cmd/bundle/push_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ func TestPushCommand(t *testing.T) {
expectedAnnotations map[string]string
ctime string
expectedCTime time.Time
labels []string
expectedLabels map[string]string
}{
{
name: "single-input",
Expand Down Expand Up @@ -102,6 +104,16 @@ func TestPushCommand(t *testing.T) {
ctime: fixedTime.Format(time.RFC3339),
expectedCTime: fixedTime,
},
{
name: "with-labels",
files: map[string]string{
"simple.yaml": exampleTask,
},
expectedContents: map[string]expected{exampleTaskExpected.name: exampleTaskExpected},
labels: []string{"version=1.0", "quay.expires-after=7d"},
expectedLabels: map[string]string{"version": "1.0", "quay.expires-after": "7d"},
expectedCTime: time.Unix(defaultTimestamp, 0),
},
}

for _, tc := range testcases {
Expand Down Expand Up @@ -149,6 +161,7 @@ func TestPushCommand(t *testing.T) {
annotationParams: tc.annotations,
remoteOptions: bundle.RemoteOptions{},
ctimeParam: tc.ctime,
labelParams: tc.labels,
}

if err := opts.Run([]string{ref}); err != nil {
Expand All @@ -174,6 +187,10 @@ func TestPushCommand(t *testing.T) {
t.Errorf("Expected created time to be %s, but it was %s", tc.expectedCTime, config.Created.Time)
}

if !cmp.Equal(config.Config.Labels, tc.expectedLabels) {
t.Errorf("Expected labels to be %+v, but it was %+v", tc.expectedLabels, config.Config.Labels)
}

layers, err := img.Layers()
if err != nil {
t.Fatal(err)
Expand Down

0 comments on commit 4877295

Please sign in to comment.