Merge pull request #15350 from nalind/manifest-amend

podman manifest create: accept --amend and --insecure flags
This commit is contained in:
OpenShift Merge Robot 2022-08-17 09:39:19 +00:00 committed by GitHub
commit a9131050cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 111 additions and 10 deletions

View File

@ -2,6 +2,7 @@ package manifest
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"github.com/containers/common/pkg/auth" "github.com/containers/common/pkg/auth"
@ -20,6 +21,7 @@ type manifestAddOptsWrapper struct {
entities.ManifestAddOptions entities.ManifestAddOptions
TLSVerifyCLI bool // CLI only TLSVerifyCLI bool // CLI only
Insecure bool // CLI only
CredentialsCLI string CredentialsCLI string
} }
@ -77,6 +79,8 @@ func init() {
flags.StringVar(&manifestAddOpts.OSVersion, osVersionFlagName, "", "override the OS `version` of the specified image") flags.StringVar(&manifestAddOpts.OSVersion, osVersionFlagName, "", "override the OS `version` of the specified image")
_ = addCmd.RegisterFlagCompletionFunc(osVersionFlagName, completion.AutocompleteNone) _ = 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") flags.BoolVar(&manifestAddOpts.TLSVerifyCLI, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry")
variantFlagName := "variant" variantFlagName := "variant"
@ -89,7 +93,7 @@ func init() {
} }
func add(cmd *cobra.Command, args []string) error { 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 return err
} }
@ -109,6 +113,12 @@ func add(cmd *cobra.Command, args []string) error {
if cmd.Flags().Changed("tls-verify") { if cmd.Flags().Changed("tls-verify") {
manifestAddOpts.SkipTLSVerify = types.NewOptionalBool(!manifestAddOpts.TLSVerifyCLI) 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) listID, err := registry.ImageEngine().ManifestAdd(context.Background(), args[0], args[1:], manifestAddOpts.ManifestAddOptions)
if err != nil { if err != nil {

View File

@ -1,16 +1,26 @@
package manifest package manifest
import ( import (
"errors"
"fmt" "fmt"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v4/cmd/podman/common" "github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/entities"
"github.com/spf13/cobra" "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 ( var (
manifestCreateOpts = entities.ManifestCreateOptions{} manifestCreateOpts = manifestCreateOptsWrapper{}
createCmd = &cobra.Command{ createCmd = &cobra.Command{
Use: "create [options] LIST [IMAGE...]", Use: "create [options] LIST [IMAGE...]",
Short: "Create manifest list or image index", Short: "Create manifest list or image index",
@ -32,10 +42,28 @@ func init() {
}) })
flags := createCmd.Flags() 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.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 { 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 { if err != nil {
return err return err
} }

View File

@ -1,6 +1,7 @@
package manifest package manifest
import ( import (
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -20,9 +21,9 @@ import (
type manifestPushOptsWrapper struct { type manifestPushOptsWrapper struct {
entities.ImagePushOptions entities.ImagePushOptions
TLSVerifyCLI bool // CLI only TLSVerifyCLI, Insecure bool // CLI only
CredentialsCLI string CredentialsCLI string
SignPassphraseFileCLI string SignPassphraseFileCLI string
} }
var ( var (
@ -82,6 +83,8 @@ func init() {
_ = pushCmd.RegisterFlagCompletionFunc(signPassphraseFileFlagName, completion.AutocompleteDefault) _ = pushCmd.RegisterFlagCompletionFunc(signPassphraseFileFlagName, completion.AutocompleteDefault)
flags.BoolVar(&manifestPushOpts.TLSVerifyCLI, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry") 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.BoolVarP(&manifestPushOpts.Quiet, "quiet", "q", false, "don't output progress information when pushing lists")
flags.SetNormalizeFunc(utils.AliasFlags) flags.SetNormalizeFunc(utils.AliasFlags)
@ -130,6 +133,12 @@ func push(cmd *cobra.Command, args []string) error {
if cmd.Flags().Changed("tls-verify") { if cmd.Flags().Changed("tls-verify") {
manifestPushOpts.SkipTLSVerify = types.NewOptionalBool(!manifestPushOpts.TLSVerifyCLI) 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) digest, err := registry.ImageEngine().ManifestPush(registry.Context(), args[0], args[1], manifestPushOpts.ImagePushOptions)
if err != nil { if err != nil {
return err 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 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. 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 ## EXAMPLES
``` ```
podman manifest create mylist:v1.11 podman manifest create mylist:v1.11
9cfd24048d5fc80903f088f1531a21bff01172abe66effa8941a4c2308dc745f 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"` Name string `schema:"name"`
Images []string `schema:"images"` Images []string `schema:"images"`
All bool `schema:"all"` All bool `schema:"all"`
Amend bool `schema:"amend"`
}{ }{
// Add defaults here once needed. // Add defaults here once needed.
} }
@ -70,7 +71,7 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) {
imageEngine := abi.ImageEngine{Libpod: runtime} 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) manID, err := imageEngine.ManifestCreate(r.Context(), query.Name, query.Images, createOptions)
if err != nil { if err != nil {
utils.InternalServerError(w, err) utils.InternalServerError(w, err)

View File

@ -117,6 +117,10 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// name: all // name: all
// type: boolean // type: boolean
// description: add all contents if given list // 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 // - in: body
// name: options // name: options
// description: options for new manifest // description: options for new manifest

View File

@ -8,7 +8,8 @@ type InspectOptions struct {
//go:generate go run ../generator/generator.go CreateOptions //go:generate go run ../generator/generator.go CreateOptions
// CreateOptions are optional options for creating manifests // CreateOptions are optional options for creating manifests
type CreateOptions struct { type CreateOptions struct {
All *bool All *bool
Amend *bool
} }
//go:generate go run ../generator/generator.go ExistsOptions //go:generate go run ../generator/generator.go ExistsOptions

View File

@ -31,3 +31,18 @@ func (o *CreateOptions) GetAll() bool {
} }
return *o.All 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 // ManifestCreateOptions provides model for creating manifest
type ManifestCreateOptions struct { type ManifestCreateOptions struct {
// True when adding lists to include all images
All bool `schema:"all"` 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 // 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) manifestList, err := ir.Libpod.LibimageRuntime().CreateManifestList(name)
if err != nil { 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} addOptions := &libimage.ManifestListAddOptions{All: opts.All}

View File

@ -15,7 +15,7 @@ import (
// ManifestCreate implements manifest create via ImageEngine // ManifestCreate implements manifest create via ImageEngine
func (ir *ImageEngine) ManifestCreate(ctx context.Context, name string, images []string, opts entities.ManifestCreateOptions) (string, error) { 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) imageID, err := manifests.Create(ir.ClientCtx, name, images, options)
if err != nil { if err != nil {
return imageID, fmt.Errorf("error creating manifest: %w", err) 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 := podmanTest.Podman([]string{"manifest", "create", "foo"})
session.WaitWithDefaultTimeout() session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0)) 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() { It("create w/ image", func() {