mirror of https://github.com/containers/podman.git
Add manifest type conversion to kpod push
User can select from 3 manifest types: oci, v2s1, or v2s2 e.g kpod push --format v2s2 alpine dir:my-directory Added "compress" flag to enable compression when true Signed-off-by: umohnani8 <umohnani@redhat.com> Closes: #126 Approved by: rhatdan
This commit is contained in:
parent
bf0d359047
commit
1e7d880b56
|
|
@ -56,7 +56,7 @@ func loginCmd(c *cli.Context) error {
|
||||||
server = args[0]
|
server = args[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
sc := common.GetSystemContext("", c.String("authfile"))
|
sc := common.GetSystemContext("", c.String("authfile"), false)
|
||||||
|
|
||||||
// username of user logged in to server (if one exists)
|
// username of user logged in to server (if one exists)
|
||||||
userFromAuthFile := config.GetUserLoggedIn(sc, server)
|
userFromAuthFile := config.GetUserLoggedIn(sc, server)
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ func logoutCmd(c *cli.Context) error {
|
||||||
server = args[0]
|
server = args[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
sc := common.GetSystemContext("", c.String("authfile"))
|
sc := common.GetSystemContext("", c.String("authfile"), false)
|
||||||
|
|
||||||
if c.Bool("all") {
|
if c.Bool("all") {
|
||||||
if err := config.RemoveAllAuthentication(sc); err != nil {
|
if err := config.RemoveAllAuthentication(sc); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,12 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/image/manifest"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/containers/storage/pkg/archive"
|
"github.com/containers/storage/pkg/archive"
|
||||||
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/projectatomic/libpod/libpod"
|
"github.com/projectatomic/libpod/libpod"
|
||||||
"github.com/projectatomic/libpod/libpod/common"
|
"github.com/projectatomic/libpod/libpod/common"
|
||||||
|
|
@ -29,6 +32,14 @@ var (
|
||||||
Name: "cert-dir",
|
Name: "cert-dir",
|
||||||
Usage: "`pathname` of a directory containing TLS certificates and keys",
|
Usage: "`pathname` of a directory containing TLS certificates and keys",
|
||||||
},
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "compress",
|
||||||
|
Usage: "compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type as source)",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "format, f",
|
||||||
|
Usage: "manifest type (oci, v2s1, or v2s2) to use when pushing an image using the 'dir:' transport (default is manifest type of source)",
|
||||||
|
},
|
||||||
cli.BoolTFlag{
|
cli.BoolTFlag{
|
||||||
Name: "tls-verify",
|
Name: "tls-verify",
|
||||||
Usage: "require HTTPS and verify certificates when contacting registries (default: true)",
|
Usage: "require HTTPS and verify certificates when contacting registries (default: true)",
|
||||||
|
|
@ -75,8 +86,16 @@ func pushCmd(c *cli.Context) error {
|
||||||
if err := validateFlags(c, pushFlags); err != nil {
|
if err := validateFlags(c, pushFlags); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
srcName := c.Args().Get(0)
|
srcName := args[0]
|
||||||
destName := c.Args().Get(1)
|
destName := args[1]
|
||||||
|
|
||||||
|
// --compress and --format can only be used for the "dir" transport
|
||||||
|
splitArg := strings.SplitN(destName, ":", 2)
|
||||||
|
if c.IsSet("compress") || c.IsSet("format") {
|
||||||
|
if splitArg[0] != libpod.DirTransport {
|
||||||
|
return errors.Errorf("--compress and --format can be set only when pushing to a directory using the 'dir' transport")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
registryCredsString := c.String("creds")
|
registryCredsString := c.String("creds")
|
||||||
certPath := c.String("cert-dir")
|
certPath := c.String("cert-dir")
|
||||||
|
|
@ -112,6 +131,20 @@ func pushCmd(c *cli.Context) error {
|
||||||
writer = os.Stdout
|
writer = os.Stdout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var manifestType string
|
||||||
|
if c.IsSet("format") {
|
||||||
|
switch c.String("format") {
|
||||||
|
case "oci":
|
||||||
|
manifestType = imgspecv1.MediaTypeImageManifest
|
||||||
|
case "v2s1":
|
||||||
|
manifestType = manifest.DockerV2Schema1SignedMediaType
|
||||||
|
case "v2s2", "docker":
|
||||||
|
manifestType = manifest.DockerV2Schema2MediaType
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", c.String("format"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
options := libpod.CopyOptions{
|
options := libpod.CopyOptions{
|
||||||
Compression: archive.Uncompressed,
|
Compression: archive.Uncompressed,
|
||||||
SignaturePolicyPath: c.String("signature-policy"),
|
SignaturePolicyPath: c.String("signature-policy"),
|
||||||
|
|
@ -126,6 +159,8 @@ func pushCmd(c *cli.Context) error {
|
||||||
},
|
},
|
||||||
AuthFile: c.String("authfile"),
|
AuthFile: c.String("authfile"),
|
||||||
Writer: writer,
|
Writer: writer,
|
||||||
|
ManifestMIMEType: manifestType,
|
||||||
|
ForceCompress: c.Bool("compress"),
|
||||||
}
|
}
|
||||||
|
|
||||||
return runtime.PushImage(srcName, destName, options)
|
return runtime.PushImage(srcName, destName, options)
|
||||||
|
|
|
||||||
|
|
@ -921,8 +921,7 @@ _kpod_mount() {
|
||||||
|
|
||||||
_kpod_push() {
|
_kpod_push() {
|
||||||
local boolean_options="
|
local boolean_options="
|
||||||
--disable-compression
|
--compress
|
||||||
-D
|
|
||||||
--quiet
|
--quiet
|
||||||
-q
|
-q
|
||||||
--remove-signatures
|
--remove-signatures
|
||||||
|
|
@ -931,6 +930,7 @@ _kpod_push() {
|
||||||
|
|
||||||
local options_with_args="
|
local options_with_args="
|
||||||
--authfile
|
--authfile
|
||||||
|
--format
|
||||||
--cert-dir
|
--cert-dir
|
||||||
--creds
|
--creds
|
||||||
--sign-by
|
--sign-by
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,10 @@ kpod push - Push an image from local storage to elsewhere
|
||||||
**kpod** **push** [*options* [...]] **imageID** [**destination**]
|
**kpod** **push** [*options* [...]] **imageID** [**destination**]
|
||||||
|
|
||||||
## DESCRIPTION
|
## DESCRIPTION
|
||||||
Pushes an image from local storage to a specified destination, decompressing
|
Pushes an image from local storage to a specified destination.
|
||||||
and recompressing layers as needed.
|
Push is mainly used to push images to registries, however **kpod push**
|
||||||
|
can be used to save images to tarballs and directories using the following
|
||||||
|
transports: **dir:**, **docker-archive:**, **docker-daemon:**, **oci-archive:**, and **ostree:**.
|
||||||
|
|
||||||
## imageID
|
## imageID
|
||||||
Image stored in local container/storage
|
Image stored in local container/storage
|
||||||
|
|
@ -19,6 +21,8 @@ Image stored in local container/storage
|
||||||
|
|
||||||
The DESTINATION is a location to store container images
|
The DESTINATION is a location to store container images
|
||||||
The Image "DESTINATION" uses a "transport":"details" format.
|
The Image "DESTINATION" uses a "transport":"details" format.
|
||||||
|
If a transport is not given, kpod push will attempt to push
|
||||||
|
to a registry.
|
||||||
|
|
||||||
Multiple transports are supported:
|
Multiple transports are supported:
|
||||||
|
|
||||||
|
|
@ -55,9 +59,15 @@ Credentials (USERNAME:PASSWORD) to use for authenticating to a registry
|
||||||
|
|
||||||
Pathname of a directory containing TLS certificates and keys
|
Pathname of a directory containing TLS certificates and keys
|
||||||
|
|
||||||
**--disable-compression, -D**
|
**--compress**
|
||||||
|
|
||||||
Don't compress copies of filesystem layers which will be pushed
|
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
|
||||||
|
|
||||||
|
**--format, -f**
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
**--quiet, -q**
|
**--quiet, -q**
|
||||||
|
|
||||||
|
|
@ -113,5 +123,19 @@ Writing manifest to image destination
|
||||||
Storing signatures
|
Storing signatures
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This example pushes the rhel7 image to rhel7-dir with the "oci" manifest type
|
||||||
|
```
|
||||||
|
# kpod push --format oci registry.access.redhat.com/rhel7 dir:rhel7-dir
|
||||||
|
Getting image source signatures
|
||||||
|
Copying blob sha256:9cadd93b16ff2a0c51ac967ea2abfadfac50cfa3af8b5bf983d89b8f8647f3e4
|
||||||
|
71.41 MB / 71.41 MB [======================================================] 9s
|
||||||
|
Copying blob sha256:4aa565ad8b7a87248163ce7dba1dd3894821aac97e846b932ff6b8ef9a8a508a
|
||||||
|
1.21 KB / 1.21 KB [========================================================] 0s
|
||||||
|
Copying config sha256:f1b09a81455c351eaa484b61aacd048ab613c08e4c5d1da80c4c46301b03cf3b
|
||||||
|
3.01 KB / 3.01 KB [========================================================] 0s
|
||||||
|
Writing manifest to image destination
|
||||||
|
Storing signatures
|
||||||
|
```
|
||||||
|
|
||||||
## SEE ALSO
|
## SEE ALSO
|
||||||
kpod(1), kpod-pull(1), crio(8), crio.conf(5), docker-login(1)
|
kpod(1), kpod-pull(1), crio(8), crio.conf(5), docker-login(1)
|
||||||
|
|
|
||||||
|
|
@ -16,31 +16,33 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetCopyOptions constructs a new containers/image/copy.Options{} struct from the given parameters
|
// GetCopyOptions constructs a new containers/image/copy.Options{} struct from the given parameters
|
||||||
func GetCopyOptions(reportWriter io.Writer, signaturePolicyPath string, srcDockerRegistry, destDockerRegistry *DockerRegistryOptions, signing SigningOptions, authFile string) *cp.Options {
|
func GetCopyOptions(reportWriter io.Writer, signaturePolicyPath string, srcDockerRegistry, destDockerRegistry *DockerRegistryOptions, signing SigningOptions, authFile, manifestType string, forceCompress bool) *cp.Options {
|
||||||
if srcDockerRegistry == nil {
|
if srcDockerRegistry == nil {
|
||||||
srcDockerRegistry = &DockerRegistryOptions{}
|
srcDockerRegistry = &DockerRegistryOptions{}
|
||||||
}
|
}
|
||||||
if destDockerRegistry == nil {
|
if destDockerRegistry == nil {
|
||||||
destDockerRegistry = &DockerRegistryOptions{}
|
destDockerRegistry = &DockerRegistryOptions{}
|
||||||
}
|
}
|
||||||
srcContext := srcDockerRegistry.GetSystemContext(signaturePolicyPath, authFile)
|
srcContext := srcDockerRegistry.GetSystemContext(signaturePolicyPath, authFile, forceCompress)
|
||||||
destContext := destDockerRegistry.GetSystemContext(signaturePolicyPath, authFile)
|
destContext := destDockerRegistry.GetSystemContext(signaturePolicyPath, authFile, forceCompress)
|
||||||
return &cp.Options{
|
return &cp.Options{
|
||||||
RemoveSignatures: signing.RemoveSignatures,
|
RemoveSignatures: signing.RemoveSignatures,
|
||||||
SignBy: signing.SignBy,
|
SignBy: signing.SignBy,
|
||||||
ReportWriter: reportWriter,
|
ReportWriter: reportWriter,
|
||||||
SourceCtx: srcContext,
|
SourceCtx: srcContext,
|
||||||
DestinationCtx: destContext,
|
DestinationCtx: destContext,
|
||||||
|
ForceManifestMIMEType: manifestType,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSystemContext Constructs a new containers/image/types.SystemContext{} struct from the given signaturePolicy path
|
// GetSystemContext Constructs a new containers/image/types.SystemContext{} struct from the given signaturePolicy path
|
||||||
func GetSystemContext(signaturePolicyPath, authFilePath string) *types.SystemContext {
|
func GetSystemContext(signaturePolicyPath, authFilePath string, forceCompress bool) *types.SystemContext {
|
||||||
sc := &types.SystemContext{}
|
sc := &types.SystemContext{}
|
||||||
if signaturePolicyPath != "" {
|
if signaturePolicyPath != "" {
|
||||||
sc.SignaturePolicyPath = signaturePolicyPath
|
sc.SignaturePolicyPath = signaturePolicyPath
|
||||||
}
|
}
|
||||||
sc.AuthFilePath = authFilePath
|
sc.AuthFilePath = authFilePath
|
||||||
|
sc.DirForceCompress = forceCompress
|
||||||
return sc
|
return sc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,13 +22,14 @@ type DockerRegistryOptions struct {
|
||||||
|
|
||||||
// GetSystemContext constructs a new system context from the given signaturePolicy path and the
|
// GetSystemContext constructs a new system context from the given signaturePolicy path and the
|
||||||
// values in the DockerRegistryOptions
|
// values in the DockerRegistryOptions
|
||||||
func (o DockerRegistryOptions) GetSystemContext(signaturePolicyPath, authFile string) *types.SystemContext {
|
func (o DockerRegistryOptions) GetSystemContext(signaturePolicyPath, authFile string, forceCompress bool) *types.SystemContext {
|
||||||
sc := &types.SystemContext{
|
sc := &types.SystemContext{
|
||||||
SignaturePolicyPath: signaturePolicyPath,
|
SignaturePolicyPath: signaturePolicyPath,
|
||||||
DockerAuthConfig: o.DockerRegistryCreds,
|
DockerAuthConfig: o.DockerRegistryCreds,
|
||||||
DockerCertPath: o.DockerCertPath,
|
DockerCertPath: o.DockerCertPath,
|
||||||
DockerInsecureSkipTLSVerify: o.DockerInsecureSkipTLSVerify,
|
DockerInsecureSkipTLSVerify: o.DockerInsecureSkipTLSVerify,
|
||||||
AuthFilePath: authFile,
|
AuthFilePath: authFile,
|
||||||
|
DirForceCompress: forceCompress,
|
||||||
}
|
}
|
||||||
return sc
|
return sc
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,10 @@ type CopyOptions struct {
|
||||||
Reference string
|
Reference string
|
||||||
// ImageConfig is the Image spec for the image created when a tar archive is imported
|
// ImageConfig is the Image spec for the image created when a tar archive is imported
|
||||||
ImageConfig ociv1.Image
|
ImageConfig ociv1.Image
|
||||||
|
// ManifestMIMEType is the manifest type of the image when saving to a directory
|
||||||
|
ManifestMIMEType string
|
||||||
|
// ForceCompress compresses the image layers when saving to a directory using the dir transport if true
|
||||||
|
ForceCompress bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Image API
|
// Image API
|
||||||
|
|
@ -643,7 +647,7 @@ func (r *Runtime) PullImage(imgName string, options CopyOptions) (string, error)
|
||||||
signaturePolicyPath = options.SignaturePolicyPath
|
signaturePolicyPath = options.SignaturePolicyPath
|
||||||
}
|
}
|
||||||
|
|
||||||
sc := common.GetSystemContext(signaturePolicyPath, options.AuthFile)
|
sc := common.GetSystemContext(signaturePolicyPath, options.AuthFile, false)
|
||||||
|
|
||||||
srcRef, err := alltransports.ParseImageName(imgName)
|
srcRef, err := alltransports.ParseImageName(imgName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -664,7 +668,7 @@ func (r *Runtime) PullImage(imgName string, options CopyOptions) (string, error)
|
||||||
}
|
}
|
||||||
defer policyContext.Destroy()
|
defer policyContext.Destroy()
|
||||||
|
|
||||||
copyOptions := common.GetCopyOptions(options.Writer, signaturePolicyPath, &options.DockerRegistryOptions, nil, options.SigningOptions, options.AuthFile)
|
copyOptions := common.GetCopyOptions(options.Writer, signaturePolicyPath, &options.DockerRegistryOptions, nil, options.SigningOptions, options.AuthFile, "", false)
|
||||||
|
|
||||||
for _, imageInfo := range pullStructs {
|
for _, imageInfo := range pullStructs {
|
||||||
// Print the following statement only when pulling from a docker or atomic registry
|
// Print the following statement only when pulling from a docker or atomic registry
|
||||||
|
|
@ -721,7 +725,7 @@ func (r *Runtime) PushImage(source string, destination string, options CopyOptio
|
||||||
signaturePolicyPath = options.SignaturePolicyPath
|
signaturePolicyPath = options.SignaturePolicyPath
|
||||||
}
|
}
|
||||||
|
|
||||||
sc := common.GetSystemContext(signaturePolicyPath, options.AuthFile)
|
sc := common.GetSystemContext(signaturePolicyPath, options.AuthFile, options.ForceCompress)
|
||||||
|
|
||||||
policyContext, err := getPolicyContext(sc)
|
policyContext, err := getPolicyContext(sc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -735,7 +739,7 @@ func (r *Runtime) PushImage(source string, destination string, options CopyOptio
|
||||||
return errors.Wrapf(err, "error getting source imageReference for %q", source)
|
return errors.Wrapf(err, "error getting source imageReference for %q", source)
|
||||||
}
|
}
|
||||||
|
|
||||||
copyOptions := common.GetCopyOptions(options.Writer, signaturePolicyPath, nil, &options.DockerRegistryOptions, options.SigningOptions, options.AuthFile)
|
copyOptions := common.GetCopyOptions(options.Writer, signaturePolicyPath, nil, &options.DockerRegistryOptions, options.SigningOptions, options.AuthFile, options.ManifestMIMEType, options.ForceCompress)
|
||||||
|
|
||||||
// Copy the image to the remote destination
|
// Copy the image to the remote destination
|
||||||
err = cp.Image(policyContext, dest, src, copyOptions)
|
err = cp.Image(policyContext, dest, src, copyOptions)
|
||||||
|
|
@ -1004,7 +1008,7 @@ func (r *Runtime) ImportImage(path string, options CopyOptions) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var reference = options.Reference
|
var reference = options.Reference
|
||||||
sc := common.GetSystemContext("", "")
|
sc := common.GetSystemContext("", "", false)
|
||||||
|
|
||||||
// if reference not given, get the image digest
|
// if reference not given, get the image digest
|
||||||
if reference == "" {
|
if reference == "" {
|
||||||
|
|
@ -1020,7 +1024,7 @@ func (r *Runtime) ImportImage(path string, options CopyOptions) error {
|
||||||
}
|
}
|
||||||
defer policyContext.Destroy()
|
defer policyContext.Destroy()
|
||||||
|
|
||||||
copyOptions := common.GetCopyOptions(os.Stdout, "", nil, nil, common.SigningOptions{}, "")
|
copyOptions := common.GetCopyOptions(os.Stdout, "", nil, nil, common.SigningOptions{}, "", "", false)
|
||||||
|
|
||||||
dest, err := is.Transport.ParseStoreReference(r.store, reference)
|
dest, err := is.Transport.ParseStoreReference(r.store, reference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -84,3 +84,19 @@ function setup() {
|
||||||
run ${KPOD_BINARY} $KPOD_OPTIONS rmi "$ALPINE"
|
run ${KPOD_BINARY} $KPOD_OPTIONS rmi "$ALPINE"
|
||||||
echo "$output"
|
echo "$output"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "push with manifest type conversion" {
|
||||||
|
run bash -c "${KPOD_BINARY} $KPOD_OPTIONS push --format oci "${BB}" dir:my-dir"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run bash -c "grep "application/vnd.oci.image.config.v1+json" my-dir/manifest.json"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run bash -c "${KPOD_BINARY} $KPOD_OPTIONS push --compress --format v2s2 "${BB}" dir:my-dir"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run bash -c "grep "application/vnd.docker.distribution.manifest.v2+json" my-dir/manifest.json"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
rm -rf my-dir
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue