github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/cmd/podman/pull.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  
     8  	buildahcli "github.com/containers/buildah/pkg/cli"
     9  	"github.com/containers/image/v5/docker"
    10  	dockerarchive "github.com/containers/image/v5/docker/archive"
    11  	"github.com/containers/image/v5/transports/alltransports"
    12  	"github.com/containers/image/v5/types"
    13  	"github.com/containers/libpod/cmd/podman/cliconfig"
    14  	"github.com/containers/libpod/libpod/image"
    15  	"github.com/containers/libpod/pkg/adapter"
    16  	"github.com/containers/libpod/pkg/util"
    17  	"github.com/docker/distribution/reference"
    18  	"github.com/opentracing/opentracing-go"
    19  	"github.com/pkg/errors"
    20  	"github.com/sirupsen/logrus"
    21  	"github.com/spf13/cobra"
    22  )
    23  
    24  var (
    25  	pullCommand     cliconfig.PullValues
    26  	pullDescription = `Pulls an image from a registry and stores it locally.
    27  
    28    An image can be pulled using its tag or digest. If a tag is not specified, the image with the 'latest' tag (if it exists) is pulled.`
    29  	_pullCommand = &cobra.Command{
    30  		Use:   "pull [flags] IMAGE-PATH",
    31  		Short: "Pull an image from a registry",
    32  		Long:  pullDescription,
    33  		RunE: func(cmd *cobra.Command, args []string) error {
    34  			pullCommand.InputArgs = args
    35  			pullCommand.GlobalFlags = MainGlobalOpts
    36  			pullCommand.Remote = remoteclient
    37  			return pullCmd(&pullCommand)
    38  		},
    39  		Example: `podman pull imageName
    40    podman pull fedora:latest`,
    41  	}
    42  )
    43  
    44  func init() {
    45  
    46  	if !remote {
    47  		_pullCommand.Example = fmt.Sprintf("%s\n  podman pull --cert-dir image/certs --authfile temp-auths/myauths.json docker://docker.io/myrepo/finaltest", _pullCommand.Example)
    48  
    49  	}
    50  	pullCommand.Command = _pullCommand
    51  	pullCommand.SetHelpTemplate(HelpTemplate())
    52  	pullCommand.SetUsageTemplate(UsageTemplate())
    53  	flags := pullCommand.Flags()
    54  	flags.BoolVar(&pullCommand.AllTags, "all-tags", false, "All tagged images in the repository will be pulled")
    55  	flags.StringVar(&pullCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
    56  	flags.BoolVarP(&pullCommand.Quiet, "quiet", "q", false, "Suppress output information when pulling images")
    57  	flags.StringVar(&pullCommand.OverrideArch, "override-arch", "", "use `ARCH` instead of the architecture of the machine for choosing images")
    58  	flags.StringVar(&pullCommand.OverrideOS, "override-os", "", "use `OS` instead of the running OS for choosing images")
    59  	markFlagHidden(flags, "override-os")
    60  	// Disabled flags for the remote client
    61  	if !remote {
    62  		flags.StringVar(&pullCommand.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
    63  		flags.StringVar(&pullCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys")
    64  		flags.StringVar(&pullCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")
    65  		flags.BoolVar(&pullCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
    66  		markFlagHidden(flags, "signature-policy")
    67  	}
    68  }
    69  
    70  // pullCmd gets the data from the command line and calls pullImage
    71  // to copy an image from a registry to a local machine
    72  func pullCmd(c *cliconfig.PullValues) (retError error) {
    73  	defer func() {
    74  		if retError != nil && exitCode == 0 {
    75  			exitCode = 1
    76  		}
    77  	}()
    78  	if c.Bool("trace") {
    79  		span, _ := opentracing.StartSpanFromContext(Ctx, "pullCmd")
    80  		defer span.Finish()
    81  	}
    82  
    83  	runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand)
    84  
    85  	if err != nil {
    86  		return errors.Wrapf(err, "could not get runtime")
    87  	}
    88  	defer runtime.DeferredShutdown(false)
    89  
    90  	args := c.InputArgs
    91  	if len(args) == 0 {
    92  		return errors.Errorf("an image name must be specified")
    93  	}
    94  	if len(args) > 1 {
    95  		return errors.Errorf("too many arguments. Requires exactly 1")
    96  	}
    97  
    98  	if c.Authfile != "" {
    99  		if _, err := os.Stat(c.Authfile); err != nil {
   100  			return errors.Wrapf(err, "error getting authfile %s", c.Authfile)
   101  		}
   102  	}
   103  
   104  	ctx := getContext()
   105  	imageName := args[0]
   106  
   107  	imageRef, err := alltransports.ParseImageName(imageName)
   108  	if err != nil {
   109  		imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s://%s", docker.Transport.Name(), imageName))
   110  		if err != nil {
   111  			return errors.Errorf("invalid image reference %q", imageName)
   112  		}
   113  	}
   114  
   115  	var writer io.Writer
   116  	if !c.Quiet {
   117  		writer = os.Stderr
   118  	}
   119  	// Special-case for docker-archive which allows multiple tags.
   120  	if imageRef.Transport().Name() == dockerarchive.Transport.Name() {
   121  		newImage, err := runtime.LoadFromArchiveReference(getContext(), imageRef, c.SignaturePolicy, writer)
   122  		if err != nil {
   123  			return errors.Wrapf(err, "error pulling image %q", imageName)
   124  		}
   125  		fmt.Println(newImage[0].ID())
   126  		return nil
   127  	}
   128  
   129  	var registryCreds *types.DockerAuthConfig
   130  	if c.Flag("creds").Changed {
   131  		creds, err := util.ParseRegistryCreds(c.Creds)
   132  		if err != nil {
   133  			return err
   134  		}
   135  		registryCreds = creds
   136  	}
   137  	dockerRegistryOptions := image.DockerRegistryOptions{
   138  		DockerRegistryCreds: registryCreds,
   139  		DockerCertPath:      c.CertDir,
   140  		OSChoice:            c.OverrideOS,
   141  		ArchitectureChoice:  c.OverrideArch,
   142  	}
   143  	if c.IsSet("tls-verify") {
   144  		dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.TlsVerify)
   145  	}
   146  
   147  	if !c.Bool("all-tags") {
   148  		newImage, err := runtime.New(getContext(), imageName, c.SignaturePolicy, c.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageAlways)
   149  		if err != nil {
   150  			return errors.Wrapf(err, "error pulling image %q", imageName)
   151  		}
   152  		fmt.Println(newImage.ID())
   153  		return nil
   154  	}
   155  
   156  	// --all-tags requires the docker transport
   157  	if imageRef.Transport().Name() != docker.Transport.Name() {
   158  		return errors.New("--all-tags requires docker transport")
   159  	}
   160  
   161  	// all-tags doesn't work with a tagged reference, so let's check early
   162  	namedRef, err := reference.Parse(imageName)
   163  	if err != nil {
   164  		return errors.Wrapf(err, "error parsing %q", imageName)
   165  	}
   166  	if _, isTagged := namedRef.(reference.Tagged); isTagged {
   167  		return errors.New("--all-tags requires a reference without a tag")
   168  
   169  	}
   170  
   171  	systemContext := image.GetSystemContext("", c.Authfile, false)
   172  	tags, err := docker.GetRepositoryTags(ctx, systemContext, imageRef)
   173  	if err != nil {
   174  		return errors.Wrapf(err, "error getting repository tags")
   175  	}
   176  
   177  	var foundIDs []string
   178  	for _, tag := range tags {
   179  		name := imageName + ":" + tag
   180  		newImage, err := runtime.New(getContext(), name, c.SignaturePolicy, c.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageAlways)
   181  		if err != nil {
   182  			logrus.Errorf("error pulling image %q", name)
   183  			continue
   184  		}
   185  		foundIDs = append(foundIDs, newImage.ID())
   186  	}
   187  
   188  	if len(tags) != len(foundIDs) {
   189  		return errors.Errorf("error pulling image %q", imageName)
   190  	}
   191  
   192  	if len(foundIDs) > 1 {
   193  		fmt.Println("Pulled Images:")
   194  	}
   195  	for _, id := range foundIDs {
   196  		fmt.Println(id)
   197  	}
   198  
   199  	return nil
   200  }