diff options
-rw-r--r-- | cmd/podman/images/list.go | 15 | ||||
-rw-r--r-- | cmd/podman/images/pull.go | 13 | ||||
-rw-r--r-- | cmd/podman/pods/create.go | 19 | ||||
-rw-r--r-- | cmd/podman/root.go | 4 | ||||
-rw-r--r-- | cmd/podman/system/version.go | 5 | ||||
-rw-r--r-- | go.mod | 2 | ||||
-rw-r--r-- | go.sum | 4 | ||||
-rw-r--r-- | libpod/image/utils.go | 27 | ||||
-rw-r--r-- | libpod/options.go | 30 | ||||
-rw-r--r-- | libpod/pod.go | 2 | ||||
-rw-r--r-- | libpod/rootless_cni_linux.go | 2 | ||||
-rw-r--r-- | libpod/runtime_pod_infra_linux.go | 25 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/images.go | 120 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/images_pull.go | 193 | ||||
-rw-r--r-- | pkg/bindings/images/images.go | 45 | ||||
-rw-r--r-- | pkg/bindings/images/pull.go | 98 | ||||
-rw-r--r-- | pkg/domain/entities/images.go | 17 | ||||
-rw-r--r-- | pkg/domain/infra/abi/images_list.go | 24 | ||||
-rw-r--r-- | pkg/domain/infra/abi/play.go | 5 | ||||
-rw-r--r-- | pkg/domain/infra/runtime_libpod.go | 17 | ||||
-rw-r--r-- | pkg/specgen/generate/pod_create.go | 9 | ||||
-rw-r--r-- | pkg/specgen/pod_validate.go | 7 | ||||
-rw-r--r-- | test/e2e/images_test.go | 32 | ||||
-rw-r--r-- | test/e2e/play_kube_test.go | 74 | ||||
-rw-r--r-- | test/e2e/pod_create_test.go | 76 | ||||
-rw-r--r-- | vendor/modules.txt | 2 |
26 files changed, 587 insertions, 280 deletions
diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go index 043871a8c..ffb341fc4 100644 --- a/cmd/podman/images/list.go +++ b/cmd/podman/images/list.go @@ -184,13 +184,26 @@ func sortImages(imageS []*entities.ImageSummary) ([]imageReporter, error) { for _, e := range imageS { var h imageReporter if len(e.RepoTags) > 0 { + tagged := []imageReporter{} + untagged := []imageReporter{} for _, tag := range e.RepoTags { h.ImageSummary = *e h.Repository, h.Tag, err = tokenRepoTag(tag) if err != nil { return nil, errors.Wrapf(err, "error parsing repository tag %q:", tag) } - imgs = append(imgs, h) + if h.Tag == "<none>" { + untagged = append(untagged, h) + } else { + tagged = append(tagged, h) + } + } + // Note: we only want to display "<none>" if we + // couldn't find any tagged name in RepoTags. + if len(tagged) > 0 { + imgs = append(imgs, tagged...) + } else { + imgs = append(imgs, untagged[0]) } } else { h.ImageSummary = *e diff --git a/cmd/podman/images/pull.go b/cmd/podman/images/pull.go index d86f9800c..448543b4d 100644 --- a/cmd/podman/images/pull.go +++ b/cmd/podman/images/pull.go @@ -44,10 +44,10 @@ var ( // child of the images command. imagesPullCmd = &cobra.Command{ Use: pullCmd.Use, + Args: pullCmd.Args, Short: pullCmd.Short, Long: pullCmd.Long, RunE: pullCmd.RunE, - Args: cobra.ExactArgs(1), Example: `podman image pull imageName podman image pull fedora:latest`, } @@ -77,8 +77,6 @@ func init() { // pullFlags set the flags for the pull command. func pullFlags(flags *pflag.FlagSet) { flags.BoolVar(&pullOptions.AllTags, "all-tags", false, "All tagged images in the repository will be pulled") - flags.StringVar(&pullOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") - flags.StringVar(&pullOptions.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") flags.StringVar(&pullOptions.CredentialsCLI, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") flags.StringVar(&pullOptions.OverrideArch, "override-arch", "", "Use `ARCH` instead of the architecture of the machine for choosing images") flags.StringVar(&pullOptions.OverrideOS, "override-os", "", "Use `OS` instead of the running OS for choosing images") @@ -86,12 +84,11 @@ func pullFlags(flags *pflag.FlagSet) { flags.Bool("disable-content-trust", false, "This is a Docker specific option and is a NOOP") flags.BoolVarP(&pullOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images") flags.StringVar(&pullOptions.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") - flags.BoolVar(&pullOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") - if registry.IsRemote() { - _ = flags.MarkHidden("authfile") - _ = flags.MarkHidden("cert-dir") - _ = flags.MarkHidden("tls-verify") + if !registry.IsRemote() { + flags.StringVar(&pullOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&pullOptions.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") + flags.BoolVar(&pullOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") } _ = flags.MarkHidden("signature-policy") } diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index 603ddcaca..d8d32b930 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -55,8 +55,8 @@ func init() { flags.StringVar(&createOptions.CGroupParent, "cgroup-parent", "", "Set parent cgroup for the pod") flags.BoolVar(&createOptions.Infra, "infra", true, "Create an infra container associated with the pod to share namespaces with") flags.StringVar(&createOptions.InfraConmonPidFile, "infra-conmon-pidfile", "", "Path to the file that will receive the POD of the infra container's conmon") - flags.StringVar(&createOptions.InfraImage, "infra-image", containerConfig.Engine.InfraImage, "The image of the infra container to associate with the pod") - flags.StringVar(&createOptions.InfraCommand, "infra-command", containerConfig.Engine.InfraCommand, "The command to run on the infra container when the pod is started") + flags.String("infra-image", containerConfig.Engine.InfraImage, "The image of the infra container to associate with the pod") + flags.String("infra-command", containerConfig.Engine.InfraCommand, "The command to run on the infra container when the pod is started") flags.StringSliceVar(&labelFile, "label-file", []string{}, "Read in a line delimited file of labels") flags.StringSliceVarP(&labels, "label", "l", []string{}, "Set metadata on pod (default [])") flags.StringVarP(&createOptions.Name, "name", "n", "", "Assign a name to the pod") @@ -92,7 +92,6 @@ func create(cmd *cobra.Command, args []string) error { if cmd.Flag("infra-command").Changed { return errors.New("cannot set infra-command without an infra container") } - createOptions.InfraCommand = "" if cmd.Flag("infra-image").Changed { return errors.New("cannot set infra-image without an infra container") } @@ -104,6 +103,20 @@ func create(cmd *cobra.Command, args []string) error { createOptions.Share = nil } else { createOptions.Share = strings.Split(share, ",") + if cmd.Flag("infra-command").Changed { + // Only send content to server side if user changed defaults + createOptions.InfraCommand, err = cmd.Flags().GetString("infra-command") + if err != nil { + return err + } + } + if cmd.Flag("infra-image").Changed { + // Only send content to server side if user changed defaults + createOptions.InfraImage, err = cmd.Flags().GetString("infra-image") + if err != nil { + return err + } + } } if cmd.Flag("pod-id-file").Changed { diff --git a/cmd/podman/root.go b/cmd/podman/root.go index d079018a0..8def7112f 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -80,10 +80,6 @@ func init() { ) rootFlags(rootCmd, registry.PodmanConfig()) - - // "version" is a local flag to avoid collisions with sub-commands that use "-v" - var dummyVersion bool - rootCmd.Flags().BoolVarP(&dummyVersion, "version", "v", false, "Version of Podman") } func Execute() { diff --git a/cmd/podman/system/version.go b/cmd/podman/system/version.go index edc860d0e..5f39b4820 100644 --- a/cmd/podman/system/version.go +++ b/cmd/podman/system/version.go @@ -47,12 +47,9 @@ func version(cmd *cobra.Command, args []string) error { if err != nil { return err } - _, err = io.WriteString(os.Stdout, s) + _, err = io.WriteString(os.Stdout, s+"\n") return err case cmd.Flag("format").Changed: - if !strings.HasSuffix(versionFormat, "\n") { - versionFormat += "\n" - } out := formats.StdoutTemplate{Output: versions, Template: versionFormat} err := out.Out() if err != nil { @@ -62,7 +62,7 @@ require ( golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed k8s.io/api v0.0.0-20190620084959-7cf5895f2711 - k8s.io/apimachinery v0.19.1 + k8s.io/apimachinery v0.19.2 k8s.io/client-go v0.0.0-20190620085101-78d2af792bab ) @@ -640,8 +640,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh k8s.io/api v0.0.0-20190620084959-7cf5895f2711 h1:BblVYz/wE5WtBsD/Gvu54KyBUTJMflolzc5I2DTvh50= k8s.io/api v0.0.0-20190620084959-7cf5895f2711/go.mod h1:TBhBqb1AWbBQbW3XRusr7n7E4v2+5ZY8r8sAMnyFC5A= k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719/go.mod h1:I4A+glKBHiTgiEjQiCCQfCAIcIMFGt291SmsvcrFzJA= -k8s.io/apimachinery v0.19.1 h1:cwsxZazM/LA9aUsBaL4bRS5ygoM6bYp8dFk22DSYQa4= -k8s.io/apimachinery v0.19.1/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= +k8s.io/apimachinery v0.19.2 h1:5Gy9vQpAGTKHPVOh5c4plE274X8D/6cuEiTO2zve7tc= +k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= k8s.io/client-go v0.0.0-20190620085101-78d2af792bab h1:E8Fecph0qbNsAbijJJQryKu4Oi9QTp5cVpjTE+nqg6g= k8s.io/client-go v0.0.0-20190620085101-78d2af792bab/go.mod h1:E95RaSlHr79aHaX0aGSwcPNfygDiPKOVXdmivCIZT0k= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= diff --git a/libpod/image/utils.go b/libpod/image/utils.go index b7ea63c66..918314476 100644 --- a/libpod/image/utils.go +++ b/libpod/image/utils.go @@ -86,33 +86,6 @@ func hasTransport(image string) bool { return strings.Contains(image, "://") } -// ReposToMap parses the specified repotags and returns a map with repositories -// as keys and the corresponding arrays of tags or digests-as-strings as values. -func ReposToMap(names []string) (map[string][]string, error) { - // map format is repo -> []tag-or-digest - repos := make(map[string][]string) - for _, name := range names { - var repository, tag string - if len(name) > 0 { - named, err := reference.ParseNormalizedNamed(name) - if err != nil { - return nil, err - } - repository = named.Name() - if ref, ok := named.(reference.NamedTagged); ok { - tag = ref.Tag() - } else if ref, ok := named.(reference.Canonical); ok { - tag = ref.Digest().String() - } - } - repos[repository] = append(repos[repository], tag) - } - if len(repos) == 0 { - repos["<none>"] = []string{"<none>"} - } - return repos, nil -} - // GetAdditionalTags returns a list of reference.NamedTagged for the // additional tags given in images func GetAdditionalTags(images []string) ([]reference.NamedTagged, error) { diff --git a/libpod/options.go b/libpod/options.go index d592124bc..f7b3419e5 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1659,6 +1659,36 @@ func WithUmask(umask string) CtrCreateOption { // Pod Creation Options +// WithInfraImage sets the infra image for libpod. +// An infra image is used for inter-container kernel +// namespace sharing within a pod. Typically, an infra +// container is lightweight and is there to reap +// zombie processes within its pid namespace. +func WithInfraImage(img string) PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + + pod.config.InfraContainer.InfraImage = img + + return nil + } +} + +// WithInfraCommand sets the command to +// run on pause container start up. +func WithInfraCommand(cmd []string) PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + + pod.config.InfraContainer.InfraCommand = cmd + return nil + } +} + // WithPodName sets the name of the pod. func WithPodName(name string) PodCreateOption { return func(pod *Pod) error { diff --git a/libpod/pod.go b/libpod/pod.go index 422966b94..709184008 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -105,6 +105,8 @@ type InfraContainerConfig struct { HostAdd []string `json:"hostsAdd,omitempty"` Networks []string `json:"networks,omitempty"` ExitCommand []string `json:"exitCommand,omitempty"` + InfraImage string `json:"infraImage,omitempty"` + InfraCommand []string `json:"infraCommand,omitempty"` } // ID retrieves the pod's ID diff --git a/libpod/rootless_cni_linux.go b/libpod/rootless_cni_linux.go index 31097dd16..7feec6b44 100644 --- a/libpod/rootless_cni_linux.go +++ b/libpod/rootless_cni_linux.go @@ -255,7 +255,7 @@ func startRootlessCNIInfraContainer(ctx context.Context, r *Runtime) (*Container Destination: "/etc/cni/net.d", Type: "bind", Source: r.config.Network.NetworkConfigDir, - Options: []string{"ro"}, + Options: []string{"ro", "bind"}, } g.AddMount(etcCNINetD) diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index b2f21d946..164068638 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -36,22 +36,26 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm isRootless := rootless.IsRootless() - entryCmd := []string{r.config.Engine.InfraCommand} + entrypointSet := len(p.config.InfraContainer.InfraCommand) > 0 + entryPoint := p.config.InfraContainer.InfraCommand + entryCmd := []string{} var options []CtrCreateOption // I've seen circumstances where config is being passed as nil. // Let's err on the side of safety and make sure it's safe to use. if config != nil { - setEntrypoint := false // default to entrypoint in image if there is one - if len(config.Entrypoint) > 0 { - entryCmd = config.Entrypoint - setEntrypoint = true + if !entrypointSet { + if len(config.Entrypoint) > 0 { + entrypointSet = true + entryPoint = config.Entrypoint + entryCmd = config.Entrypoint + } } if len(config.Cmd) > 0 { // We can't use the default pause command, since we're // sourcing from the image. If we didn't already set an // entrypoint, set one now. - if !setEntrypoint { + if !entrypointSet { // Use the Docker default "/bin/sh -c" // entrypoint, as we're overriding command. // If an image doesn't want this, it can @@ -136,6 +140,9 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm options = append(options, WithRootFSFromImage(imgID, imgName, rawImageName)) options = append(options, WithName(containerName)) options = append(options, withIsInfra()) + if entrypointSet { + options = append(options, WithEntrypoint(entryPoint)) + } if len(p.config.InfraContainer.ConmonPidFile) > 0 { options = append(options, WithConmonPidFile(p.config.InfraContainer.ConmonPidFile)) } @@ -151,7 +158,11 @@ func (r *Runtime) createInfraContainer(ctx context.Context, p *Pod) (*Container, return nil, define.ErrRuntimeStopped } - newImage, err := r.ImageRuntime().New(ctx, r.config.Engine.InfraImage, "", "", nil, nil, image.SigningOptions{}, nil, util.PullImageMissing) + img := p.config.InfraContainer.InfraImage + if img == "" { + img = r.config.Engine.InfraImage + } + newImage, err := r.ImageRuntime().New(ctx, img, "", "", nil, nil, image.SigningOptions{}, nil, util.PullImageMissing) if err != nil { return nil, err } diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 1da41ad88..bc1bdc287 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -11,8 +11,6 @@ import ( "strings" "github.com/containers/buildah" - "github.com/containers/image/v5/docker" - "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/types" "github.com/containers/podman/v2/libpod" @@ -25,7 +23,6 @@ import ( "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/domain/infra/abi" "github.com/containers/podman/v2/pkg/errorhandling" - "github.com/containers/podman/v2/pkg/util" utils2 "github.com/containers/podman/v2/utils" "github.com/gorilla/schema" "github.com/pkg/errors" @@ -400,123 +397,6 @@ func ImagesImport(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, entities.ImageImportReport{Id: importedImage}) } -// ImagesPull is the v2 libpod endpoint for pulling images. Note that the -// mandatory `reference` must be a reference to a registry (i.e., of docker -// transport or be normalized to one). Other transports are rejected as they -// do not make sense in a remote context. -func ImagesPull(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) - query := struct { - Reference string `schema:"reference"` - OverrideOS string `schema:"overrideOS"` - OverrideArch string `schema:"overrideArch"` - OverrideVariant string `schema:"overrideVariant"` - TLSVerify bool `schema:"tlsVerify"` - AllTags bool `schema:"allTags"` - }{ - TLSVerify: true, - } - - if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, - errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) - return - } - - if len(query.Reference) == 0 { - utils.InternalServerError(w, errors.New("reference parameter cannot be empty")) - return - } - - imageRef, err := utils.ParseDockerReference(query.Reference) - if err != nil { - utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, - errors.Wrapf(err, "image destination %q is not a docker-transport reference", query.Reference)) - return - } - - // Trim the docker-transport prefix. - rawImage := strings.TrimPrefix(query.Reference, fmt.Sprintf("%s://", docker.Transport.Name())) - - // all-tags doesn't work with a tagged reference, so let's check early - namedRef, err := reference.Parse(rawImage) - if err != nil { - utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, - errors.Wrapf(err, "error parsing reference %q", rawImage)) - return - } - if _, isTagged := namedRef.(reference.Tagged); isTagged && query.AllTags { - utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, - errors.Errorf("reference %q must not have a tag for all-tags", rawImage)) - return - } - - authConf, authfile, 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", auth.XRegistryAuthHeader, r.URL.String())) - return - } - defer auth.RemoveAuthfile(authfile) - - // Setup the registry options - dockerRegistryOptions := image.DockerRegistryOptions{ - DockerRegistryCreds: authConf, - OSChoice: query.OverrideOS, - ArchitectureChoice: query.OverrideArch, - VariantChoice: query.OverrideVariant, - } - if _, found := r.URL.Query()["tlsVerify"]; found { - dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) - } - - sys := runtime.SystemContext() - if sys == nil { - sys = image.GetSystemContext("", authfile, false) - } - dockerRegistryOptions.DockerCertPath = sys.DockerCertPath - sys.DockerAuthConfig = authConf - - // Prepare the images we want to pull - imagesToPull := []string{} - res := []handlers.LibpodImagesPullReport{} - imageName := namedRef.String() - - if !query.AllTags { - imagesToPull = append(imagesToPull, imageName) - } else { - tags, err := docker.GetRepositoryTags(context.Background(), sys, imageRef) - if err != nil { - utils.InternalServerError(w, errors.Wrap(err, "error getting repository tags")) - return - } - for _, tag := range tags { - imagesToPull = append(imagesToPull, fmt.Sprintf("%s:%s", imageName, tag)) - } - } - - // Finally pull the images - for _, img := range imagesToPull { - newImage, err := runtime.ImageRuntime().New( - context.Background(), - img, - "", - authfile, - os.Stderr, - &dockerRegistryOptions, - image.SigningOptions{}, - nil, - util.PullImageAlways) - if err != nil { - utils.InternalServerError(w, err) - return - } - res = append(res, handlers.LibpodImagesPullReport{ID: newImage.ID()}) - } - - utils.WriteResponse(w, http.StatusOK, res) -} - // PushImage is the handler for the compat http endpoint for pushing images. func PushImage(w http.ResponseWriter, r *http.Request) { decoder := r.Context().Value("decoder").(*schema.Decoder) diff --git a/pkg/api/handlers/libpod/images_pull.go b/pkg/api/handlers/libpod/images_pull.go new file mode 100644 index 000000000..8a2f4f4cf --- /dev/null +++ b/pkg/api/handlers/libpod/images_pull.go @@ -0,0 +1,193 @@ +package libpod + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "strings" + + "github.com/containers/image/v5/docker" + "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/types" + "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/channel" + "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/util" + "github.com/gorilla/schema" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// ImagesPull is the v2 libpod endpoint for pulling images. Note that the +// mandatory `reference` must be a reference to a registry (i.e., of docker +// transport or be normalized to one). Other transports are rejected as they +// do not make sense in a remote context. +func ImagesPull(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value("decoder").(*schema.Decoder) + query := struct { + Reference string `schema:"reference"` + OverrideOS string `schema:"overrideOS"` + OverrideArch string `schema:"overrideArch"` + OverrideVariant string `schema:"overrideVariant"` + TLSVerify bool `schema:"tlsVerify"` + AllTags bool `schema:"allTags"` + }{ + TLSVerify: true, + } + + if err := decoder.Decode(&query, r.URL.Query()); err != nil { + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, + errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + return + } + + if len(query.Reference) == 0 { + utils.InternalServerError(w, errors.New("reference parameter cannot be empty")) + return + } + + imageRef, err := utils.ParseDockerReference(query.Reference) + if err != nil { + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, + errors.Wrapf(err, "image destination %q is not a docker-transport reference", query.Reference)) + return + } + + // Trim the docker-transport prefix. + rawImage := strings.TrimPrefix(query.Reference, fmt.Sprintf("%s://", docker.Transport.Name())) + + // all-tags doesn't work with a tagged reference, so let's check early + namedRef, err := reference.Parse(rawImage) + if err != nil { + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, + errors.Wrapf(err, "error parsing reference %q", rawImage)) + return + } + if _, isTagged := namedRef.(reference.Tagged); isTagged && query.AllTags { + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, + errors.Errorf("reference %q must not have a tag for all-tags", rawImage)) + return + } + + authConf, authfile, 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", auth.XRegistryAuthHeader, r.URL.String())) + return + } + defer auth.RemoveAuthfile(authfile) + + // Setup the registry options + dockerRegistryOptions := image.DockerRegistryOptions{ + DockerRegistryCreds: authConf, + OSChoice: query.OverrideOS, + ArchitectureChoice: query.OverrideArch, + VariantChoice: query.OverrideVariant, + } + if _, found := r.URL.Query()["tlsVerify"]; found { + dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) + } + + sys := runtime.SystemContext() + if sys == nil { + sys = image.GetSystemContext("", authfile, false) + } + dockerRegistryOptions.DockerCertPath = sys.DockerCertPath + sys.DockerAuthConfig = authConf + + // Prepare the images we want to pull + imagesToPull := []string{} + imageName := namedRef.String() + + if !query.AllTags { + imagesToPull = append(imagesToPull, imageName) + } else { + tags, err := docker.GetRepositoryTags(context.Background(), sys, imageRef) + if err != nil { + utils.InternalServerError(w, errors.Wrap(err, "error getting repository tags")) + return + } + for _, tag := range tags { + imagesToPull = append(imagesToPull, fmt.Sprintf("%s:%s", imageName, tag)) + } + } + + writer := channel.NewWriter(make(chan []byte, 1)) + defer writer.Close() + + stderr := channel.NewWriter(make(chan []byte, 1)) + defer stderr.Close() + + images := make([]string, 0, len(imagesToPull)) + runCtx, cancel := context.WithCancel(context.Background()) + go func(imgs []string) { + defer cancel() + // Finally pull the images + for _, img := range imgs { + newImage, err := runtime.ImageRuntime().New( + runCtx, + img, + "", + authfile, + writer, + &dockerRegistryOptions, + image.SigningOptions{}, + nil, + util.PullImageAlways) + if err != nil { + stderr.Write([]byte(err.Error() + "\n")) + } else { + images = append(images, newImage.ID()) + } + } + }(imagesToPull) + + flush := func() { + if flusher, ok := w.(http.Flusher); ok { + flusher.Flush() + } + } + + w.WriteHeader(http.StatusOK) + w.Header().Add("Content-Type", "application/json") + flush() + + enc := json.NewEncoder(w) + enc.SetEscapeHTML(true) + var failed bool +loop: // break out of for/select infinite loop + for { + var report entities.ImagePullReport + select { + case e := <-writer.Chan(): + report.Stream = string(e) + if err := enc.Encode(report); err != nil { + stderr.Write([]byte(err.Error())) + } + flush() + case e := <-stderr.Chan(): + failed = true + report.Error = string(e) + if err := enc.Encode(report); err != nil { + logrus.Warnf("Failed to json encode error %q", err.Error()) + } + flush() + case <-runCtx.Done(): + if !failed { + report.Images = images + if err := enc.Encode(report); err != nil { + logrus.Warnf("Failed to json encode error %q", err.Error()) + } + flush() + } + break loop // break out of for/select infinite loop + case <-r.Context().Done(): + // Client has closed connection + break loop // break out of for/select infinite loop + } + } +} diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index 05ab25d5b..596491044 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -270,51 +270,6 @@ func Import(ctx context.Context, changes []string, message, reference, u *string return &report, response.Process(&report) } -// Pull is the binding for libpod's v2 endpoints for pulling images. Note that -// `rawImage` must be a reference to a registry (i.e., of docker transport or be -// normalized to one). Other transports are rejected as they do not make sense -// in a remote context. -func Pull(ctx context.Context, rawImage string, options entities.ImagePullOptions) ([]string, error) { - conn, err := bindings.GetClient(ctx) - if err != nil { - return nil, err - } - params := url.Values{} - params.Set("reference", rawImage) - params.Set("overrideArch", options.OverrideArch) - params.Set("overrideOS", options.OverrideOS) - params.Set("overrideVariant", options.OverrideVariant) - if options.SkipTLSVerify != types.OptionalBoolUndefined { - // Note: we have to verify if skipped is false. - verifyTLS := bool(options.SkipTLSVerify == types.OptionalBoolFalse) - params.Set("tlsVerify", strconv.FormatBool(verifyTLS)) - } - params.Set("allTags", strconv.FormatBool(options.AllTags)) - - // TODO: have a global system context we can pass around (1st argument) - header, err := auth.Header(nil, options.Authfile, options.Username, options.Password) - if err != nil { - return nil, err - } - - response, err := conn.DoRequest(nil, http.MethodPost, "/images/pull", params, header) - if err != nil { - return nil, err - } - - reports := []handlers.LibpodImagesPullReport{} - if err := response.Process(&reports); err != nil { - return nil, err - } - - pulledImages := []string{} - for _, r := range reports { - pulledImages = append(pulledImages, r.ID) - } - - return pulledImages, nil -} - // Push is the binding for libpod's v2 endpoints for push images. Note that // `source` must be a referring to an image in the remote's container storage. // The destination must be a reference to a registry (i.e., of docker transport diff --git a/pkg/bindings/images/pull.go b/pkg/bindings/images/pull.go new file mode 100644 index 000000000..261a481a2 --- /dev/null +++ b/pkg/bindings/images/pull.go @@ -0,0 +1,98 @@ +package images + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "os" + "strconv" + + "github.com/containers/image/v5/types" + "github.com/containers/podman/v2/pkg/auth" + "github.com/containers/podman/v2/pkg/bindings" + "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/hashicorp/go-multierror" +) + +// Pull is the binding for libpod's v2 endpoints for pulling images. Note that +// `rawImage` must be a reference to a registry (i.e., of docker transport or be +// normalized to one). Other transports are rejected as they do not make sense +// in a remote context. Progress reported on stderr +func Pull(ctx context.Context, rawImage string, options entities.ImagePullOptions) ([]string, error) { + conn, err := bindings.GetClient(ctx) + if err != nil { + return nil, err + } + params := url.Values{} + params.Set("reference", rawImage) + params.Set("overrideArch", options.OverrideArch) + params.Set("overrideOS", options.OverrideOS) + params.Set("overrideVariant", options.OverrideVariant) + + if options.SkipTLSVerify != types.OptionalBoolUndefined { + // Note: we have to verify if skipped is false. + verifyTLS := bool(options.SkipTLSVerify == types.OptionalBoolFalse) + params.Set("tlsVerify", strconv.FormatBool(verifyTLS)) + } + params.Set("allTags", strconv.FormatBool(options.AllTags)) + + // TODO: have a global system context we can pass around (1st argument) + header, err := auth.Header(nil, options.Authfile, options.Username, options.Password) + if err != nil { + return nil, err + } + + response, err := conn.DoRequest(nil, http.MethodPost, "/images/pull", params, header) + if err != nil { + return nil, err + } + defer response.Body.Close() + + if !response.IsSuccess() { + return nil, response.Process(err) + } + + // Historically pull writes status to stderr + stderr := io.Writer(os.Stderr) + if options.Quiet { + stderr = ioutil.Discard + } + + dec := json.NewDecoder(response.Body) + var images []string + var mErr error + for { + var report entities.ImagePullReport + if err := dec.Decode(&report); err != nil { + if errors.Is(err, io.EOF) { + break + } + report.Error = err.Error() + "\n" + } + + select { + case <-response.Request.Context().Done(): + return images, mErr + default: + // non-blocking select + } + + switch { + case report.Stream != "": + fmt.Fprint(stderr, report.Stream) + case report.Error != "": + mErr = multierror.Append(mErr, errors.New(report.Error)) + case len(report.Images) > 0: + images = report.Images + default: + return images, errors.New("failed to parse pull results stream, unexpected input") + } + + } + return images, mErr +} diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 3a2e762d6..d0b738934 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -45,7 +45,7 @@ type Image struct { HealthCheck *manifest.Schema2HealthConfig `json:",omitempty"` } -func (i *Image) Id() string { //nolint +func (i *Image) Id() string { // nolint return i.ID } @@ -70,7 +70,7 @@ type ImageSummary struct { History []string `json:",omitempty"` } -func (i *ImageSummary) Id() string { //nolint +func (i *ImageSummary) Id() string { // nolint return i.ID } @@ -150,7 +150,12 @@ type ImagePullOptions struct { // ImagePullReport is the response from pulling one or more images. type ImagePullReport struct { - Images []string + // Stream used to provide output from c/image + Stream string `json:"stream,omitempty"` + // Error contains text of errors from c/image + Error string `json:"error,omitempty"` + // Images contains the ID's of the images pulled + Images []string `json:"images,omitempty"` } // ImagePushOptions are the arguments for pushing images. @@ -269,7 +274,7 @@ type ImageImportOptions struct { } type ImageImportReport struct { - Id string //nolint + Id string // nolint } // ImageSaveOptions provide options for saving images. @@ -349,7 +354,7 @@ type ImageUnmountOptions struct { // ImageMountReport describes the response from image mount type ImageMountReport struct { Err error - Id string //nolint + Id string // nolint Name string Repositories []string Path string @@ -358,5 +363,5 @@ type ImageMountReport struct { // ImageUnmountReport describes the response from umounting an image type ImageUnmountReport struct { Err error - Id string //nolint + Id string // nolint } diff --git a/pkg/domain/infra/abi/images_list.go b/pkg/domain/infra/abi/images_list.go index 7ec84246d..3e47dc67a 100644 --- a/pkg/domain/infra/abi/images_list.go +++ b/pkg/domain/infra/abi/images_list.go @@ -23,33 +23,13 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) summaries := []*entities.ImageSummary{} for _, img := range images { - var repoTags []string - if opts.All { - pairs, err := libpodImage.ReposToMap(img.Names()) - if err != nil { - return nil, err - } - - for repo, tags := range pairs { - for _, tag := range tags { - repoTags = append(repoTags, repo+":"+tag) - } - } - } else { - repoTags, err = img.RepoTags() - if err != nil { - return nil, err - } - } - digests := make([]string, len(img.Digests())) for j, d := range img.Digests() { digests[j] = string(d) } e := entities.ImageSummary{ - ID: img.ID(), - + ID: img.ID(), ConfigDigest: string(img.ConfigDigest), Created: img.Created().Unix(), Dangling: img.Dangling(), @@ -61,7 +41,7 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ReadOnly: img.IsReadOnly(), SharedSize: 0, VirtualSize: img.VirtualSize, - RepoTags: repoTags, + RepoTags: img.Names(), // may include tags and digests } e.Labels, _ = img.Labels(context.TODO()) diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 6dfb52c63..659cc799c 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -132,6 +132,11 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY libpod.WithInfraContainer(), libpod.WithPodName(podName), } + + if podYAML.ObjectMeta.Labels != nil { + podOptions = append(podOptions, libpod.WithPodLabels(podYAML.ObjectMeta.Labels)) + } + // TODO we only configure Process namespace. We also need to account for Host{IPC,Network,PID} // which is not currently possible with pod create if podYAML.Spec.ShareProcessNamespace != nil && *podYAML.Spec.ShareProcessNamespace { diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go index f9b8106ef..26c9c7e2e 100644 --- a/pkg/domain/infra/runtime_libpod.go +++ b/pkg/domain/infra/runtime_libpod.go @@ -227,23 +227,6 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo // TODO flag to set CNI plugins dir? - // TODO I don't think these belong here? - // Will follow up with a different PR to address - // - // Pod create options - - infraImageFlag := fs.Lookup("infra-image") - if infraImageFlag != nil && infraImageFlag.Changed { - infraImage, _ := fs.GetString("infra-image") - options = append(options, libpod.WithDefaultInfraImage(infraImage)) - } - - infraCommandFlag := fs.Lookup("infra-command") - if infraCommandFlag != nil && infraImageFlag.Changed { - infraCommand, _ := fs.GetString("infra-command") - options = append(options, libpod.WithDefaultInfraCommand(infraCommand)) - } - if !opts.withFDS { options = append(options, libpod.WithEnableSDNotify()) } diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index 0bd39d5a4..101201252 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -84,6 +84,15 @@ func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime) ([]libpod if len(p.CNINetworks) > 0 { options = append(options, libpod.WithPodNetworks(p.CNINetworks)) } + + if len(p.InfraImage) > 0 { + options = append(options, libpod.WithInfraImage(p.InfraImage)) + } + + if len(p.InfraCommand) > 0 { + options = append(options, libpod.WithInfraCommand(p.InfraCommand)) + } + switch p.NetNS.NSMode { case specgen.Bridge, specgen.Default, "": logrus.Debugf("Pod using default network mode") diff --git a/pkg/specgen/pod_validate.go b/pkg/specgen/pod_validate.go index d5e0aecf2..907c0bb69 100644 --- a/pkg/specgen/pod_validate.go +++ b/pkg/specgen/pod_validate.go @@ -95,12 +95,5 @@ func (p *PodSpecGenerator) Validate() error { return exclusivePodOptions("NoManageHosts", "HostAdd") } - // Set Defaults - if len(p.InfraImage) < 1 { - p.InfraImage = containerConfig.Engine.InfraImage - } - if len(p.InfraCommand) < 1 { - p.InfraCommand = []string{containerConfig.Engine.InfraCommand} - } return nil } diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go index a615a9f99..ddf2e20b8 100644 --- a/test/e2e/images_test.go +++ b/test/e2e/images_test.go @@ -241,6 +241,38 @@ WORKDIR /test Expect(result.OutputToStringArray()).Should(HaveLen(0), "dangling image output: %q", result.OutputToString()) }) + It("podman pull by digest and list --all", func() { + // Prevent regressing on issue #7651. + digestPullAndList := func(noneTag bool) { + session := podmanTest.Podman([]string{"pull", ALPINEAMD64DIGEST}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + result := podmanTest.Podman([]string{"images", "--all", ALPINEAMD64DIGEST}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + + found, _ := result.GrepString("<none>") + if noneTag { + Expect(found).To(BeTrue()) + } else { + Expect(found).To(BeFalse()) + } + } + // No "<none>" tag as tagged alpine instances should be present. + session := podmanTest.Podman([]string{"pull", ALPINELISTTAG}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + digestPullAndList(false) + + // Now remove all images, re-pull by digest and check for the "<none>" tag. + session = podmanTest.Podman([]string{"rmi", "-af"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + digestPullAndList(true) + }) + It("podman check for image with sha256: prefix", func() { session := podmanTest.Podman([]string{"inspect", "--format=json", ALPINE}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 5e01971cb..87de92357 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -30,9 +30,14 @@ apiVersion: v1 kind: Pod metadata: creationTimestamp: "2019-07-17T14:44:08Z" + name: {{ .Name }} labels: app: {{ .Name }} - name: {{ .Name }} +{{ with .Labels }} + {{ range $key, $value := . }} + {{ $key }}: {{ $value }} + {{ end }} +{{ end }} {{ with .Annotations }} annotations: {{ range $key, $value := . }} @@ -125,9 +130,14 @@ apiVersion: v1 kind: Deployment metadata: creationTimestamp: "2019-07-17T14:44:08Z" + name: {{ .Name }} labels: app: {{ .Name }} - name: {{ .Name }} +{{ with .Labels }} + {{ range $key, $value := . }} + {{ $key }}: {{ $value }} + {{ end }} +{{ end }} {{ with .Annotations }} annotations: {{ range $key, $value := . }} @@ -145,6 +155,9 @@ spec: metadata: labels: app: {{ .Name }} + {{- with .Labels }}{{ range $key, $value := . }} + {{ $key }}: {{ $value }} + {{- end }}{{ end }} {{ with .Annotations }} annotations: {{ range $key, $value := . }} @@ -266,6 +279,7 @@ type Pod struct { HostAliases []HostAlias Ctrs []*Ctr Volumes []*Volume + Labels map[string]string Annotations map[string]string } @@ -278,7 +292,15 @@ type HostAlias struct { // and the configured options // if no containers are added, it will add the default container func getPod(options ...podOption) *Pod { - p := Pod{defaultPodName, "", nil, make([]*Ctr, 0), make([]*Volume, 0), make(map[string]string)} + p := Pod{ + Name: defaultPodName, + Hostname: "", + HostAliases: nil, + Ctrs: make([]*Ctr, 0), + Volumes: make([]*Volume, 0), + Labels: make(map[string]string), + Annotations: make(map[string]string), + } for _, option := range options { option(&p) } @@ -311,6 +333,12 @@ func withCtr(c *Ctr) podOption { } } +func withLabel(k, v string) podOption { + return func(pod *Pod) { + pod.Labels[k] = v + } +} + func withAnnotation(k, v string) podOption { return func(pod *Pod) { pod.Annotations[k] = v @@ -327,12 +355,19 @@ func withVolume(v *Volume) podOption { type Deployment struct { Name string Replicas int32 + Labels map[string]string Annotations map[string]string PodTemplate *Pod } func getDeployment(options ...deploymentOption) *Deployment { - d := Deployment{defaultDeploymentName, 1, make(map[string]string), getPod()} + d := Deployment{ + Name: defaultDeploymentName, + Replicas: 1, + Labels: make(map[string]string), + Annotations: make(map[string]string), + PodTemplate: getPod(), + } for _, option := range options { option(&d) } @@ -342,6 +377,12 @@ func getDeployment(options ...deploymentOption) *Deployment { type deploymentOption func(*Deployment) +func withDeploymentLabel(k, v string) deploymentOption { + return func(deployment *Deployment) { + deployment.Labels[k] = v + } +} + func withDeploymentAnnotation(k, v string) deploymentOption { return func(deployment *Deployment) { deployment.Annotations[k] = v @@ -1077,4 +1118,29 @@ spec: correct := fmt.Sprintf("%s:%s:%s", hostPathLocation, hostPathLocation, "ro") Expect(inspect.OutputToString()).To(ContainSubstring(correct)) }) + + It("podman play kube applies labels to pods", func() { + SkipIfRemote() + var numReplicas int32 = 5 + expectedLabelKey := "key1" + expectedLabelValue := "value1" + deployment := getDeployment( + withReplicas(numReplicas), + withPod(getPod(withLabel(expectedLabelKey, expectedLabelValue))), + ) + err := generateDeploymentKubeYaml(deployment, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + correctLabels := expectedLabelKey + ":" + expectedLabelValue + for _, pod := range getPodNamesInDeployment(deployment) { + inspect := podmanTest.Podman([]string{"pod", "inspect", pod.Name, "--format", "'{{ .Labels }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(correctLabels)) + } + }) }) diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index f260a123a..ed62e8a4b 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -329,4 +329,80 @@ var _ = Describe("Podman pod create", func() { Expect(session.ExitCode()).To(Equal(0)) } }) + + It("podman create pod with defaults", func() { + name := "test" + session := podmanTest.Podman([]string{"pod", "create", "--name", name}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + check := podmanTest.Podman([]string{"pod", "inspect", name}) + check.WaitWithDefaultTimeout() + Expect(check.ExitCode()).To(Equal(0)) + data := check.InspectPodToJSON() + + check1 := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.Config.Entrypoint}}", data.Containers[0].ID}) + check1.WaitWithDefaultTimeout() + Expect(check1.ExitCode()).To(Equal(0)) + Expect(check1.OutputToString()).To(Equal("/pause")) + }) + + It("podman create pod with --infra-command", func() { + name := "test" + session := podmanTest.Podman([]string{"pod", "create", "--infra-command", "/pause1", "--name", name}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + check := podmanTest.Podman([]string{"pod", "inspect", name}) + check.WaitWithDefaultTimeout() + Expect(check.ExitCode()).To(Equal(0)) + data := check.InspectPodToJSON() + + check1 := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.Config.Entrypoint}}", data.Containers[0].ID}) + check1.WaitWithDefaultTimeout() + Expect(check1.ExitCode()).To(Equal(0)) + Expect(check1.OutputToString()).To(Equal("/pause1")) + }) + + It("podman create pod with --infra-image", func() { + dockerfile := `FROM docker.io/library/alpine:latest +entrypoint ["/fromimage"] +` + podmanTest.BuildImage(dockerfile, "localhost/infra", "false") + name := "test" + session := podmanTest.Podman([]string{"pod", "create", "--infra-image", "localhost/infra", "--name", name}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + check := podmanTest.Podman([]string{"pod", "inspect", name}) + check.WaitWithDefaultTimeout() + Expect(check.ExitCode()).To(Equal(0)) + data := check.InspectPodToJSON() + + check1 := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.Config.Entrypoint}}", data.Containers[0].ID}) + check1.WaitWithDefaultTimeout() + Expect(check1.ExitCode()).To(Equal(0)) + Expect(check1.OutputToString()).To(Equal("/fromimage")) + }) + + It("podman create pod with --infra-command --infra-image", func() { + dockerfile := `FROM docker.io/library/alpine:latest +entrypoint ["/fromimage"] +` + podmanTest.BuildImage(dockerfile, "localhost/infra", "false") + name := "test" + session := podmanTest.Podman([]string{"pod", "create", "--infra-image", "localhost/infra", "--infra-command", "/fromcommand", "--name", name}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + check := podmanTest.Podman([]string{"pod", "inspect", name}) + check.WaitWithDefaultTimeout() + Expect(check.ExitCode()).To(Equal(0)) + data := check.InspectPodToJSON() + + check1 := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.Config.Entrypoint}}", data.Containers[0].ID}) + check1.WaitWithDefaultTimeout() + Expect(check1.ExitCode()).To(Equal(0)) + Expect(check1.OutputToString()).To(Equal("/fromcommand")) + }) }) diff --git a/vendor/modules.txt b/vendor/modules.txt index 9959c9efb..186a85883 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -701,7 +701,7 @@ gopkg.in/yaml.v3 # k8s.io/api v0.0.0-20190620084959-7cf5895f2711 k8s.io/api/apps/v1 k8s.io/api/core/v1 -# k8s.io/apimachinery v0.19.1 +# k8s.io/apimachinery v0.19.2 k8s.io/apimachinery/pkg/api/errors k8s.io/apimachinery/pkg/api/resource k8s.io/apimachinery/pkg/apis/meta/v1 |