mirror of https://github.com/containers/podman.git
168 lines
4.7 KiB
Go
168 lines
4.7 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/containers/image/directory"
|
|
"github.com/containers/image/manifest"
|
|
"github.com/containers/image/types"
|
|
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
|
"github.com/containers/libpod/libpod/image"
|
|
"github.com/containers/libpod/pkg/util"
|
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"github.com/pkg/errors"
|
|
"github.com/urfave/cli"
|
|
)
|
|
|
|
var (
|
|
pushFlags = []cli.Flag{
|
|
cli.StringFlag{
|
|
Name: "signature-policy",
|
|
Usage: "`pathname` of signature policy file (not usually used)",
|
|
Hidden: true,
|
|
},
|
|
cli.StringFlag{
|
|
Name: "creds",
|
|
Usage: "`credentials` (USERNAME:PASSWORD) to use for authenticating to a registry",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "cert-dir",
|
|
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{
|
|
Name: "tls-verify",
|
|
Usage: "require HTTPS and verify certificates when contacting registries (default: true)",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "remove-signatures",
|
|
Usage: "discard any pre-existing signatures in the image",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "sign-by",
|
|
Usage: "add a signature at the destination using the specified key",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "quiet, q",
|
|
Usage: "don't output progress information when pushing images",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "authfile",
|
|
Usage: "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override. ",
|
|
},
|
|
}
|
|
pushDescription = fmt.Sprintf(`
|
|
Pushes an image to a specified location.
|
|
The Image "DESTINATION" uses a "transport":"details" format.
|
|
See podman-push(1) section "DESTINATION" for the expected format`)
|
|
|
|
pushCommand = cli.Command{
|
|
Name: "push",
|
|
Usage: "Push an image to a specified destination",
|
|
Description: pushDescription,
|
|
Flags: sortFlags(pushFlags),
|
|
Action: pushCmd,
|
|
ArgsUsage: "IMAGE DESTINATION",
|
|
OnUsageError: usageErrorHandler,
|
|
}
|
|
)
|
|
|
|
func pushCmd(c *cli.Context) error {
|
|
var (
|
|
registryCreds *types.DockerAuthConfig
|
|
destName string
|
|
)
|
|
|
|
args := c.Args()
|
|
if len(args) == 0 || len(args) > 2 {
|
|
return errors.New("podman push requires at least one image name, and optionally a second to specify a different destination name")
|
|
}
|
|
srcName := args[0]
|
|
switch len(args) {
|
|
case 1:
|
|
destName = args[0]
|
|
case 2:
|
|
destName = args[1]
|
|
}
|
|
if err := validateFlags(c, pushFlags); err != nil {
|
|
return err
|
|
}
|
|
|
|
// --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] != directory.Transport.Name() {
|
|
return errors.Errorf("--compress and --format can be set only when pushing to a directory using the 'dir' transport")
|
|
}
|
|
}
|
|
|
|
certPath := c.String("cert-dir")
|
|
removeSignatures := c.Bool("remove-signatures")
|
|
signBy := c.String("sign-by")
|
|
|
|
if c.IsSet("creds") {
|
|
creds, err := util.ParseRegistryCreds(c.String("creds"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
registryCreds = creds
|
|
}
|
|
|
|
runtime, err := libpodruntime.GetRuntime(c)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "could not create runtime")
|
|
}
|
|
defer runtime.Shutdown(false)
|
|
|
|
var writer io.Writer
|
|
if !c.Bool("quiet") {
|
|
writer = os.Stderr
|
|
}
|
|
|
|
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"))
|
|
}
|
|
}
|
|
|
|
dockerRegistryOptions := image.DockerRegistryOptions{
|
|
DockerRegistryCreds: registryCreds,
|
|
DockerCertPath: certPath,
|
|
}
|
|
if c.IsSet("tls-verify") {
|
|
dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.BoolT("tls-verify"))
|
|
}
|
|
|
|
so := image.SigningOptions{
|
|
RemoveSignatures: removeSignatures,
|
|
SignBy: signBy,
|
|
}
|
|
|
|
newImage, err := runtime.ImageRuntime().NewFromLocal(srcName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
authfile := getAuthFile(c.String("authfile"))
|
|
|
|
return newImage.PushImageToHeuristicDestination(getContext(), destName, manifestType, authfile, c.String("signature-policy"), writer, c.Bool("compress"), so, &dockerRegistryOptions, nil)
|
|
}
|