Skip to content

Commit

Permalink
Adding acceptance test for manifest commands
Browse files Browse the repository at this point in the history
Signed-off-by: Juan Bustamante <jbustamante@vmware.com>
  • Loading branch information
jjbustamante committed May 7, 2024
1 parent 09a6373 commit 922af8b
Show file tree
Hide file tree
Showing 10 changed files with 229 additions and 20 deletions.
144 changes: 144 additions & 0 deletions acceptance/acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ import (
"testing"
"time"

"github.com/buildpacks/imgutil"
"github.com/buildpacks/lifecycle/api"
dockertypes "github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/types"
"github.com/pelletier/go-toml"
"github.com/sclevine/spec"
"github.com/sclevine/spec/report"
Expand Down Expand Up @@ -623,6 +626,147 @@ func testWithoutSpecificBuilderRequirement(
})
})
})

when("manifest", func() {
var (
indexRepoName string
repoName1 string
repoName2 string
indexLocalPath string
tmpDir string
err error
)

it.Before(func() {
h.SkipIf(t, !pack.SupportsFeature(invoke.ManifestCommands), "pack manifest commands are available since 0.34.0")

// local storage path
tmpDir, err = os.MkdirTemp("", "manifest-commands-test")
assert.Nil(err)
os.Setenv("XDG_RUNTIME_DIR", tmpDir)

// manifest commands are experimental
pack.EnableExperimental()

// used to avoid authentication issues with the local registry
os.Setenv("DOCKER_CONFIG", registryConfig.DockerConfigDir)
})

it.After(func() {
assert.Succeeds(os.RemoveAll(tmpDir))
})

when("create", func() {
it.Before(func() {
it.Before(func() {
indexRepoName = registryConfig.RepoName(h.NewRandomIndexRepoName())

// Manifest 1
repoName1 = fmt.Sprintf("%s:%s", indexRepoName, "busybox-amd64")
h.CreateRemoteImage(t, indexRepoName, "busybox-amd64", "busybox@sha256:a236a6469768c17ca1a6ac81a35fe6fbc1efd76b0dcdf5aebb1cf5f0774ee539")

// Manifest 2
repoName2 = fmt.Sprintf("%s:%s", indexRepoName, "busybox-arm64")
h.CreateRemoteImage(t, indexRepoName, "busybox-arm64", "busybox@sha256:0bcc1b827b855c65eaf6e031e894e682b6170160b8a676e1df7527a19d51fb1a")
})
})
when("--publish", func() {
it("creates and push the index to a remote registry", func() {
output := pack.RunSuccessfully("manifest", "create", "--publish", indexRepoName, repoName1, repoName2)
assertions.NewOutputAssertionManager(t, output).ReportsSuccessfulIndexPushed(indexRepoName)
h.AssertRemoteImageIndex(t, indexRepoName, types.OCIImageIndex, 2)
})
})

when("no --publish", func() {
it("creates the index locally", func() {
output := pack.RunSuccessfully("manifest", "create", indexRepoName, repoName1, repoName2)
assertions.NewOutputAssertionManager(t, output).ReportsSuccessfulIndexLocallyCreated(indexRepoName)

indexLocalPath = filepath.Join(tmpDir, imgutil.MakeFileSafeName(indexRepoName))
index := h.ReadIndexManifest(t, indexLocalPath)
h.AssertEq(t, len(index.Manifests), 2)
h.AssertEq(t, index.MediaType, types.OCIImageIndex)
})
})
})

when("index is already created", func() {
var digest v1.Hash

it.Before(func() {
indexRepoName = registryConfig.RepoName(h.NewRandomIndexRepoName())

// Manifest 1
repoName1 = fmt.Sprintf("%s:%s", indexRepoName, "busybox-amd64")
image1 := h.CreateRemoteImage(t, indexRepoName, "busybox-amd64", "busybox@sha256:a236a6469768c17ca1a6ac81a35fe6fbc1efd76b0dcdf5aebb1cf5f0774ee539")
digest, err = image1.Digest()
assert.Nil(err)

// Manifest 2
repoName2 = fmt.Sprintf("%s:%s", indexRepoName, "busybox-arm64")
h.CreateRemoteImage(t, indexRepoName, "busybox-arm64", "busybox@sha256:0bcc1b827b855c65eaf6e031e894e682b6170160b8a676e1df7527a19d51fb1a")

// create an index locally
pack.RunSuccessfully("manifest", "create", indexRepoName, repoName1)
indexLocalPath = filepath.Join(tmpDir, imgutil.MakeFileSafeName(indexRepoName))
})

when("add", func() {
it("adds the manifest to the index", func() {
output := pack.RunSuccessfully("manifest", "add", indexRepoName, repoName2)
assertions.NewOutputAssertionManager(t, output).ReportsSuccessfulManifestAddedToIndex(repoName2)

index := h.ReadIndexManifest(t, indexLocalPath)
h.AssertEq(t, len(index.Manifests), 2)
h.AssertEq(t, index.MediaType, types.OCIImageIndex)
})
})

when("remove", func() {
it("removes the index from local storage", func() {
output := pack.RunSuccessfully("manifest", "remove", indexRepoName)
assertions.NewOutputAssertionManager(t, output).ReportsSuccessfulIndexDeleted()

h.AssertPathDoesNotExists(t, indexLocalPath)
})
})

when("annotate", func() {
it("adds annotations to the manifest in the index", func() {
output := pack.RunSuccessfully("manifest", "annotate", indexRepoName, repoName1, "--annotations", "foo=bar")
assertions.NewOutputAssertionManager(t, output).ReportsSuccessfulIndexAnnotated(repoName1, indexRepoName)

index := h.ReadIndexManifest(t, indexLocalPath)
h.AssertEq(t, len(index.Manifests), 1)
h.AssertEq(t, len(index.Manifests[0].Annotations), 1)
})
})

when("rm", func() {
it.Before(func() {
// we need to point to the manifest digest we want to delete
repoName1 = fmt.Sprintf("%s@%s", repoName1, digest.String())
})

it("removes the manifest from the index", func() {
output := pack.RunSuccessfully("manifest", "rm", indexRepoName, repoName1)
assertions.NewOutputAssertionManager(t, output).ReportsSuccessfulRemoveManifestFromIndex(indexRepoName)

index := h.ReadIndexManifest(t, indexLocalPath)
h.AssertEq(t, len(index.Manifests), 0)
})
})

when("push", func() {
it("pushes the index to a remote registry", func() {
output := pack.RunSuccessfully("manifest", "push", indexRepoName)
assertions.NewOutputAssertionManager(t, output).ReportsSuccessfulIndexPushed(indexRepoName)
h.AssertRemoteImageIndex(t, indexRepoName, types.OCIImageIndex, 1)
})
})
})
})
}

