From 7e7a79b075f7d65657d95169f02c2c1c03198b93 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Tue, 16 Aug 2022 18:30:19 -0400 Subject: podman manifest create: accept --amend and --insecure flags Accept a --amend flag in `podman manifest create`, and treat `--insecure` as we would `--tls-verify=false` in `podman manifest`'s "add", "create", and "push" subcommands. Signed-off-by: Nalin Dahyabhai --- cmd/podman/manifest/add.go | 12 ++++++++- cmd/podman/manifest/create.go | 32 ++++++++++++++++++++++-- cmd/podman/manifest/push.go | 15 ++++++++--- docs/source/markdown/podman-manifest-create.1.md | 12 +++++++++ pkg/api/handlers/libpod/manifests.go | 3 ++- pkg/api/server/register_manifest.go | 4 +++ pkg/bindings/manifests/types.go | 3 ++- pkg/bindings/manifests/types_create_options.go | 15 +++++++++++ pkg/domain/entities/manifest.go | 5 ++++ pkg/domain/infra/abi/manifest.go | 10 +++++++- pkg/domain/infra/tunnel/manifest.go | 2 +- test/e2e/manifest_test.go | 8 ++++++ 12 files changed, 111 insertions(+), 10 deletions(-) diff --git a/cmd/podman/manifest/add.go b/cmd/podman/manifest/add.go index 35583ffcb..09a1a9a36 100644 --- a/cmd/podman/manifest/add.go +++ b/cmd/podman/manifest/add.go @@ -2,6 +2,7 @@ package manifest import ( "context" + "errors" "fmt" "github.com/containers/common/pkg/auth" @@ -20,6 +21,7 @@ type manifestAddOptsWrapper struct { entities.ManifestAddOptions TLSVerifyCLI bool // CLI only + Insecure bool // CLI only CredentialsCLI string } @@ -77,6 +79,8 @@ func init() { flags.StringVar(&manifestAddOpts.OSVersion, osVersionFlagName, "", "override the OS `version` of the specified image") _ = addCmd.RegisterFlagCompletionFunc(osVersionFlagName, completion.AutocompleteNone) + flags.BoolVar(&manifestAddOpts.Insecure, "insecure", false, "neither require HTTPS nor verify certificates when accessing the registry") + _ = flags.MarkHidden("insecure") flags.BoolVar(&manifestAddOpts.TLSVerifyCLI, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry") variantFlagName := "variant" @@ -89,7 +93,7 @@ func init() { } func add(cmd *cobra.Command, args []string) error { - if err := auth.CheckAuthFile(manifestPushOpts.Authfile); err != nil { + if err := auth.CheckAuthFile(manifestAddOpts.Authfile); err != nil { return err } @@ -109,6 +113,12 @@ func add(cmd *cobra.Command, args []string) error { if cmd.Flags().Changed("tls-verify") { manifestAddOpts.SkipTLSVerify = types.NewOptionalBool(!manifestAddOpts.TLSVerifyCLI) } + if cmd.Flags().Changed("insecure") { + if manifestAddOpts.SkipTLSVerify != types.OptionalBoolUndefined { + return errors.New("--insecure may not be used with --tls-verify") + } + manifestAddOpts.SkipTLSVerify = types.NewOptionalBool(manifestAddOpts.Insecure) + } listID, err := registry.ImageEngine().ManifestAdd(context.Background(), args[0], args[1:], manifestAddOpts.ManifestAddOptions) if err != nil { diff --git a/cmd/podman/manifest/create.go b/cmd/podman/manifest/create.go index 435b4a57c..0a0ea1d88 100644 --- a/cmd/podman/manifest/create.go +++ b/cmd/podman/manifest/create.go @@ -1,16 +1,26 @@ package manifest import ( + "errors" "fmt" + "github.com/containers/image/v5/types" "github.com/containers/podman/v4/cmd/podman/common" "github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/spf13/cobra" ) +// manifestCreateOptsWrapper wraps entities.ManifestCreateOptions and prevents leaking +// CLI-only fields into the API types. +type manifestCreateOptsWrapper struct { + entities.ManifestCreateOptions + + TLSVerifyCLI, Insecure bool // CLI only +} + var ( - manifestCreateOpts = entities.ManifestCreateOptions{} + manifestCreateOpts = manifestCreateOptsWrapper{} createCmd = &cobra.Command{ Use: "create [options] LIST [IMAGE...]", Short: "Create manifest list or image index", @@ -32,10 +42,28 @@ func init() { }) flags := createCmd.Flags() flags.BoolVar(&manifestCreateOpts.All, "all", false, "add all of the lists' images if the images to add are lists") + flags.BoolVar(&manifestCreateOpts.Amend, "amend", false, "modify an existing list if one with the desired name already exists") + flags.BoolVar(&manifestCreateOpts.Insecure, "insecure", false, "neither require HTTPS nor verify certificates when accessing the registry") + _ = flags.MarkHidden("insecure") + flags.BoolVar(&manifestCreateOpts.TLSVerifyCLI, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry") } func create(cmd *cobra.Command, args []string) error { - imageID, err := registry.ImageEngine().ManifestCreate(registry.Context(), args[0], args[1:], manifestCreateOpts) + // TLS verification in c/image is controlled via a `types.OptionalBool` + // which allows for distinguishing among set-true, set-false, unspecified + // which is important to implement a sane way of dealing with defaults of + // boolean CLI flags. + if cmd.Flags().Changed("tls-verify") { + manifestCreateOpts.SkipTLSVerify = types.NewOptionalBool(!manifestCreateOpts.TLSVerifyCLI) + } + if cmd.Flags().Changed("insecure") { + if manifestCreateOpts.SkipTLSVerify != types.OptionalBoolUndefined { + return errors.New("--insecure may not be used with --tls-verify") + } + manifestCreateOpts.SkipTLSVerify = types.NewOptionalBool(manifestCreateOpts.Insecure) + } + + imageID, err := registry.ImageEngine().ManifestCreate(registry.Context(), args[0], args[1:], manifestCreateOpts.ManifestCreateOptions) if err != nil { return err } diff --git a/cmd/podman/manifest/push.go b/cmd/podman/manifest/push.go index 756ed2a74..fd67769b8 100644 --- a/cmd/podman/manifest/push.go +++ b/cmd/podman/manifest/push.go @@ -1,6 +1,7 @@ package manifest import ( + "errors" "fmt" "io/ioutil" @@ -20,9 +21,9 @@ import ( type manifestPushOptsWrapper struct { entities.ImagePushOptions - TLSVerifyCLI bool // CLI only - CredentialsCLI string - SignPassphraseFileCLI string + TLSVerifyCLI, Insecure bool // CLI only + CredentialsCLI string + SignPassphraseFileCLI string } var ( @@ -82,6 +83,8 @@ func init() { _ = pushCmd.RegisterFlagCompletionFunc(signPassphraseFileFlagName, completion.AutocompleteDefault) flags.BoolVar(&manifestPushOpts.TLSVerifyCLI, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry") + flags.BoolVar(&manifestPushOpts.Insecure, "insecure", false, "neither require HTTPS nor verify certificates when accessing the registry") + _ = flags.MarkHidden("insecure") flags.BoolVarP(&manifestPushOpts.Quiet, "quiet", "q", false, "don't output progress information when pushing lists") flags.SetNormalizeFunc(utils.AliasFlags) @@ -130,6 +133,12 @@ func push(cmd *cobra.Command, args []string) error { if cmd.Flags().Changed("tls-verify") { manifestPushOpts.SkipTLSVerify = types.NewOptionalBool(!manifestPushOpts.TLSVerifyCLI) } + if cmd.Flags().Changed("insecure") { + if manifestPushOpts.SkipTLSVerify != types.OptionalBoolUndefined { + return errors.New("--insecure may not be used with --tls-verify") + } + manifestPushOpts.SkipTLSVerify = types.NewOptionalBool(manifestPushOpts.Insecure) + } digest, err := registry.ImageEngine().ManifestPush(registry.Context(), args[0], args[1], manifestPushOpts.ImagePushOptions) if err != nil { return err diff --git a/docs/source/markdown/podman-manifest-create.1.md b/docs/source/markdown/podman-manifest-create.1.md index 77a4b9db6..f2aac6069 100644 --- a/docs/source/markdown/podman-manifest-create.1.md +++ b/docs/source/markdown/podman-manifest-create.1.md @@ -22,11 +22,23 @@ If any of the images which should be added to the new list or index are themselves lists or indexes, add all of their contents. By default, only one image from such a list will be added to the newly-created list or index. +#### **--amend** + +If a manifest list named *listnameorindexname* already exists, modify the +preexisting list instead of exiting with an error. The contents of +*listnameorindexname* are not modified if no *imagename*s are given. + +#### **--tls-verify** + +Require HTTPS and verify certificates when talking to container registries. (defaults to true) + ## EXAMPLES ``` podman manifest create mylist:v1.11 9cfd24048d5fc80903f088f1531a21bff01172abe66effa8941a4c2308dc745f +podman manifest create --amend mylist:v1.11 +9cfd24048d5fc80903f088f1531a21bff01172abe66effa8941a4c2308dc745f ``` ``` diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go index b0c93f3b9..fa83bbfe1 100644 --- a/pkg/api/handlers/libpod/manifests.go +++ b/pkg/api/handlers/libpod/manifests.go @@ -36,6 +36,7 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) { Name string `schema:"name"` Images []string `schema:"images"` All bool `schema:"all"` + Amend bool `schema:"amend"` }{ // Add defaults here once needed. } @@ -70,7 +71,7 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) { imageEngine := abi.ImageEngine{Libpod: runtime} - createOptions := entities.ManifestCreateOptions{All: query.All} + createOptions := entities.ManifestCreateOptions{All: query.All, Amend: query.Amend} manID, err := imageEngine.ManifestCreate(r.Context(), query.Name, query.Images, createOptions) if err != nil { utils.InternalServerError(w, err) diff --git a/pkg/api/server/register_manifest.go b/pkg/api/server/register_manifest.go index c22479cf9..7a55eaefe 100644 --- a/pkg/api/server/register_manifest.go +++ b/pkg/api/server/register_manifest.go @@ -117,6 +117,10 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error { // name: all // type: boolean // description: add all contents if given list + // - in: query + // name: amend + // type: boolean + // description: modify an existing list if one with the desired name already exists // - in: body // name: options // description: options for new manifest diff --git a/pkg/bindings/manifests/types.go b/pkg/bindings/manifests/types.go index e23ef798d..5f2557fe1 100644 --- a/pkg/bindings/manifests/types.go +++ b/pkg/bindings/manifests/types.go @@ -8,7 +8,8 @@ type InspectOptions struct { //go:generate go run ../generator/generator.go CreateOptions // CreateOptions are optional options for creating manifests type CreateOptions struct { - All *bool + All *bool + Amend *bool } //go:generate go run ../generator/generator.go ExistsOptions diff --git a/pkg/bindings/manifests/types_create_options.go b/pkg/bindings/manifests/types_create_options.go index 960332a82..09942c00a 100644 --- a/pkg/bindings/manifests/types_create_options.go +++ b/pkg/bindings/manifests/types_create_options.go @@ -31,3 +31,18 @@ func (o *CreateOptions) GetAll() bool { } return *o.All } + +// WithAmend set field Amend to given value +func (o *CreateOptions) WithAmend(value bool) *CreateOptions { + o.Amend = &value + return o +} + +// GetAmend returns value of field Amend +func (o *CreateOptions) GetAmend() bool { + if o.Amend == nil { + var z bool + return z + } + return *o.Amend +} diff --git a/pkg/domain/entities/manifest.go b/pkg/domain/entities/manifest.go index 126b76c62..f17079271 100644 --- a/pkg/domain/entities/manifest.go +++ b/pkg/domain/entities/manifest.go @@ -4,7 +4,12 @@ import "github.com/containers/image/v5/types" // ManifestCreateOptions provides model for creating manifest type ManifestCreateOptions struct { + // True when adding lists to include all images All bool `schema:"all"` + // Amend an extant list if there's already one with the desired name + Amend bool `schema:"amend"` + // Should TLS registry certificate be verified? + SkipTLSVerify types.OptionalBool `json:"-" schema:"-"` } // ManifestAddOptions provides model for adding digests to manifest list diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index e0c11267e..7e8c86526 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -32,7 +32,15 @@ func (ir *ImageEngine) ManifestCreate(ctx context.Context, name string, images [ manifestList, err := ir.Libpod.LibimageRuntime().CreateManifestList(name) if err != nil { - return "", err + if errors.Is(err, storage.ErrDuplicateName) && opts.Amend { + amendList, amendErr := ir.Libpod.LibimageRuntime().LookupManifestList(name) + if amendErr != nil { + return "", err + } + manifestList = amendList + } else { + return "", err + } } addOptions := &libimage.ManifestListAddOptions{All: opts.All} diff --git a/pkg/domain/infra/tunnel/manifest.go b/pkg/domain/infra/tunnel/manifest.go index 2a514861d..2e6134051 100644 --- a/pkg/domain/infra/tunnel/manifest.go +++ b/pkg/domain/infra/tunnel/manifest.go @@ -15,7 +15,7 @@ import ( // ManifestCreate implements manifest create via ImageEngine func (ir *ImageEngine) ManifestCreate(ctx context.Context, name string, images []string, opts entities.ManifestCreateOptions) (string, error) { - options := new(manifests.CreateOptions).WithAll(opts.All) + options := new(manifests.CreateOptions).WithAll(opts.All).WithAmend(opts.Amend) imageID, err := manifests.Create(ir.ClientCtx, name, images, options) if err != nil { return imageID, fmt.Errorf("error creating manifest: %w", err) diff --git a/test/e2e/manifest_test.go b/test/e2e/manifest_test.go index ee954a1a4..145a016ea 100644 --- a/test/e2e/manifest_test.go +++ b/test/e2e/manifest_test.go @@ -49,6 +49,14 @@ var _ = Describe("Podman manifest", func() { session := podmanTest.Podman([]string{"manifest", "create", "foo"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"manifest", "create", "foo"}) + session.WaitWithDefaultTimeout() + Expect(session).To(ExitWithError()) + + session = podmanTest.Podman([]string{"manifest", "create", "--amend", "foo"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) }) It("create w/ image", func() { -- cgit v1.2.3-54-g00ecf