Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for OCI labels on image/bundle push #552

Merged
5 changes: 3 additions & 2 deletions pkg/imgpkg/bundle/contents.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,14 @@ func NewContents(paths []string, excludedPaths []string, preservePermissions boo
}

// Push the contents of the bundle to the registry as an OCI Image
func (b Contents) Push(uploadRef regname.Tag, registry ImagesMetadataWriter, logger Logger) (string, error) {
func (b Contents) Push(uploadRef regname.Tag, labels map[string]string, registry ImagesMetadataWriter, logger Logger) (string, error) {
err := b.validate()
if err != nil {
return "", err
}

labels := map[string]string{BundleConfigLabel: "true"}
labels[BundleConfigLabel] = "true"
phenixblue marked this conversation as resolved.
Show resolved Hide resolved

return plainimage.NewContents(b.paths, b.excludedPaths, b.preservePermissions).Push(uploadRef, labels, registry, logger)
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/imgpkg/bundle/contents_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ images:
t.Fatalf("failed to read tag: %s", err)
}

_, err = subject.Push(imgTag, fakeRegistry, util.NewNoopLevelLogger())
_, err = subject.Push(imgTag, make(map[string]string), fakeRegistry, util.NewNoopLevelLogger())
phenixblue marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
t.Fatalf("not expecting push to fail: %s", err)
}
Expand Down Expand Up @@ -78,7 +78,7 @@ images:
t.Fatalf("failed to read tag: %s", err)
}

_, err = subject.Push(imgTag, fakeRegistry, util.NewNoopLevelLogger())
_, err = subject.Push(imgTag, map[string]string{}, fakeRegistry, util.NewNoopLevelLogger())
if err != nil {
t.Fatalf("not expecting push to fail: %s", err)
}
Expand Down
16 changes: 16 additions & 0 deletions pkg/imgpkg/cmd/label_flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2020 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0

package cmd

import (
"github.com/spf13/cobra"
)

type LabelFlags struct {

Check warning on line 10 in pkg/imgpkg/cmd/label_flags.go

View workflow job for this annotation

GitHub Actions / lint

exported: exported type LabelFlags should have comment or be unexported (revive)
Labels map[string]string
}

func (l *LabelFlags) Set(cmd *cobra.Command) {

Check warning on line 14 in pkg/imgpkg/cmd/label_flags.go

View workflow job for this annotation

GitHub Actions / lint

exported: exported method LabelFlags.Set should have comment or be unexported (revive)
cmd.Flags().StringToStringVarP(&l.Labels, "labels", "l", make(map[string]string), "Set labels on image")
joaopapereira marked this conversation as resolved.
Show resolved Hide resolved
}
30 changes: 28 additions & 2 deletions pkg/imgpkg/cmd/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type PushOptions struct {
LockOutputFlags LockOutputFlags
FileFlags FileFlags
RegistryFlags RegistryFlags
LabelFlags LabelFlags
}

