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 <nalin@redhat.com>
This commit is contained in:
Nalin Dahyabhai 2022-08-16 18:30:19 -04:00
parent 3aa92010df
commit 7e7a79b075
12 changed files with 111 additions and 10 deletions

View File

@ -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 {

View File

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

View File

@ -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

View File

@ -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
```
```

View File

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

View File

@ -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

View File

@ -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

View File

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

View File

@ -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

View File

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

View File

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

View File

@ -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() {