func testAcceptance(
Expand Down
36 changes: 36 additions & 0 deletions acceptance/assertions/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,42 @@ func (o OutputAssertionManager) ReportsSuccessfulImageBuild(name string) {
o.assert.ContainsF(o.output, "Successfully built image '%s'", name)
}

func (o OutputAssertionManager) ReportsSuccessfulIndexLocallyCreated(name string) {
o.testObject.Helper()

o.assert.ContainsF(o.output, "Successfully created manifest list '%s'", name)
}

func (o OutputAssertionManager) ReportsSuccessfulIndexPushed(name string) {
o.testObject.Helper()

o.assert.ContainsF(o.output, "Successfully pushed manifest list '%s' to registry", name)
}

func (o OutputAssertionManager) ReportsSuccessfulManifestAddedToIndex(name string) {
o.testObject.Helper()

o.assert.ContainsF(o.output, "Successfully added image '%s' to index", name)
}

func (o OutputAssertionManager) ReportsSuccessfulIndexDeleted() {
o.testObject.Helper()

o.assert.Contains(o.output, "Successfully deleted manifest list(s) from local storage")
}

func (o OutputAssertionManager) ReportsSuccessfulIndexAnnotated(name, manifest string) {
o.testObject.Helper()

o.assert.ContainsF(o.output, "Successfully annotated image '%s' in index '%s'", name, manifest)
}

func (o OutputAssertionManager) ReportsSuccessfulRemoveManifestFromIndex(name string) {
o.testObject.Helper()

o.assert.ContainsF(o.output, "Successfully removed image(s) from index: '%s'", name)
}

func (o OutputAssertionManager) ReportSuccessfulQuietBuild(name string) {
o.testObject.Helper()
o.testObject.Log("quiet mode")
Expand Down
4 changes: 4 additions & 0 deletions acceptance/invoke/pack.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ const (
PlatformRetries
FlattenBuilderCreationV2
FixesRunImageMetadata
ManifestCommands
)

var featureTests = map[Feature]func(i *PackInvoker) bool{
Expand Down Expand Up @@ -274,6 +275,9 @@ var featureTests = map[Feature]func(i *PackInvoker) bool{
FixesRunImageMetadata: func(i *PackInvoker) bool {
return i.atLeast("v0.34.0")
},
ManifestCommands: func(i *PackInvoker) bool {
return i.atLeast("v0.34.0")
},
}

func (i *PackInvoker) SupportsFeature(f Feature) bool {
Expand Down
2 changes: 1 addition & 1 deletion pkg/client/manifest_add_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func testAddManifest(t *testing.T, when spec.G, it spec.S) {
h.AssertNil(t, err)

// Create a remote image to be fetched when adding to the image index
fakeImage := h.NewFakeWithRandomUnderlyingV1Image(t, nil)
fakeImage := h.NewFakeWithRandomUnderlyingV1Image(t, "pack/image", nil)
fakeImageFetcher.RemoteImages["index.docker.io/pack/image:latest"] = fakeImage
})
it.After(func() {
Expand Down
12 changes: 6 additions & 6 deletions pkg/client/manifest_annotate.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,29 +59,29 @@ func (c *Client) AnnotateManifest(ctx context.Context, opts ManifestAnnotateOpti

if opts.OS != "" {
if err = idx.SetOS(digest, opts.OS); err != nil {
return fmt.Errorf("failed to set the 'os' for '%s': %w", opts.RepoName, err)
return fmt.Errorf("failed to set the 'os' for %s: %w", style.Symbol(opts.RepoName), err)
}
}
if opts.OSArch != "" {
if err = idx.SetArchitecture(digest, opts.OSArch); err != nil {
return fmt.Errorf("failed to set the 'arch' for '%s': %w", opts.RepoName, err)
return fmt.Errorf("failed to set the 'arch' for %s: %w", style.Symbol(opts.RepoName), err)
}
}
if opts.OSVariant != "" {
if err = idx.SetVariant(digest, opts.OSVariant); err != nil {
return fmt.Errorf("failed to set the 'os variant' for '%s': %w", opts.RepoName, err)
return fmt.Errorf("failed to set the 'os variant' for %s: %w", style.Symbol(opts.RepoName), err)
}
}
if len(opts.Annotations) != 0 {
if err = idx.SetAnnotations(digest, opts.Annotations); err != nil {
return fmt.Errorf("failed to set the 'annotations' for '%s': %w", opts.RepoName, err)
return fmt.Errorf("failed to set the 'annotations' for %s: %w", style.Symbol(opts.RepoName), err)
}
}

if err = idx.SaveDir(); err != nil {
return fmt.Errorf("failed to save manifest list '%s' to local storage: %w", opts.RepoName, err)
return fmt.Errorf("failed to save manifest list %s to local storage: %w", style.Symbol(opts.RepoName), err)
}

c.logger.Infof("Successfully annotated image '%s' in index '%s'", style.Symbol(opts.RepoName), style.Symbol(opts.IndexRepoName))
c.logger.Infof("Successfully annotated image %s in index %s", style.Symbol(opts.RepoName), style.Symbol(opts.IndexRepoName))
return nil
}
10 changes: 5 additions & 5 deletions pkg/client/manifest_annotate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const invalidDigest = "sha256:d4707523ce6e12afdbe9a3be5ad69027150a834870ca0933ba
func TestAnnotateManifest(t *testing.T) {
color.Disable(true)
defer color.Disable(false)
spec.Run(t, "build", testAnnotateManifest, spec.Parallel(), spec.Report(report.Terminal{}))
spec.Run(t, "build", testAnnotateManifest, spec.Sequential(), spec.Report(report.Terminal{}))
}

func testAnnotateManifest(t *testing.T, when spec.G, it spec.S) {
Expand Down Expand Up @@ -97,7 +97,7 @@ func testAnnotateManifest(t *testing.T, when spec.G, it spec.S) {
indexRepoName = h.NewRandomIndexRepoName()
idx, digest = h.RandomCNBIndexAndDigest(t, indexRepoName, 1, 2)
mockIndexFactory.EXPECT().LoadIndex(gomock.Eq(indexRepoName), gomock.Any()).Return(idx, nil)
fakeImage := h.NewFakeWithRandomUnderlyingV1Image(t, digest)
fakeImage := h.NewFakeWithRandomUnderlyingV1Image(t, "pack/image", digest)
fakeImageFetcher.RemoteImages[digest.Name()] = fakeImage
})

Expand All @@ -122,7 +122,7 @@ func testAnnotateManifest(t *testing.T, when spec.G, it spec.S) {
indexRepoName = h.NewRandomIndexRepoName()
idx, digest = h.RandomCNBIndexAndDigest(t, indexRepoName, 1, 2)
mockIndexFactory.EXPECT().LoadIndex(gomock.Eq(indexRepoName), gomock.Any()).Return(idx, nil)
fakeImage := h.NewFakeWithRandomUnderlyingV1Image(t, digest)
fakeImage := h.NewFakeWithRandomUnderlyingV1Image(t, "pack/image", digest)
fakeImageFetcher.RemoteImages[digest.Name()] = fakeImage
})

Expand All @@ -147,7 +147,7 @@ func testAnnotateManifest(t *testing.T, when spec.G, it spec.S) {
indexRepoName = h.NewRandomIndexRepoName()
idx, digest = h.RandomCNBIndexAndDigest(t, indexRepoName, 1, 2)
mockIndexFactory.EXPECT().LoadIndex(gomock.Eq(indexRepoName), gomock.Any()).Return(idx, nil)
fakeImage := h.NewFakeWithRandomUnderlyingV1Image(t, digest)
fakeImage := h.NewFakeWithRandomUnderlyingV1Image(t, "pack/image", digest)
fakeImageFetcher.RemoteImages[digest.Name()] = fakeImage
})

Expand All @@ -172,7 +172,7 @@ func testAnnotateManifest(t *testing.T, when spec.G, it spec.S) {
indexRepoName = h.NewRandomIndexRepoName()
idx, digest = h.RandomCNBIndexAndDigest(t, indexRepoName, 1, 2)
mockIndexFactory.EXPECT().LoadIndex(gomock.Eq(indexRepoName), gomock.Any()).Return(idx, nil)
fakeImage := h.NewFakeWithRandomUnderlyingV1Image(t, digest)
fakeImage := h.NewFakeWithRandomUnderlyingV1Image(t, "pack/image", digest)
fakeImageFetcher.RemoteImages[digest.Name()] = fakeImage
})

Expand Down
6 changes: 3 additions & 3 deletions pkg/client/manifest_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ func (c *Client) CreateManifest(ctx context.Context, opts CreateManifestOptions)
return err
}

c.logger.Infof("Successfully pushed manifest list '%s' to registry", style.Symbol(opts.IndexRepoName))
c.logger.Infof("Successfully pushed manifest list %s to registry", style.Symbol(opts.IndexRepoName))
return nil
}

