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 a new manifest command to support multi-architecture builds #1952

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ func NewPackCommand(logger ConfigurableLogger) (*cobra.Command, error) {
rootCmd.AddCommand(commands.NewBuildpackCommand(logger, cfg, packClient, buildpackage.NewConfigReader()))
rootCmd.AddCommand(commands.NewExtensionCommand(logger, cfg, packClient, buildpackage.NewConfigReader()))
rootCmd.AddCommand(commands.NewConfigCommand(logger, cfg, cfgPath, packClient))
rootCmd.AddCommand(commands.NewManifestCommand(logger, packClient))
rootCmd.AddCommand(commands.InspectImage(logger, imagewriter.NewFactory(), cfg, packClient))
rootCmd.AddCommand(commands.NewStackCommand(logger))
rootCmd.AddCommand(commands.Rebase(logger, cfg, packClient))
Expand Down
3 changes: 1 addition & 2 deletions internal/builder/testmocks/mock_lifecycle.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions internal/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ type PackClient interface {
InspectImage(string, bool) (*client.ImageInfo, error)
Rebase(context.Context, client.RebaseOptions) error
CreateBuilder(context.Context, client.CreateBuilderOptions) error
CreateManifest(ctx context.Context, name string, images []string, opts client.CreateManifestOptions) (imageID string, err 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) (imageID string, err 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) (imageID string, err error)
InspectManifest(ctx context.Context, name string, opts client.InspectManifestOptions) error
NewBuildpack(context.Context, client.NewBuildpackOptions) error
PackageBuildpack(ctx context.Context, opts client.PackageBuildpackOptions) error
PackageExtension(ctx context.Context, opts client.PackageBuildpackOptions) error
Expand Down
26 changes: 26 additions & 0 deletions internal/commands/manifest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package commands

import (
"github.com/spf13/cobra"

"github.com/buildpacks/pack/pkg/logging"
)

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

cmd.AddCommand(ManifestCreate(logger, client))
cmd.AddCommand(ManifestAdd(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))

AddHelpFlag(cmd, "manifest")
return cmd
}
61 changes: 61 additions & 0 deletions internal/commands/manifest_add.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package commands

import (
"github.com/spf13/cobra"

"github.com/buildpacks/pack/pkg/client"
"github.com/buildpacks/pack/pkg/logging"
)

// ManifestAddFlags define flags provided to the ManifestAdd
type ManifestAddFlags struct {
ManifestAnnotateFlags
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: "pack manifest add [OPTIONS] <manifest-list> <manifest> [flags]",
Args: cobra.MatchAll(cobra.ExactArgs(2), cobra.OnlyValidArgs),
Short: "manifest add modifies a manifest list (Image index) and add a new image to the list of manifests.",
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.

When a manifest list exits locally, user can add a new image to the manifest list using this command`,
RunE: logError(logger, func(cmd *cobra.Command, args []string) error {
imageIndex := args[0]
manifests := args[1]
if err := validateManifestAddFlags(flags); err != nil {
return err
}

imageID, err := pack.AddManifest(cmd.Context(), imageIndex, manifests, client.ManifestAddOptions{

All: flags.all,
})

if err != nil {
return err
}

logger.Infof(imageID)
return nil
}),
}

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.arch, "arch", "", "Set the architecture")
cmd.Flags().StringVar(&flags.variant, "variant", "", "Set the architecture variant")

AddHelpFlag(cmd, "add")
return cmd
}

func validateManifestAddFlags(flags ManifestAddFlags) error {
return nil
}
103 changes: 103 additions & 0 deletions internal/commands/manifest_add_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package commands_test

import (
"bytes"
"testing"

"github.com/buildpacks/pack/internal/commands"
"github.com/buildpacks/pack/internal/commands/testmocks"
"github.com/buildpacks/pack/pkg/logging"
"github.com/golang/mock/gomock"
"github.com/heroku/color"
"github.com/sclevine/spec"
"github.com/sclevine/spec/report"
"github.com/spf13/cobra"
)

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)
})

when("#ManifestAdd", func() {
when("no flags specified", func() {

})
when("when --all flags passed", func() {

})
when("when --os flags passed", func() {

})
when("when --arch flags passed", func() {

})
when("when --variant flags passed", func() {

})
when("when --os-version flags passed", func() {

})
when("when --features flags passed", func() {

})
when("when --os-features flags passed", func() {

})
when("when --annotations flags passed", func() {

})
when("when multiple flags passed", func() {

})
when("when no args passed", func() {

})
when("when manifest list reference is incorrect", func() {

})
when("when manifest reference is incorrect", func() {

})
when("when manifest passed in-place of manifest list on first arg", func() {

})
when("when manifest list is passed on second arg", func() {

})
when("when manifest is passed on second arg with --all option", func() {

})
when("when manifest list in locally available", func() {

})
when("when manifest is not locally available", func() {

})
when("when manifest is locally available passed", func() {

})
when("when multiple manifests passed", func() {

})
})
}
43 changes: 43 additions & 0 deletions internal/commands/manifest_annotate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package commands

import (
"github.com/spf13/cobra"

"github.com/buildpacks/pack/pkg/logging"
)

// ManifestAnnotateFlags define flags provided to the ManifestAnnotate
type ManifestAnnotateFlags struct {
os, arch, variant, osVersion string
features, osFeatures, annotations []string
}

// ManifestAnnotate modifies a manifest list (Image index) and update the platform information for an image included in the manifest list.
func ManifestAnnotate(logger logging.Logger, pack PackClient) *cobra.Command {
var flags ManifestAnnotateFlags

cmd := &cobra.Command{
Use: "pack manifest annotate [OPTIONS] <manifest-list> <manifest> [<manifest>...] [flags]",
Args: cobra.MatchAll(cobra.ExactArgs(2), cobra.OnlyValidArgs),
Short: "manifest annotate modifies a manifest list (Image index) and update the platform information for an image included in the manifest list.",
Example: `pack manifest annotate cnbs/sample-package:hello-universe-multiarch \
cnbs/sample-package:hello-universe --arch amd64`,
Long: `manifest annotate modifies a manifest list (Image index) and update the platform information for an image included in the manifest list.

Sometimes a manifest list could reference an image that doesn't specify the architecture, The "annotate" command allows users to update those values before pushing the manifest list a registry`,
RunE: logError(logger, func(cmd *cobra.Command, args []string) error {
return nil
}),
}

cmd.Flags().StringVar(&flags.os, "os", "", "Set the architecture")
cmd.Flags().StringVar(&flags.arch, "arch", "", "Set the architecture")
cmd.Flags().StringVar(&flags.variant, "variant", "", "Set the architecture")
cmd.Flags().StringVar(&flags.osVersion, "os-version", "", "override the os `version` of the specified image")
cmd.Flags().StringSliceVar(&flags.features, "features", nil, "override the `features` of the specified image")
cmd.Flags().StringSliceVar(&flags.osFeatures, "os-features", nil, "override the os `features` of the specified image")
cmd.Flags().StringSliceVar(&flags.annotations, "annotations", nil, "set an `annotation` for the specified image")

AddHelpFlag(cmd, "annotate")
return cmd
}
100 changes: 100 additions & 0 deletions internal/commands/manifest_annotate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package commands_test

