mirror of
https://github.com/containers/podman
synced 2024-10-19 08:44:11 +00:00
Switch podman image push handlers to use abi
Change API Handlers to use the same functions that the local podman uses. At the same time: Cleanup and pass proper bindings. Remove cli options from podman-remote push. Cleanup manifest push. Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
parent
179b9d1745
commit
84f7bdc4db
|
@ -114,7 +114,11 @@ func pushFlags(cmd *cobra.Command) {
|
|||
if registry.IsRemote() {
|
||||
_ = flags.MarkHidden("cert-dir")
|
||||
_ = flags.MarkHidden("compress")
|
||||
_ = flags.MarkHidden("digestfile")
|
||||
_ = flags.MarkHidden("format")
|
||||
_ = flags.MarkHidden("quiet")
|
||||
_ = flags.MarkHidden("remove-signatures")
|
||||
_ = flags.MarkHidden("sign-by")
|
||||
}
|
||||
_ = flags.MarkHidden("signature-policy")
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ solely for scripting compatibility.
|
|||
#### **--format**, **-f**=*format*
|
||||
|
||||
Manifest Type (oci, v2s1, or v2s2) to use when pushing an image to a directory using the 'dir:' transport (default is manifest type of source)
|
||||
Note: This flag can only be set when using the **dir** transport
|
||||
Note: This flag can only be set when using the **dir** transport. (Not available for remote commands)
|
||||
|
||||
#### **--quiet**, **-q**
|
||||
|
||||
|
@ -99,11 +99,11 @@ When writing the output image, suppress progress output
|
|||
|
||||
#### **--remove-signatures**
|
||||
|
||||
Discard any pre-existing signatures in the image
|
||||
Discard any pre-existing signatures in the image. (Not available for remote commands)
|
||||
|
||||
#### **--sign-by**=*key*
|
||||
|
||||
Add a signature at the destination using the specified key
|
||||
Add a signature at the destination using the specified key. (Not available for remote commands)
|
||||
|
||||
#### **--tls-verify**=*true|false*
|
||||
|
||||
|
|
|
@ -3,13 +3,14 @@ package compat
|
|||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/podman/v2/libpod"
|
||||
"github.com/containers/podman/v2/libpod/image"
|
||||
"github.com/containers/podman/v2/pkg/api/handlers/utils"
|
||||
"github.com/containers/podman/v2/pkg/auth"
|
||||
"github.com/containers/podman/v2/pkg/domain/entities"
|
||||
"github.com/containers/podman/v2/pkg/domain/infra/abi"
|
||||
"github.com/containers/storage"
|
||||
"github.com/gorilla/schema"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
@ -18,11 +19,19 @@ import (
|
|||
func PushImage(w http.ResponseWriter, r *http.Request) {
|
||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||
// Now use the ABI implementation to prevent us from having duplicate
|
||||
// code.
|
||||
imageEngine := abi.ImageEngine{Libpod: runtime}
|
||||
|
||||
query := struct {
|
||||
All bool `schema:"all"`
|
||||
Compress bool `schema:"compress"`
|
||||
Destination string `schema:"destination"`
|
||||
Tag string `schema:"tag"`
|
||||
TLSVerify bool `schema:"tlsVerify"`
|
||||
}{
|
||||
// This is where you can override the golang default value for one of fields
|
||||
TLSVerify: true,
|
||||
}
|
||||
|
||||
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
||||
|
@ -43,39 +52,30 @@ func PushImage(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
newImage, err := runtime.ImageRuntime().NewFromLocal(imageName)
|
||||
if err != nil {
|
||||
utils.ImageNotFound(w, imageName, errors.Wrapf(err, "failed to find image %s", imageName))
|
||||
return
|
||||
}
|
||||
|
||||
authConf, authfile, key, err := auth.GetCredentials(r)
|
||||
authconf, authfile, key, err := auth.GetCredentials(r)
|
||||
if err != nil {
|
||||
utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse %q header for %s", key, r.URL.String()))
|
||||
return
|
||||
}
|
||||
defer auth.RemoveAuthfile(authfile)
|
||||
|
||||
dockerRegistryOptions := &image.DockerRegistryOptions{DockerRegistryCreds: authConf}
|
||||
if sys := runtime.SystemContext(); sys != nil {
|
||||
dockerRegistryOptions.DockerCertPath = sys.DockerCertPath
|
||||
dockerRegistryOptions.RegistriesConfPath = sys.SystemRegistriesConfPath
|
||||
var username, password string
|
||||
if authconf != nil {
|
||||
username = authconf.Username
|
||||
password = authconf.Password
|
||||
}
|
||||
options := entities.ImagePushOptions{
|
||||
All: query.All,
|
||||
Authfile: authfile,
|
||||
Compress: query.Compress,
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
if err := imageEngine.Push(context.Background(), imageName, query.Destination, options); err != nil {
|
||||
if errors.Cause(err) != storage.ErrImageUnknown {
|
||||
utils.ImageNotFound(w, imageName, errors.Wrapf(err, "failed to find image %s", imageName))
|
||||
return
|
||||
}
|
||||
|
||||
err = newImage.PushImageToHeuristicDestination(
|
||||
context.Background(),
|
||||
imageName,
|
||||
"", // manifest type
|
||||
authfile,
|
||||
"", // digest file
|
||||
"", // signature policy
|
||||
os.Stderr,
|
||||
false, // force compression
|
||||
image.SigningOptions{},
|
||||
dockerRegistryOptions,
|
||||
nil, // additional tags
|
||||
)
|
||||
if err != nil {
|
||||
utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "error pushing image %q", imageName))
|
||||
return
|
||||
}
|
||||
|
|
|
@ -147,7 +147,6 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) {
|
|||
query := struct {
|
||||
All bool `schema:"all"`
|
||||
Destination string `schema:"destination"`
|
||||
Format string `schema:"format"`
|
||||
TLSVerify bool `schema:"tlsVerify"`
|
||||
}{
|
||||
// Add defaults here once needed.
|
||||
|
@ -163,24 +162,21 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
source := utils.GetName(r)
|
||||
authConf, authfile, key, err := auth.GetCredentials(r)
|
||||
authconf, authfile, key, err := auth.GetCredentials(r)
|
||||
if err != nil {
|
||||
utils.Error(w, "failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "failed to parse %q header for %s", key, r.URL.String()))
|
||||
return
|
||||
}
|
||||
defer auth.RemoveAuthfile(authfile)
|
||||
var username, password string
|
||||
if authConf != nil {
|
||||
username = authConf.Username
|
||||
password = authConf.Password
|
||||
|
||||
if authconf != nil {
|
||||
username = authconf.Username
|
||||
password = authconf.Password
|
||||
}
|
||||
|
||||
options := entities.ImagePushOptions{
|
||||
Authfile: authfile,
|
||||
Username: username,
|
||||
Password: password,
|
||||
Format: query.Format,
|
||||
All: query.All,
|
||||
}
|
||||
if sys := runtime.SystemContext(); sys != nil {
|
||||
|
|
|
@ -235,6 +235,18 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
|
|||
// name: tag
|
||||
// type: string
|
||||
// description: The tag to associate with the image on the registry.
|
||||
// - in: query
|
||||
// name: all
|
||||
// type: boolean
|
||||
// description: All indicates whether to push all images related to the image list
|
||||
// - in: query
|
||||
// name: compress
|
||||
// type: boolean
|
||||
// description: use compression on image
|
||||
// - in: query
|
||||
// name: destination
|
||||
// type: string
|
||||
// description: destination name for the image being pushed
|
||||
// - in: header
|
||||
// name: X-Registry-Auth
|
||||
// type: string
|
||||
|
|
|
@ -104,37 +104,14 @@ type PushOptions struct {
|
|||
// Authfile is the path to the authentication file. Ignored for remote
|
||||
// calls.
|
||||
Authfile *string
|
||||
// CertDir is the path to certificate directories. Ignored for remote
|
||||
// calls.
|
||||
CertDir *string
|
||||
// Compress tarball image layers when pushing to a directory using the 'dir'
|
||||
// transport. Default is same compression type as source. Ignored for remote
|
||||
// calls.
|
||||
// Compress tarball image layers when pushing to a directory using the 'dir' transport.
|
||||
Compress *bool
|
||||
// Username for authenticating against the registry.
|
||||
Username *string
|
||||
// Password for authenticating against the registry.
|
||||
Password *string
|
||||
// DigestFile, after copying the image, write the digest of the resulting
|
||||
// image to the file. Ignored for remote calls.
|
||||
DigestFile *string
|
||||
// Format is the Manifest type (oci, v2s1, or v2s2) to use when pushing an
|
||||
// image using the 'dir' transport. Default is manifest type of source.
|
||||
// Ignored for remote calls.
|
||||
Format *string
|
||||
// Quiet can be specified to suppress pull progress when pulling. Ignored
|
||||
// for remote calls.
|
||||
Quiet *bool
|
||||
// RemoveSignatures, discard any pre-existing signatures in the image.
|
||||
// Ignored for remote calls.
|
||||
RemoveSignatures *bool
|
||||
// SignaturePolicy to use when pulling. Ignored for remote calls.
|
||||
SignaturePolicy *string
|
||||
// SignBy adds a signature at the destination using the specified key.
|
||||
// Ignored for remote calls.
|
||||
SignBy *string
|
||||
// SkipTLSVerify to skip HTTPS and certificate verification.
|
||||
SkipTLSVerify *bool
|
||||
// Username for authenticating against the registry.
|
||||
Username *string
|
||||
}
|
||||
|
||||
//go:generate go run ../generator/generator.go SearchOptions
|
||||
|
|
|
@ -119,22 +119,6 @@ func (o *PushOptions) GetAuthfile() string {
|
|||
return *o.Authfile
|
||||
}
|
||||
|
||||
// WithCertDir
|
||||
func (o *PushOptions) WithCertDir(value string) *PushOptions {
|
||||
v := &value
|
||||
o.CertDir = v
|
||||
return o
|
||||
}
|
||||
|
||||
// GetCertDir
|
||||
func (o *PushOptions) GetCertDir() string {
|
||||
var certDir string
|
||||
if o.CertDir == nil {
|
||||
return certDir
|
||||
}
|
||||
return *o.CertDir
|
||||
}
|
||||
|
||||
// WithCompress
|
||||
func (o *PushOptions) WithCompress(value bool) *PushOptions {
|
||||
v := &value
|
||||
|
@ -151,22 +135,6 @@ func (o *PushOptions) GetCompress() bool {
|
|||
return *o.Compress
|
||||
}
|
||||
|
||||
// WithUsername
|
||||
func (o *PushOptions) WithUsername(value string) *PushOptions {
|
||||
v := &value
|
||||
o.Username = v
|
||||
return o
|
||||
}
|
||||
|
||||
// GetUsername
|
||||
func (o *PushOptions) GetUsername() string {
|
||||
var username string
|
||||
if o.Username == nil {
|
||||
return username
|
||||
}
|
||||
return *o.Username
|
||||
}
|
||||
|
||||
// WithPassword
|
||||
func (o *PushOptions) WithPassword(value string) *PushOptions {
|
||||
v := &value
|
||||
|
@ -183,102 +151,6 @@ func (o *PushOptions) GetPassword() string {
|
|||
return *o.Password
|
||||
}
|
||||
|
||||
// WithDigestFile
|
||||
func (o *PushOptions) WithDigestFile(value string) *PushOptions {
|
||||
v := &value
|
||||
o.DigestFile = v
|
||||
return o
|
||||
}
|
||||
|
||||
// GetDigestFile
|
||||
func (o *PushOptions) GetDigestFile() string {
|
||||
var digestFile string
|
||||
if o.DigestFile == nil {
|
||||
return digestFile
|
||||
}
|
||||
return *o.DigestFile
|
||||
}
|
||||
|
||||
// WithFormat
|
||||
func (o *PushOptions) WithFormat(value string) *PushOptions {
|
||||
v := &value
|
||||
o.Format = v
|
||||
return o
|
||||
}
|
||||
|
||||
// GetFormat
|
||||
func (o *PushOptions) GetFormat() string {
|
||||
var format string
|
||||
if o.Format == nil {
|
||||
return format
|
||||
}
|
||||
return *o.Format
|
||||
}
|
||||
|
||||
// WithQuiet
|
||||
func (o *PushOptions) WithQuiet(value bool) *PushOptions {
|
||||
v := &value
|
||||
o.Quiet = v
|
||||
return o
|
||||
}
|
||||
|
||||
// GetQuiet
|
||||
func (o *PushOptions) GetQuiet() bool {
|
||||
var quiet bool
|
||||
if o.Quiet == nil {
|
||||
return quiet
|
||||
}
|
||||
return *o.Quiet
|
||||
}
|
||||
|
||||
// WithRemoveSignatures
|
||||
func (o *PushOptions) WithRemoveSignatures(value bool) *PushOptions {
|
||||
v := &value
|
||||
o.RemoveSignatures = v
|
||||
return o
|
||||
}
|
||||
|
||||
// GetRemoveSignatures
|
||||
func (o *PushOptions) GetRemoveSignatures() bool {
|
||||
var removeSignatures bool
|
||||
if o.RemoveSignatures == nil {
|
||||
return removeSignatures
|
||||
}
|
||||
return *o.RemoveSignatures
|
||||
}
|
||||
|
||||
// WithSignaturePolicy
|
||||
func (o *PushOptions) WithSignaturePolicy(value string) *PushOptions {
|
||||
v := &value
|
||||
o.SignaturePolicy = v
|
||||
return o
|
||||
}
|
||||
|
||||
// GetSignaturePolicy
|
||||
func (o *PushOptions) GetSignaturePolicy() string {
|
||||
var signaturePolicy string
|
||||
if o.SignaturePolicy == nil {
|
||||
return signaturePolicy
|
||||
}
|
||||
return *o.SignaturePolicy
|
||||
}
|
||||
|
||||
// WithSignBy
|
||||
func (o *PushOptions) WithSignBy(value string) *PushOptions {
|
||||
v := &value
|
||||
o.SignBy = v
|
||||
return o
|
||||
}
|
||||
|
||||
// GetSignBy
|
||||
func (o *PushOptions) GetSignBy() string {
|
||||
var signBy string
|
||||
if o.SignBy == nil {
|
||||
return signBy
|
||||
}
|
||||
return *o.SignBy
|
||||
}
|
||||
|
||||
// WithSkipTLSVerify
|
||||
func (o *PushOptions) WithSkipTLSVerify(value bool) *PushOptions {
|
||||
v := &value
|
||||
|
@ -294,3 +166,19 @@ func (o *PushOptions) GetSkipTLSVerify() bool {
|
|||
}
|
||||
return *o.SkipTLSVerify
|
||||
}
|
||||
|
||||
// WithUsername
|
||||
func (o *PushOptions) WithUsername(value string) *PushOptions {
|
||||
v := &value
|
||||
o.Username = v
|
||||
return o
|
||||
}
|
||||
|
||||
// GetUsername
|
||||
func (o *PushOptions) GetUsername() string {
|
||||
var username string
|
||||
if o.Username == nil {
|
||||
return username
|
||||
}
|
||||
return *o.Username
|
||||
}
|
||||
|
|
|
@ -153,7 +153,6 @@ func Push(ctx context.Context, name, destination string, options *images.PushOpt
|
|||
}
|
||||
params.Set("image", name)
|
||||
params.Set("destination", destination)
|
||||
params.Set("format", *options.Format)
|
||||
_, err = conn.DoRequest(nil, http.MethodPost, "/manifests/%s/push", params, nil, name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
|
@ -236,10 +236,7 @@ func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOpti
|
|||
|
||||
func (ir *ImageEngine) Push(ctx context.Context, source string, destination string, opts entities.ImagePushOptions) error {
|
||||
options := new(images.PushOptions)
|
||||
options.WithUsername(opts.Username).WithSignaturePolicy(opts.SignaturePolicy).WithQuiet(opts.Quiet)
|
||||
options.WithPassword(opts.Password).WithCertDir(opts.CertDir).WithAuthfile(opts.Authfile)
|
||||
options.WithCompress(opts.Compress).WithDigestFile(opts.DigestFile).WithFormat(opts.Format)
|
||||
options.WithRemoveSignatures(opts.RemoveSignatures).WithSignBy(opts.SignBy)
|
||||
options.WithAll(opts.All).WithCompress(opts.Compress).WithUsername(opts.Username).WithPassword(opts.Password).WithAuthfile(opts.Authfile)
|
||||
|
||||
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
|
||||
if s == types.OptionalBoolTrue {
|
||||
|
|
|
@ -86,10 +86,8 @@ func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (stri
|
|||
// ManifestPush pushes a manifest list or image index to the destination
|
||||
func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination string, opts entities.ImagePushOptions) (string, error) {
|
||||
options := new(images.PushOptions)
|
||||
options.WithUsername(opts.Username).WithSignaturePolicy(opts.SignaturePolicy).WithQuiet(opts.Quiet)
|
||||
options.WithPassword(opts.Password).WithCertDir(opts.CertDir).WithAuthfile(opts.Authfile)
|
||||
options.WithCompress(opts.Compress).WithDigestFile(opts.DigestFile).WithFormat(opts.Format)
|
||||
options.WithRemoveSignatures(opts.RemoveSignatures).WithSignBy(opts.SignBy)
|
||||
options.WithUsername(opts.Username).WithPassword(opts.Password).WithAuthfile(opts.Authfile)
|
||||
options.WithAll(opts.All)
|
||||
|
||||
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
|
||||
if s == types.OptionalBoolTrue {
|
||||
|
|
|
@ -54,10 +54,16 @@ var _ = Describe("Podman push", func() {
|
|||
fmt.Sprintf("dir:%s", bbdir)})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
|
||||
bbdir = filepath.Join(podmanTest.TempDir, "busybox")
|
||||
session = podmanTest.Podman([]string{"push", "--format", "oci", ALPINE,
|
||||
fmt.Sprintf("dir:%s", bbdir)})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
})
|
||||
|
||||
It("podman push to local registry", func() {
|
||||
SkipIfRemote("FIXME: This should work")
|
||||
SkipIfRemote("Remote does not support --digestfile or --remove-sginatures")
|
||||
if podmanTest.Host.Arch == "ppc64le" {
|
||||
Skip("No registry image for ppc64le")
|
||||
}
|
||||
|
@ -74,7 +80,7 @@ var _ = Describe("Podman push", func() {
|
|||
Skip("Cannot start docker registry.")
|
||||
}
|
||||
|
||||
push := podmanTest.Podman([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"})
|
||||
push := podmanTest.Podman([]string{"push", "-q", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"})
|
||||
push.WaitWithDefaultTimeout()
|
||||
Expect(push.ExitCode()).To(Equal(0))
|
||||
|
||||
|
@ -88,7 +94,6 @@ var _ = Describe("Podman push", func() {
|
|||
})
|
||||
|
||||
It("podman push to local registry with authorization", func() {
|
||||
SkipIfRemote("FIXME: This does not seem to be returning an error")
|
||||
SkipIfRootless("FIXME: Creating content in certs.d we use directories in homedir")
|
||||
if podmanTest.Host.Arch == "ppc64le" {
|
||||
Skip("No registry image for ppc64le")
|
||||
|
@ -155,9 +160,12 @@ var _ = Describe("Podman push", func() {
|
|||
push.WaitWithDefaultTimeout()
|
||||
Expect(push).To(ExitWithError())
|
||||
|
||||
if !IsRemote() {
|
||||
// remote does not support --cert-dir
|
||||
push = podmanTest.Podman([]string{"push", "--creds=podmantest:test", "--cert-dir=fakedir", ALPINE, "localhost:5000/certdirtest"})
|
||||
push.WaitWithDefaultTimeout()
|
||||
Expect(push).To(ExitWithError())
|
||||
}
|
||||
|
||||
push = podmanTest.Podman([]string{"push", "--creds=podmantest:test", ALPINE, "localhost:5000/defaultflags"})
|
||||
push.WaitWithDefaultTimeout()
|
||||
|
|
|
@ -197,6 +197,7 @@ EOF
|
|||
destname=ok-$(random_string 10 | tr A-Z a-z)-ok
|
||||
# Use command-line credentials
|
||||
run_podman push --tls-verify=false \
|
||||
--format docker \
|
||||
--creds ${PODMAN_LOGIN_USER}:${PODMAN_LOGIN_PASS} \
|
||||
$IMAGE localhost:${PODMAN_LOGIN_REGISTRY_PORT}/$destname
|
||||
|
||||
|
|
Loading…
Reference in a new issue