func NewPushOptions(ui ui.UI) *PushOptions {
Expand All @@ -47,6 +48,8 @@ func NewPushCmd(o *PushOptions) *cobra.Command {
o.LockOutputFlags.SetOnPush(cmd)
o.FileFlags.Set(cmd)
o.RegistryFlags.Set(cmd)
o.LabelFlags.Set(cmd)

return cmd
}

Expand All @@ -56,11 +59,20 @@ func (po *PushOptions) Run() error {
return err
}

err = po.validateFlags()
if err != nil {
return err
}

var imageURL string

isBundle := po.BundleFlags.Bundle != ""
isImage := po.ImageFlags.Image != ""

if po.LabelFlags.Labels == nil {
phenixblue marked this conversation as resolved.
Show resolved Hide resolved
po.LabelFlags.Labels = map[string]string{}
}

switch {
case isBundle && isImage:
return fmt.Errorf("Expected only one of image or bundle")
Expand Down Expand Up @@ -96,7 +108,7 @@ func (po *PushOptions) pushBundle(registry registry.Registry) (string, error) {
}

logger := util.NewUILevelLogger(util.LogWarn, util.NewLogger(po.ui))
imageURL, err := bundle.NewContents(po.FileFlags.Files, po.FileFlags.ExcludedFilePaths, po.FileFlags.PreservePermissions).Push(uploadRef, registry, logger)
imageURL, err := bundle.NewContents(po.FileFlags.Files, po.FileFlags.ExcludedFilePaths, po.FileFlags.PreservePermissions).Push(uploadRef, po.LabelFlags.Labels, registry, logger)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -141,5 +153,19 @@ func (po *PushOptions) pushImage(registry registry.Registry) (string, error) {
}

logger := util.NewUILevelLogger(util.LogWarn, util.NewLogger(po.ui))
return plainimage.NewContents(po.FileFlags.Files, po.FileFlags.ExcludedFilePaths, po.FileFlags.PreservePermissions).Push(uploadRef, nil, registry, logger)
return plainimage.NewContents(po.FileFlags.Files, po.FileFlags.ExcludedFilePaths, po.FileFlags.PreservePermissions).Push(uploadRef, po.LabelFlags.Labels, registry, logger)
}

// validateFlags checks if the provided flags are valid
func (po *PushOptions) validateFlags() error {

// Verify the user did NOT specify a reserved OCI label
_, present := po.LabelFlags.Labels[bundle.BundleConfigLabel]

if present {
return fmt.Errorf("label '%s' is reserved and cannot be overriden. Please use a different key", bundle.BundleConfigLabel)
}

return nil

}
84 changes: 84 additions & 0 deletions pkg/imgpkg/cmd/push_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ import (
"strings"
"testing"

"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/vmware-tanzu/carvel-imgpkg/test/helpers"
)

const emptyImagesYaml = `apiVersion: imgpkg.carvel.dev/v1alpha1
Expand Down Expand Up @@ -235,6 +239,86 @@ func TestImageAndBundleLockError(t *testing.T) {
}
}

func TestLabels(t *testing.T) {
phenixblue marked this conversation as resolved.
Show resolved Hide resolved
testCases := []struct {
name string
opType string
expectedError string
expectedLabels map[string]string
labelInput string
}{
{
name: "bundle with multiple labels",
opType: "bundle",
expectedError: "",
labelInput: "foo=bar,bar=baz",
expectedLabels: map[string]string{"dev.carvel.imgpkg.bundle": "true", "foo": "bar", "bar": "baz"},
},
{
name: "image with multiple labels",
opType: "image",
expectedError: "",
labelInput: "foo=bar,bar=baz",
expectedLabels: map[string]string{"foo": "bar", "bar": "baz"},
},
{
name: "bundle with \".\" in label key",
opType: "bundle",
expectedError: "",
labelInput: "foo.bar=baz",
expectedLabels: map[string]string{"dev.carvel.imgpkg.bundle": "true", "foo.bar": "baz"},
},
{
name: "bundle with long label key (> 64 chars)",
opType: "bundle",
expectedError: "",
labelInput: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=baz",
expectedLabels: map[string]string{"dev.carvel.imgpkg.bundle": "true", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": "baz"},
},
{
name: "bundle with long label value (> 256 chars)",
opType: "bundle",
expectedError: "",
labelInput: "foo=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
expectedLabels: map[string]string{"dev.carvel.imgpkg.bundle": "true", "foo": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
},
}

for _, tc := range testCases {
f := func(t *testing.T) {
env := helpers.BuildEnv(t)
imgpkg := helpers.Imgpkg{T: t, ImgpkgPath: env.ImgpkgPath}
defer env.Cleanup()

opTypeFlag := "-b"
pushDir := env.BundleFactory.CreateBundleDir(helpers.BundleYAML, helpers.ImagesYAML)

if tc.opType == "image" {
opTypeFlag = "-i"
pushDir = env.Assets.CreateAndCopySimpleApp("image-to-push")
}

if tc.labelInput == "" {
imgpkg.Run([]string{"push", opTypeFlag, env.Image, "-f", pushDir})
} else {
imgpkg.Run([]string{"push", opTypeFlag, env.Image, "-l", tc.labelInput, "-f", pushDir})
}

ref, _ := name.NewTag(env.Image, name.WeakValidation)
image, err := remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain))
require.NoError(t, err)

config, err := image.ConfigFile()
require.NoError(t, err)

require.Equal(t, tc.expectedLabels, config.Config.Labels, "Expected labels provided via flags to match labels discovered on image")

}

t.Run(tc.name, f)
}
}

func Cleanup(dirs ...string) {
for _, dir := range dirs {
os.RemoveAll(dir)
Expand Down
Loading