import (
"bytes"
"testing"

"github.com/buildpacks/pack/internal/commands"
"github.com/buildpacks/pack/internal/commands/testmocks"
"github.com/buildpacks/pack/pkg/logging"
"github.com/golang/mock/gomock"
"github.com/heroku/color"
"github.com/sclevine/spec"
"github.com/sclevine/spec/report"
"github.com/spf13/cobra"
)

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

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

func testManifestAnnotateCommand(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.ManifestAnnotate(logger, mockClient)
})

when("#ManifestAnnotate", func() {
when("no flags specified", func() {

})
when("when --os flags passed", func() {

})
when("when --arch flags passed", func() {

})
when("when --variant flags passed", func() {

})
when("when --os-version flags passed", func() {

})
when("when --features flags passed", func() {

})
when("when --os-features flags passed", func() {

})
when("when --annotations flags passed", func() {

})
when("when multiple flags passed", func() {

})
when("when no args passed", func() {

})
when("when manifest list reference is incorrect", func() {

})
when("when manifest reference is incorrect", func() {

})
when("when manifest passed in-place of manifest list on first arg", func() {

})
when("when manifest list is passed on second arg", func() {

})
when("when manifest is passed on second arg with --all option", func() {

})
when("when manifest list in locally available", func() {

})
when("when manifest is not locally available", func() {

})
when("when manifest is locally available passed", func() {

})
when("when multiple manifests passed", func() {

})
})
}
Loading
Loading