Skip to content

Commit

Permalink
Merge pull request #3 from WYGIN/image-index
Browse files Browse the repository at this point in the history
WIP added pack manifest cli
  • Loading branch information
jjbustamante authored Apr 24, 2024
2 parents ee7c3a3 + 6e65ec8 commit 5168fa3
Show file tree
Hide file tree
Showing 45 changed files with 2,794 additions and 537 deletions.
18 changes: 0 additions & 18 deletions .gitpod.yml

This file was deleted.

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,4 @@ require (

go 1.22

replace github.com/buildpacks/imgutil => github.com/drac98/imgutil v1.4.0
replace github.com/buildpacks/imgutil => github.com/WYGIN/buildpacks-imgutil v0.0.0-20240304130223-abfcabf596ce
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7
github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w=
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg=
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/WYGIN/buildpacks-imgutil v0.0.0-20240304130223-abfcabf596ce h1:7ntd8izCev/sa05xJPxpWBK0Db0o+LX57yiX6ZCR5D0=
github.com/WYGIN/buildpacks-imgutil v0.0.0-20240304130223-abfcabf596ce/go.mod h1:7zUmt4wkVJNuXCZhQndEd3kvVGmWLVyzRFIQXTaeXlU=
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
Expand Down Expand Up @@ -91,10 +93,10 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/buildpacks/imgutil v0.0.0-20240206215312-f8d38e1de03d h1:b/tiReZf9jorbpaOwVw9MX3n99w7Ta4u+SVV7yy6XCE=
github.com/buildpacks/imgutil v0.0.0-20240206215312-f8d38e1de03d/go.mod h1:7zUmt4wkVJNuXCZhQndEd3kvVGmWLVyzRFIQXTaeXlU=
github.com/buildpacks/lifecycle v0.19.3 h1:T6dwX+/Nq7Q41Pb2zVu54MLrJPt93KEMNj4dHkXINbA=
github.com/buildpacks/lifecycle v0.19.3/go.mod h1:BoLvGP1fjOqab59dariHDhVh5uIQuQ7yoIfj0orvL8M=
github.com/buildpacks/lifecycle v0.18.5 h1:lfoUX8jYCUZ2/Tr2AopaRjinqDivkNkcTChzysQTo00=
github.com/buildpacks/lifecycle v0.18.5/go.mod h1:Kvuu9IWABPLXc6yHCMtbdmgrGEi7QEiVzi5GGtcAkW0=
github.com/buildpacks/imgutil v0.0.0-20240118145509-e94a1b7de8a9 h1:kxe31xfMWJAIAzDfGQ3lL0j8QSSRfEHyLg7dRWIHA8I=
github.com/buildpacks/imgutil v0.0.0-20240118145509-e94a1b7de8a9/go.mod h1:PsazEB9yz+NG/cgm0Z1oQ0Xq6rD/U7eNMt5Su41afYY=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
Expand Down Expand Up @@ -139,8 +141,6 @@ github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQ
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/drac98/imgutil v1.4.0 h1:b8dtuhmfyYrWfjxRyg92MofmVt/bKgq3izY7Sc7NsMY=
github.com/drac98/imgutil v1.4.0/go.mod h1:mBG5M3GJW5nknCEOOqtmMHyPYnSpw/5GEiciuYU/COw=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/buildpack_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func BuildpackInspect(logger logging.Logger, cfg config.Config, client PackClien
return cmd
}

func buildpackInspect(logger logging.Logger, buildpackName, registryName string, flags BuildpackInspectFlags, cfg config.Config, pack PackClient) error {
func buildpackInspect(logger logging.Logger, buildpackName, registryName string, flags BuildpackInspectFlags, _ config.Config, pack PackClient) error {
logger.Infof("Inspecting buildpack: %s\n", style.Symbol(buildpackName))

inspectedBuildpacksOutput, err := inspectAllBuildpacks(
Expand Down
15 changes: 8 additions & 7 deletions internal/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ type PackClient interface {
InspectExtension(client.InspectExtensionOptions) (*client.ExtensionInfo, error)
PullBuildpack(context.Context, client.PullBuildpackOptions) error
DownloadSBOM(name string, options client.DownloadSBOMOptions) error
CreateManifest(context.Context, client.CreateManifestOptions) error
AnnotateManifest(context.Context, client.AnnotateManifestOptions) error
AddManifest(context.Context, client.AddManifestOptions) error
PushManifest(context.Context, client.PushManifestOptions) error
RemoveManifest(context.Context, client.RemoveManifestOptions) error
DeleteManifest(context.Context, client.DeleteManifestOptions) error
InspectManifest(context.Context, client.InspectManifestOptions) error
CreateManifest(ctx context.Context, name string, images []string, opts client.CreateManifestOptions) error
AnnotateManifest(ctx context.Context, name string, image string, opts client.ManifestAnnotateOptions) error
ExistsManifest(ctx context.Context, image string) error
AddManifest(ctx context.Context, index string, images string, opts client.ManifestAddOptions) error
DeleteManifest(ctx context.Context, name []string) []error
RemoveManifest(ctx context.Context, name string, images []string) []error
PushManifest(ctx context.Context, index string, opts client.PushManifestOptions) error
InspectManifest(ctx context.Context, name string) error
}

func AddHelpFlag(cmd *cobra.Command, commandName string) {
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/extension_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func ExtensionInspect(logger logging.Logger, cfg config.Config, client PackClien
return cmd
}

func extensionInspect(logger logging.Logger, extensionName string, cfg config.Config, pack PackClient) error {
func extensionInspect(logger logging.Logger, extensionName string, _ config.Config, pack PackClient) error {
logger.Infof("Inspecting extension: %s\n", style.Symbol(extensionName))

inspectedExtensionsOutput, err := inspectAllExtensions(
Expand Down
14 changes: 7 additions & 7 deletions internal/commands/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ import (

func NewManifestCommand(logger logging.Logger, client PackClient) *cobra.Command {
cmd := &cobra.Command{
Use: "manifest",
Aliases: []string{"manifest"},
Short: "Handle manifest list",
RunE: nil,
Use: "manifest",
Short: "Interact with image index",
RunE: nil,
}

cmd.AddCommand(ManifestCreate(logger, client))
cmd.AddCommand(ManifestAnnotate(logger, client))
cmd.AddCommand(ManifestAdd(logger, client))
cmd.AddCommand(ManifestPush(logger, client))
cmd.AddCommand(ManifestRemove(logger, client))
cmd.AddCommand(ManifestAnnotate(logger, client))
cmd.AddCommand(ManifestDelete(logger, client))
cmd.AddCommand(ManifestInspect(logger, client))
cmd.AddCommand(ManifestPush(logger, client))
cmd.AddCommand(ManifestRemove(logger, client))
cmd.AddCommand(ManifestExists(logger, client))

AddHelpFlag(cmd, "manifest")
return cmd
Expand Down
79 changes: 38 additions & 41 deletions internal/commands/manifest_add.go
Original file line number Diff line number Diff line change
@@ -1,70 +1,67 @@
package commands

import (
"path/filepath"
"errors"

"github.com/spf13/cobra"

"github.com/buildpacks/pack/internal/config"
"github.com/buildpacks/pack/internal/style"
"github.com/buildpacks/pack/pkg/client"
"github.com/buildpacks/pack/pkg/logging"
)

// ManifestAddFlags define flags provided to the ManifestAdd
type ManifestAddFlags struct {
All bool
Architecture string
OS string
Variant string
os, osVersion, osArch, osVariant string
osFeatures, features []string
annotations map[string]string
all bool
}

// ManifestAdd modifies a manifest list (Image index) and add a new image to the list of manifests.
func ManifestAdd(logger logging.Logger, pack PackClient) *cobra.Command {
var flags ManifestAddFlags
cmd := &cobra.Command{
Use: "add [OPTIONS] <manifest-list> <manifest>",
Short: "Add a new image to the manifest list",
Args: cobra.MatchAll(cobra.ExactArgs(2)),
Example: `pack manifest add cnbs/sample-package:hello-multiarch-universe \
cnbs/sample-package:hello-universe-riscv-linux`,
Long: "manifest add modifies a manifest list (Image index) and add a new image to the list of manifests.",
RunE: logError(logger, func(cmd *cobra.Command, args []string) error {
if err := validateManifestAddFlags(&flags); err != nil {
return err
}

indexName := args[0]
manifest := args[1]

packHome, err := config.PackHome()
if err != nil {
return err
}

manifestDir := filepath.Join(packHome, "manifests")

if err := pack.AddManifest(cmd.Context(), client.AddManifestOptions{
Index: indexName,
Path: manifestDir,
Manifest: manifest,
All: flags.All,
}); err != nil {
cmd := &cobra.Command{
Use: "add [OPTIONS] <manifest-list> <manifest> [flags]",
Args: cobra.MatchAll(cobra.ExactArgs(2), cobra.OnlyValidArgs),
Short: "Add an image to a manifest list or image index.",
Example: `pack manifest add cnbs/sample-package:hello-multiarch-universe \
cnbs/sample-package:hello-universe-riscv-linux`,
Long: `manifest add modifies a manifest list (Image index) and add a new image to the list of manifests.`,
RunE: logError(logger, func(cmd *cobra.Command, args []string) (err error) {
if err := validateManifestAddFlags(flags); err != nil {
return err
}
logger.Infof("Successfully added the image %s to the image index %s", style.Symbol(manifest), style.Symbol(indexName))

return nil
return pack.AddManifest(cmd.Context(), args[0], args[1], client.ManifestAddOptions{
OS: flags.os,
OSVersion: flags.osVersion,
OSArch: flags.osArch,
OSVariant: flags.osVariant,
OSFeatures: flags.osFeatures,
Features: flags.features,
Annotations: flags.annotations,
All: flags.all,
})
}),
}

cmd.Flags().BoolVar(&flags.All, "all", false, `add all of the contents to the local list (applies only if <manifest> is an index)`)
cmd.Flags().StringVar(&flags.Architecture, "arch", "", "Set the architecture")
cmd.Flags().StringVar(&flags.OS, "os", "", "Set the operating system")
cmd.Flags().StringVar(&flags.Variant, "variant", "", "Set the architecture variant")
cmd.Flags().BoolVar(&flags.all, "all", false, "add all of the contents to the local list (applies only if <manifest> is an index)")
cmd.Flags().StringVar(&flags.os, "os", "", "Set the operating system")
cmd.Flags().StringVar(&flags.osArch, "arch", "", "Set the architecture")
cmd.Flags().StringVar(&flags.osVariant, "variant", "", "Set the architecture variant")
cmd.Flags().StringVar(&flags.osVersion, "os-version", "", "Set the os-version")
cmd.Flags().StringSliceVar(&flags.osFeatures, "os-features", make([]string, 0), "Set the OSFeatures")
cmd.Flags().StringSliceVar(&flags.features, "features", make([]string, 0), "Set the Features")
cmd.Flags().StringToStringVar(&flags.annotations, "annotations", make(map[string]string, 0), "Set the annotations")

AddHelpFlag(cmd, "add")
return cmd
}

func validateManifestAddFlags(p *ManifestAddFlags) error {
func validateManifestAddFlags(flags ManifestAddFlags) error {
if (flags.os != "" && flags.osArch == "") || (flags.os == "" && flags.osArch != "") {
return errors.New("'os' or 'arch' is undefined")
}
return nil
}
133 changes: 133 additions & 0 deletions internal/commands/manifest_add_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package commands_test

import (
"bytes"
"testing"

"github.com/golang/mock/gomock"
"github.com/heroku/color"
"github.com/sclevine/spec"
"github.com/sclevine/spec/report"
"github.com/spf13/cobra"

"github.com/buildpacks/pack/internal/commands"
"github.com/buildpacks/pack/internal/commands/testmocks"
"github.com/buildpacks/pack/pkg/logging"
h "github.com/buildpacks/pack/testhelpers"
)

func TestManifestAddCommand(t *testing.T) {
color.Disable(true)
defer color.Disable(false)

spec.Run(t, "Commands", testManifestAddCommand, spec.Random(), spec.Report(report.Terminal{}))
}

func testManifestAddCommand(t *testing.T, when spec.G, it spec.S) {
var (
command *cobra.Command
logger *logging.LogWithWriters
outBuf bytes.Buffer
mockController *gomock.Controller
mockClient *testmocks.MockPackClient
)

it.Before(func() {
logger = logging.NewLogWithWriters(&outBuf, &outBuf)
mockController = gomock.NewController(t)
mockClient = testmocks.NewMockPackClient(mockController)

command = commands.ManifestAdd(logger, mockClient)
})
it("should add image with current platform specs", func() {
prepareAddManifest(t, mockClient)

command.SetArgs([]string{"some-index", "busybox:1.36-musl"})
err := command.Execute()
h.AssertNil(t, err)
h.AssertEq(t, outBuf.String(), "")
})
it("should add images with given platform", func() {
prepareAddManifest(t, mockClient)

command.SetArgs([]string{
"some-index",
"busybox:1.36-musl",
"--os",
"linux",
"--arch",
"arm",
"--variant",
"v6",
"--os-version",
"22.04",
})
err := command.Execute()
h.AssertNil(t, err)
h.AssertEq(t, outBuf.String(), "")
})
it("should add return an error when platform's os and arch not defined", func() {
prepareAddManifest(t, mockClient)

command.SetArgs([]string{"some-index", "busybox:1.36-musl", "--os", "linux"})
err := command.Execute()
h.AssertEq(t, err.Error(), "'os' or 'arch' is undefined")
h.AssertEq(t, outBuf.String(), "ERROR: 'os' or 'arch' is undefined\n")
})
it("should add all images", func() {
prepareAddManifest(t, mockClient)

command.SetArgs([]string{"some-index", "busybox:1.36-musl", "--all"})
err := command.Execute()
h.AssertNil(t, err)
h.AssertEq(t, outBuf.String(), "")
})
it("should return an error when features defined invalidly", func() {
prepareAddManifest(t, mockClient)

command.SetArgs([]string{"some-index", "busybox:1.36-musl", "--features"})
err := command.Execute()
h.AssertEq(t, err.Error(), "flag needs an argument: --features")
})
it("should return an error when osFeatures defined invalidly", func() {
prepareAddManifest(t, mockClient)

command.SetArgs([]string{"some-index", "busybox:1.36-musl", "--os-features"})
err := command.Execute()
h.AssertEq(t, err.Error(), "flag needs an argument: --os-features")
})
it("should return an error when invalid arg passed", func() {
prepareAddManifest(t, mockClient)

command.SetArgs([]string{"some-index", "busybox:1.36-musl", "--urls"})
err := command.Execute()
h.AssertEq(t, err.Error(), "unknown flag: --urls")
})
it("should return an error when annotations defined invalidly", func() {
prepareAddManifest(t, mockClient)

command.SetArgs([]string{"some-index", "busybox:1.36-musl", "--annotations", "some-key"})
err := command.Execute()
h.AssertEq(t, err.Error(), `invalid argument "some-key" for "--annotations" flag: some-key must be formatted as key=value`)
})
it("should have help flag", func() {
prepareAddManifest(t, mockClient)

command.SetArgs([]string{"--help"})
h.AssertNilE(t, command.Execute())
h.AssertEq(t, outBuf.String(), "")
})
}

func prepareAddManifest(t *testing.T, mockClient *testmocks.MockPackClient) {
mockClient.
EXPECT().
AddManifest(
gomock.Any(),
gomock.Any(),
gomock.Any(),
gomock.Any(),
).
AnyTimes().
Return(nil)
}
Loading

0 comments on commit 5168fa3

Please sign in to comment.