if err = index.SaveDir(); err != nil {
return fmt.Errorf("manifest list '%s' could not be saved to local storage: %w", style.Symbol(opts.IndexRepoName), err)
return fmt.Errorf("manifest list %s could not be saved to local storage: %w", style.Symbol(opts.IndexRepoName), err)
}

c.logger.Infof("Successfully created manifest list '%s'", style.Symbol(opts.IndexRepoName))
c.logger.Infof("Successfully created manifest list %s", style.Symbol(opts.IndexRepoName))
return nil
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/client/manifest_create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func testCreateManifest(t *testing.T, when spec.G, it spec.S) {

when("remote manifest is provided", func() {
it.Before(func() {
fakeImage := h.NewFakeWithRandomUnderlyingV1Image(t, nil)
fakeImage := h.NewFakeWithRandomUnderlyingV1Image(t, "pack/image", nil)
fakeImageFetcher.RemoteImages["index.docker.io/library/busybox:1.36-musl"] = fakeImage
})

Expand Down
4 changes: 2 additions & 2 deletions pkg/client/manifest_push.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ func (c *Client) PushManifest(opts PushManifestOptions) (err error) {
}

if err = idx.Push(ops...); err != nil {
return fmt.Errorf("failed to push manifest list '%s': %w", style.Symbol(opts.IndexRepoName), err)
return fmt.Errorf("failed to push manifest list %s: %w", style.Symbol(opts.IndexRepoName), err)
}

if !opts.Purge {
c.logger.Infof("Successfully pushed manifest list '%s'", style.Symbol(opts.IndexRepoName))
c.logger.Infof("Successfully pushed manifest list %s to registry", style.Symbol(opts.IndexRepoName))
return nil
}

Expand Down
Loading

0 comments on commit 922af8b

Please sign in to comment.