mirror of
https://github.com/containers/podman.git
synced 2025-06-22 18:08:11 +08:00
Merge pull request #3863 from TomSweeneyRedHat/dev/tsweeney/digestfile2
Add --digestfile option to push
This commit is contained in:
@ -423,6 +423,7 @@ type PushValues struct {
|
|||||||
CertDir string
|
CertDir string
|
||||||
Compress bool
|
Compress bool
|
||||||
Creds string
|
Creds string
|
||||||
|
Digestfile string
|
||||||
Format string
|
Format string
|
||||||
Quiet bool
|
Quiet bool
|
||||||
RemoveSignatures bool
|
RemoveSignatures bool
|
||||||
|
@ -51,6 +51,7 @@ func init() {
|
|||||||
pushCommand.SetUsageTemplate(UsageTemplate())
|
pushCommand.SetUsageTemplate(UsageTemplate())
|
||||||
flags := pushCommand.Flags()
|
flags := pushCommand.Flags()
|
||||||
flags.StringVar(&pushCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
|
flags.StringVar(&pushCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
|
||||||
|
flags.StringVar(&pushCommand.Digestfile, "digestfile", "", "After copying the image, write the digest of the resulting image to the file")
|
||||||
flags.StringVarP(&pushCommand.Format, "format", "f", "", "Manifest type (oci, v2s1, or v2s2) to use when pushing an image using the 'dir:' transport (default is manifest type of source)")
|
flags.StringVarP(&pushCommand.Format, "format", "f", "", "Manifest type (oci, v2s1, or v2s2) to use when pushing an image using the 'dir:' transport (default is manifest type of source)")
|
||||||
flags.BoolVarP(&pushCommand.Quiet, "quiet", "q", false, "Don't output progress information when pushing images")
|
flags.BoolVarP(&pushCommand.Quiet, "quiet", "q", false, "Don't output progress information when pushing images")
|
||||||
flags.BoolVar(&pushCommand.RemoveSignatures, "remove-signatures", false, "Discard any pre-existing signatures in the image")
|
flags.BoolVar(&pushCommand.RemoveSignatures, "remove-signatures", false, "Discard any pre-existing signatures in the image")
|
||||||
@ -143,5 +144,5 @@ func pushCmd(c *cliconfig.PushValues) error {
|
|||||||
SignBy: signBy,
|
SignBy: signBy,
|
||||||
}
|
}
|
||||||
|
|
||||||
return runtime.Push(getContext(), srcName, destName, manifestType, c.Authfile, c.SignaturePolicy, writer, c.Compress, so, &dockerRegistryOptions, nil)
|
return runtime.Push(getContext(), srcName, destName, manifestType, c.Authfile, c.String("digestfile"), c.SignaturePolicy, writer, c.Compress, so, &dockerRegistryOptions, nil)
|
||||||
}
|
}
|
||||||
|
@ -1758,6 +1758,7 @@ _podman_mount() {
|
|||||||
_podman_push() {
|
_podman_push() {
|
||||||
local boolean_options="
|
local boolean_options="
|
||||||
--compress
|
--compress
|
||||||
|
--digestflag
|
||||||
--help
|
--help
|
||||||
-h
|
-h
|
||||||
--quiet
|
--quiet
|
||||||
|
@ -61,13 +61,17 @@ value can be entered. The password is entered without echo.
|
|||||||
**--cert-dir**=*path*
|
**--cert-dir**=*path*
|
||||||
|
|
||||||
Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry.
|
Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry.
|
||||||
Default certificates directory is _/etc/containers/certs.d_. (Not available for remote commands) (Not available for remote commands)
|
Default certificates directory is _/etc/containers/certs.d_. (Not available for remote commands)
|
||||||
|
|
||||||
**--compress**
|
**--compress**
|
||||||
|
|
||||||
Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type, compressed or uncompressed, as source)
|
Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type, compressed or uncompressed, as 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
|
||||||
|
|
||||||
|
**--digestfile** *Digestfile*
|
||||||
|
|
||||||
|
After copying the image, write the digest of the resulting image to the file. (Not available for remote commands)
|
||||||
|
|
||||||
**--format**, **-f**=*format*
|
**--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)
|
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)
|
||||||
@ -93,19 +97,23 @@ TLS verification will be used unless the target registry is listed as an insecur
|
|||||||
|
|
||||||
## EXAMPLE
|
## EXAMPLE
|
||||||
|
|
||||||
This example extracts the imageID image to a local directory in docker format.
|
This example pushes the image specified by the imageID to a local directory in docker format.
|
||||||
|
|
||||||
`# podman push imageID dir:/path/to/image`
|
`# podman push imageID dir:/path/to/image`
|
||||||
|
|
||||||
This example extracts the imageID image to a local directory in oci format.
|
This example pushes the image specified by the imageID to a local directory in oci format.
|
||||||
|
|
||||||
`# podman push imageID oci-archive:/path/to/layout:image:tag`
|
`# podman push imageID oci-archive:/path/to/layout:image:tag`
|
||||||
|
|
||||||
This example extracts the imageID image to a container registry named registry.example.com
|
This example pushes the image specified by the imageID to a container registry named registry.example.com
|
||||||
|
|
||||||
`# podman push imageID docker://registry.example.com/repository:tag`
|
`# podman push imageID docker://registry.example.com/repository:tag`
|
||||||
|
|
||||||
This example extracts the imageID image and puts into the local docker container store
|
This example pushes the image specified by the imageID to a container registry named registry.example.com and saves the digest in the specified digestfile.
|
||||||
|
|
||||||
|
`# podman push --digestfile=/tmp/mydigest imageID docker://registry.example.com/repository:tag`
|
||||||
|
|
||||||
|
This example pushes the image specified by the imageID and puts it into the local docker container store
|
||||||
|
|
||||||
`# podman push imageID docker-daemon:image:tag`
|
`# podman push imageID docker-daemon:image:tag`
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@ -555,7 +556,7 @@ func (i *Image) UntagImage(tag string) error {
|
|||||||
|
|
||||||
// PushImageToHeuristicDestination pushes the given image to "destination", which is heuristically parsed.
|
// PushImageToHeuristicDestination pushes the given image to "destination", which is heuristically parsed.
|
||||||
// Use PushImageToReference if the destination is known precisely.
|
// Use PushImageToReference if the destination is known precisely.
|
||||||
func (i *Image) PushImageToHeuristicDestination(ctx context.Context, destination, manifestMIMEType, authFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error {
|
func (i *Image) PushImageToHeuristicDestination(ctx context.Context, destination, manifestMIMEType, authFile, digestFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error {
|
||||||
if destination == "" {
|
if destination == "" {
|
||||||
return errors.Wrapf(syscall.EINVAL, "destination image name must be specified")
|
return errors.Wrapf(syscall.EINVAL, "destination image name must be specified")
|
||||||
}
|
}
|
||||||
@ -573,11 +574,11 @@ func (i *Image) PushImageToHeuristicDestination(ctx context.Context, destination
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return i.PushImageToReference(ctx, dest, manifestMIMEType, authFile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, additionalDockerArchiveTags)
|
return i.PushImageToReference(ctx, dest, manifestMIMEType, authFile, digestFile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, additionalDockerArchiveTags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushImageToReference pushes the given image to a location described by the given path
|
// PushImageToReference pushes the given image to a location described by the given path
|
||||||
func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageReference, manifestMIMEType, authFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error {
|
func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageReference, manifestMIMEType, authFile, digestFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error {
|
||||||
sc := GetSystemContext(signaturePolicyPath, authFile, forceCompress)
|
sc := GetSystemContext(signaturePolicyPath, authFile, forceCompress)
|
||||||
sc.BlobInfoCacheDir = filepath.Join(i.imageruntime.store.GraphRoot(), "cache")
|
sc.BlobInfoCacheDir = filepath.Join(i.imageruntime.store.GraphRoot(), "cache")
|
||||||
|
|
||||||
@ -599,10 +600,22 @@ func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageRefere
|
|||||||
copyOptions := getCopyOptions(sc, writer, nil, dockerRegistryOptions, signingOptions, manifestMIMEType, additionalDockerArchiveTags)
|
copyOptions := getCopyOptions(sc, writer, nil, dockerRegistryOptions, signingOptions, manifestMIMEType, additionalDockerArchiveTags)
|
||||||
copyOptions.DestinationCtx.SystemRegistriesConfPath = registries.SystemRegistriesConfPath() // FIXME: Set this more globally. Probably no reason not to have it in every types.SystemContext, and to compute the value just once in one place.
|
copyOptions.DestinationCtx.SystemRegistriesConfPath = registries.SystemRegistriesConfPath() // FIXME: Set this more globally. Probably no reason not to have it in every types.SystemContext, and to compute the value just once in one place.
|
||||||
// Copy the image to the remote destination
|
// Copy the image to the remote destination
|
||||||
_, err = cp.Image(ctx, policyContext, dest, src, copyOptions)
|
manifestBytes, err := cp.Image(ctx, policyContext, dest, src, copyOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "Error copying image to the remote destination")
|
return errors.Wrapf(err, "Error copying image to the remote destination")
|
||||||
}
|
}
|
||||||
|
digest, err := manifest.Digest(manifestBytes)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error computing digest of manifest of new image %q", transports.ImageName(dest))
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("Successfully pushed %s with digest %s", transports.ImageName(dest), digest.String())
|
||||||
|
|
||||||
|
if digestFile != "" {
|
||||||
|
if err = ioutil.WriteFile(digestFile, []byte(digest.String()), 0644); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to write digest to file %q", digestFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
i.newImageEvent(events.Push)
|
i.newImageEvent(events.Push)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -1358,7 +1371,7 @@ func (i *Image) Save(ctx context.Context, source, format, output string, moreTag
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := i.PushImageToReference(ctx, destRef, manifestType, "", "", writer, compress, SigningOptions{}, &DockerRegistryOptions{}, additionaltags); err != nil {
|
if err := i.PushImageToReference(ctx, destRef, manifestType, "", "", "", writer, compress, SigningOptions{}, &DockerRegistryOptions{}, additionaltags); err != nil {
|
||||||
return errors.Wrapf(err, "unable to save %q", source)
|
return errors.Wrapf(err, "unable to save %q", source)
|
||||||
}
|
}
|
||||||
i.newImageEvent(events.Save)
|
i.newImageEvent(events.Save)
|
||||||
|
@ -201,12 +201,12 @@ func (r *LocalRuntime) RemoveVolumes(ctx context.Context, c *cliconfig.VolumeRmV
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Push is a wrapper to push an image to a registry
|
// Push is a wrapper to push an image to a registry
|
||||||
func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error {
|
func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, digestfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error {
|
||||||
newImage, err := r.ImageRuntime().NewFromLocal(srcName)
|
newImage, err := r.ImageRuntime().NewFromLocal(srcName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return newImage.PushImageToHeuristicDestination(ctx, destination, manifestMIMEType, authfile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, nil)
|
return newImage.PushImageToHeuristicDestination(ctx, destination, manifestMIMEType, authfile, digestfile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InspectVolumes returns a slice of volumes based on an arg list or --all
|
// InspectVolumes returns a slice of volumes based on an arg list or --all
|
||||||
|
@ -619,7 +619,7 @@ func (r *LocalRuntime) RemoveVolumes(ctx context.Context, c *cliconfig.VolumeRmV
|
|||||||
return iopodman.VolumeRemove().Call(r.Conn, rmOpts)
|
return iopodman.VolumeRemove().Call(r.Conn, rmOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error {
|
func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, digestfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error {
|
||||||
|
|
||||||
reply, err := iopodman.PushImage().Send(r.Conn, varlink.More, srcName, destination, forceCompress, manifestMIMEType, signingOptions.RemoveSignatures, signingOptions.SignBy)
|
reply, err := iopodman.PushImage().Send(r.Conn, varlink.More, srcName, destination, forceCompress, manifestMIMEType, signingOptions.RemoveSignatures, signingOptions.SignBy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -353,7 +353,7 @@ func (i *LibpodAPI) PushImage(call iopodman.VarlinkCall, name, tag string, compr
|
|||||||
output := bytes.NewBuffer([]byte{})
|
output := bytes.NewBuffer([]byte{})
|
||||||
c := make(chan error)
|
c := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
err := newImage.PushImageToHeuristicDestination(getContext(), destname, manifestType, "", "", output, compress, so, &dockerRegistryOptions, nil)
|
err := newImage.PushImageToHeuristicDestination(getContext(), destname, manifestType, "", "", "", output, compress, so, &dockerRegistryOptions, nil)
|
||||||
c <- err
|
c <- err
|
||||||
close(c)
|
close(c)
|
||||||
}()
|
}()
|
||||||
@ -615,7 +615,7 @@ func (i *LibpodAPI) ExportImage(call iopodman.VarlinkCall, name, destination str
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := newImage.PushImageToHeuristicDestination(getContext(), destination, "", "", "", nil, compress, image.SigningOptions{}, &image.DockerRegistryOptions{}, additionalTags); err != nil {
|
if err := newImage.PushImageToHeuristicDestination(getContext(), destination, "", "", "", "", nil, compress, image.SigningOptions{}, &image.DockerRegistryOptions{}, additionalTags); err != nil {
|
||||||
return call.ReplyErrorOccurred(err.Error())
|
return call.ReplyErrorOccurred(err.Error())
|
||||||
}
|
}
|
||||||
return call.ReplyExportImage(newImage.ID())
|
return call.ReplyExportImage(newImage.ID())
|
||||||
|
@ -76,6 +76,14 @@ var _ = Describe("Podman push", func() {
|
|||||||
push := podmanTest.PodmanNoCache([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"})
|
push := podmanTest.PodmanNoCache([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"})
|
||||||
push.WaitWithDefaultTimeout()
|
push.WaitWithDefaultTimeout()
|
||||||
Expect(push.ExitCode()).To(Equal(0))
|
Expect(push.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
// Test --digestfile option
|
||||||
|
push2 := podmanTest.PodmanNoCache([]string{"push", "--tls-verify=false", "--digestfile=/tmp/digestfile.txt", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"})
|
||||||
|
push2.WaitWithDefaultTimeout()
|
||||||
|
fi, err := os.Lstat("/tmp/digestfile.txt")
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(fi.Name()).To(Equal("digestfile.txt"))
|
||||||
|
Expect(push2.ExitCode()).To(Equal(0))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman push to local registry with authorization", func() {
|
It("podman push to local registry with authorization", func() {
|
||||||
|
Reference in New Issue
Block a user