diff options
43 files changed, 377 insertions, 122 deletions
@@ -4,10 +4,8 @@ approvers: - giuseppe - jwhonce - mheon - - mrunalp - rhatdan - TomSweeneyRedHat - - umohnani8 - vrothberg reviewers: - baude @@ -15,8 +13,8 @@ reviewers: - giuseppe - jwhonce - mheon - - mrunalp - rhatdan - TomSweeneyRedHat - - umohnani8 - vrothberg + - ashley-cui + - QiWang19 diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index 2b6f9348e..cfbcf6140 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -416,6 +416,11 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "Size of /dev/shm "+sizeWithUnitFormat, ) createFlags.StringVar( + &cf.SignaturePolicy, + "signature-policy", "", + "`Pathname` of signature policy file (not usually used)", + ) + createFlags.StringVar( &cf.StopSignal, "stop-signal", "", "Signal to stop a container. Default is SIGTERM", diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index 1b0e64590..83a25f4ab 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -84,6 +84,7 @@ type ContainerCLIOpts struct { SecurityOpt []string SdNotifyMode string ShmSize string + SignaturePolicy string StopSignal string StopTimeout uint StoreageOpt []string diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index f9d33a223..96d94dc00 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -61,6 +61,7 @@ func createFlags(flags *pflag.FlagSet) { flags.AddFlagSet(common.GetNetFlags()) flags.SetNormalizeFunc(utils.AliasFlags) + _ = flags.MarkHidden("signature-policy") if registry.IsRemote() { _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("env-host") @@ -110,7 +111,9 @@ func create(cmd *cobra.Command, args []string) error { } imageName := args[0] + rawImageName := "" if !cliVals.RootFS { + rawImageName = args[0] name, err := pullImage(args[0]) if err != nil { return err @@ -121,6 +124,7 @@ func create(cmd *cobra.Command, args []string) error { if err := common.FillOutSpecGen(s, &cliVals, args); err != nil { return err } + s.RawImageName = rawImageName if _, err := createPodIfNecessary(s, cliVals.Net); err != nil { return err @@ -256,6 +260,7 @@ func pullImage(imageName string) (string, error) { OverrideArch: cliVals.OverrideArch, OverrideOS: cliVals.OverrideOS, OverrideVariant: cliVals.OverrideVariant, + SignaturePolicy: cliVals.SignaturePolicy, }) if pullErr != nil { return "", pullErr diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go index 34eea14e1..8052b033e 100644 --- a/cmd/podman/containers/run.go +++ b/cmd/podman/containers/run.go @@ -64,6 +64,7 @@ func runFlags(flags *pflag.FlagSet) { flags.BoolVar(&runRmi, "rmi", false, "Remove container image unless used by other containers") flags.UintVar(&runOpts.PreserveFDs, "preserve-fds", 0, "Pass a number of additional file descriptors into the container") + _ = flags.MarkHidden("signature-policy") if registry.IsRemote() { _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("env-host") @@ -130,7 +131,9 @@ func run(cmd *cobra.Command, args []string) error { } imageName := args[0] + rawImageName := "" if !cliVals.RootFS { + rawImageName = args[0] name, err := pullImage(args[0]) if err != nil { return err @@ -177,6 +180,7 @@ func run(cmd *cobra.Command, args []string) error { if err := common.FillOutSpecGen(s, &cliVals, args); err != nil { return err } + s.RawImageName = rawImageName runOpts.Spec = s if _, err := createPodIfNecessary(s, cliVals.Net); err != nil { diff --git a/cmd/podman/images/import.go b/cmd/podman/images/import.go index e605ddfc6..1c234e743 100644 --- a/cmd/podman/images/import.go +++ b/cmd/podman/images/import.go @@ -63,6 +63,8 @@ func importFlags(flags *pflag.FlagSet) { flags.StringArrayVarP(&importOpts.Changes, "change", "c", []string{}, "Apply the following possible instructions to the created image (default []): CMD | ENTRYPOINT | ENV | EXPOSE | LABEL | STOPSIGNAL | USER | VOLUME | WORKDIR") flags.StringVarP(&importOpts.Message, "message", "m", "", "Set commit message for imported image") flags.BoolVarP(&importOpts.Quiet, "quiet", "q", false, "Suppress output") + flags.StringVar(&importOpts.SignaturePolicy, "signature-policy", "", "Path to a signature-policy file") + _ = flags.MarkHidden("signature-policy") } func importCon(cmd *cobra.Command, args []string) error { 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/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 { @@ -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/container_internal_linux.go b/libpod/container_internal_linux.go index 3bdf28e8c..dde7cafb1 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -635,7 +635,7 @@ func (c *Container) setupSystemd(mounts []spec.Mount, g generate.Generator) erro Destination: "/sys/fs/cgroup/systemd", Type: "bind", Source: "/sys/fs/cgroup/systemd", - Options: []string{"bind", "nodev", "nosuid", "rprivate"}, + Options: []string{"bind", "nodev", "noexec", "nosuid", "rprivate"}, } g.AddMount(systemdMnt) g.AddLinuxMaskedPaths("/sys/fs/cgroup/systemd/release_agent") diff --git a/libpod/image/image.go b/libpod/image/image.go index 850a48eae..5dfb33afb 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -1284,7 +1284,7 @@ func (ir *Runtime) Import(ctx context.Context, path, reference string, writer io return nil, errors.Wrapf(err, "error updating image config") } - sc := GetSystemContext("", "", false) + sc := GetSystemContext(ir.SignaturePolicyPath, "", false) // if reference not given, get the image digest if reference == "" { diff --git a/libpod/image/pull.go b/libpod/image/pull.go index 94d6af4c2..65acdf427 100644 --- a/libpod/image/pull.go +++ b/libpod/image/pull.go @@ -255,6 +255,9 @@ func (ir *Runtime) pullImageFromHeuristicSource(ctx context.Context, inputName s sc.ArchitectureChoice = dockerOptions.ArchitectureChoice sc.VariantChoice = dockerOptions.VariantChoice } + if signaturePolicyPath == "" { + sc.SignaturePolicyPath = ir.SignaturePolicyPath + } sc.BlobInfoCacheDir = filepath.Join(ir.store.GraphRoot(), "cache") srcRef, err := alltransports.ParseImageName(inputName) if err != nil { 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_img.go b/libpod/runtime_img.go index eb4512f8d..e57890fa2 100644 --- a/libpod/runtime_img.go +++ b/libpod/runtime_img.go @@ -174,7 +174,7 @@ func (r *Runtime) Build(ctx context.Context, options imagebuildah.BuildOptions, } // Import is called as an intermediary to the image library Import -func (r *Runtime) Import(ctx context.Context, source string, reference string, changes []string, history string, quiet bool) (string, error) { +func (r *Runtime) Import(ctx context.Context, source, reference, signaturePolicyPath string, changes []string, history string, quiet bool) (string, error) { var ( writer io.Writer err error @@ -223,6 +223,7 @@ func (r *Runtime) Import(ctx context.Context, source string, reference string, c source = file } + r.imageRuntime.SignaturePolicyPath = signaturePolicyPath newImage, err := r.imageRuntime.Import(ctx, source, reference, writer, image.SigningOptions{}, config) if err != nil { return "", err 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/compat/images.go b/pkg/api/handlers/compat/images.go index 8765e20ca..c1ba9ca66 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -205,7 +205,7 @@ func CreateImageFromSrc(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to write temporary file")) } } - iid, err := runtime.Import(r.Context(), source, "", query.Changes, "", false) + iid, err := runtime.Import(r.Context(), source, "", "", query.Changes, "", false) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to import tarball")) return diff --git a/pkg/api/handlers/compat/ping.go b/pkg/api/handlers/compat/ping.go index eb7eed5b6..06150bb63 100644 --- a/pkg/api/handlers/compat/ping.go +++ b/pkg/api/handlers/compat/ping.go @@ -5,7 +5,6 @@ import ( "net/http" "github.com/containers/buildah" - "github.com/containers/podman/v2/pkg/api/handlers/utils" ) // Ping returns headers to client about the service @@ -14,13 +13,12 @@ import ( // Clients will use the Header availability to test which backend engine is in use. // Note: Additionally handler supports GET and HEAD methods func Ping(w http.ResponseWriter, r *http.Request) { - w.Header().Set("API-Version", utils.APIVersion[utils.CompatTree][utils.CurrentAPIVersion].String()) + // Note API-Version and Libpod-API-Version are set in handler_api.go w.Header().Set("BuildKit-Version", "") w.Header().Set("Docker-Experimental", "true") w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Pragma", "no-cache") - w.Header().Set("Libpod-API-Version", utils.APIVersion[utils.LibpodTree][utils.CurrentAPIVersion].String()) w.Header().Set("Libpod-Buildha-Version", buildah.Version) w.WriteHeader(http.StatusOK) diff --git a/pkg/api/handlers/compat/version.go b/pkg/api/handlers/compat/version.go index e12c7cefa..92900b75d 100644 --- a/pkg/api/handlers/compat/version.go +++ b/pkg/api/handlers/compat/version.go @@ -30,6 +30,7 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "Failed to obtain system memory info")) return } + components := []docker.ComponentVersion{{ Name: "Podman Engine", Version: versionInfo.Version, @@ -46,6 +47,9 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) { }, }} + apiVersion := utils.APIVersion[utils.CompatTree][utils.CurrentAPIVersion] + minVersion := utils.APIVersion[utils.CompatTree][utils.MinimalAPIVersion] + utils.WriteResponse(w, http.StatusOK, entities.ComponentVersion{ Version: docker.Version{ Platform: struct { @@ -53,7 +57,7 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) { }{ Name: fmt.Sprintf("%s/%s/%s-%s", goRuntime.GOOS, goRuntime.GOARCH, infoData.Host.Distribution.Distribution, infoData.Host.Distribution.Version), }, - APIVersion: components[0].Details["APIVersion"], + APIVersion: fmt.Sprintf("%d.%d", apiVersion.Major, apiVersion.Minor), Arch: components[0].Details["Arch"], BuildTime: components[0].Details["BuildTime"], Components: components, @@ -61,7 +65,7 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) { GitCommit: components[0].Details["GitCommit"], GoVersion: components[0].Details["GoVersion"], KernelVersion: components[0].Details["KernelVersion"], - MinAPIVersion: components[0].Details["MinAPIVersion"], + MinAPIVersion: fmt.Sprintf("%d.%d", minVersion.Major, minVersion.Minor), Os: components[0].Details["Os"], Version: components[0].Version, }}) diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 85f7903dc..1da41ad88 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -391,7 +391,7 @@ func ImagesImport(w http.ResponseWriter, r *http.Request) { tmpfile.Close() source = tmpfile.Name() } - importedImage, err := runtime.Import(context.Background(), source, query.Reference, query.Changes, query.Message, true) + importedImage, err := runtime.Import(context.Background(), source, query.Reference, "", query.Changes, query.Message, true) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to import image")) return diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go index 62fdc05dd..517dccad0 100644 --- a/pkg/api/handlers/utils/handler.go +++ b/pkg/api/handlers/utils/handler.go @@ -43,8 +43,8 @@ var ( // clients to shop for the Version they wish to support APIVersion = map[VersionTree]map[VersionLevel]semver.Version{ LibpodTree: { - CurrentAPIVersion: semver.MustParse("1.0.0"), - MinimalAPIVersion: semver.MustParse("1.0.0"), + CurrentAPIVersion: semver.MustParse("2.0.0"), + MinimalAPIVersion: semver.MustParse("2.0.0"), }, CompatTree: { CurrentAPIVersion: semver.MustParse("1.40.0"), diff --git a/pkg/api/server/handler_api.go b/pkg/api/server/handler_api.go index e47b66bb4..f2ce0301b 100644 --- a/pkg/api/server/handler_api.go +++ b/pkg/api/server/handler_api.go @@ -40,6 +40,10 @@ func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc { c = context.WithValue(c, "idletracker", s.idleTracker) //nolint r = r.WithContext(c) + v := utils.APIVersion[utils.CompatTree][utils.CurrentAPIVersion] + w.Header().Set("API-Version", fmt.Sprintf("%d.%d", v.Major, v.Minor)) + w.Header().Set("Libpod-API-Version", utils.APIVersion[utils.LibpodTree][utils.CurrentAPIVersion].String()) + h(w, r) } fn(w, r) diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 2a8133680..3a2e762d6 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -259,12 +259,13 @@ type ImageLoadReport struct { } type ImageImportOptions struct { - Changes []string - Message string - Quiet bool - Reference string - Source string - SourceIsURL bool + Changes []string + Message string + Quiet bool + Reference string + SignaturePolicy string + Source string + SourceIsURL bool } type ImageImportReport struct { diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index cc3ec37fb..cc62c3f27 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -467,7 +467,7 @@ func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) } func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOptions) (*entities.ImageImportReport, error) { - id, err := ir.Libpod.Import(ctx, opts.Source, opts.Reference, opts.Changes, opts.Message, opts.Quiet) + id, err := ir.Libpod.Import(ctx, opts.Source, opts.Reference, opts.SignaturePolicy, opts.Changes, opts.Message, opts.Quiet) if err != nil { return nil, err } 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/container_create.go b/pkg/specgen/generate/container_create.go index fda4c098c..2ac3b376f 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -95,7 +95,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener if len(names) > 0 { imgName = names[0] } - options = append(options, libpod.WithRootFSFromImage(newImage.ID(), imgName, s.Image)) + options = append(options, libpod.WithRootFSFromImage(newImage.ID(), imgName, s.RawImageName)) } if err := s.Validate(); err != nil { return nil, errors.Wrap(err, "invalid config provided") 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/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index cca05eddb..b8f37ec7a 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -89,6 +89,9 @@ type ContainerBasicConfig struct { // If not given, a default location will be used. // Optional. ConmonPidFile string `json:"conmon_pid_file,omitempty"` + // RawImageName is the user-specified and unprocessed input referring + // to a local or a remote image. + RawImageName string `json:"raw_image_name,omitempty"` // RestartPolicy is the container's restart policy - an action which // will be taken when the container exits. // If not given, the default policy, which does nothing, will be used. diff --git a/test/apiv2/01-basic.at b/test/apiv2/01-basic.at index 96b6aef7c..541d8cbf1 100644 --- a/test/apiv2/01-basic.at +++ b/test/apiv2/01-basic.at @@ -18,11 +18,11 @@ t HEAD libpod/_ping 200 for i in /version version; do t GET $i 200 \ .Components[0].Name="Podman Engine" \ - .Components[0].Details.APIVersion=1.0.0 \ - .Components[0].Details.MinAPIVersion=1.0.0 \ + .Components[0].Details.APIVersion=2.0.0 \ + .Components[0].Details.MinAPIVersion=2.0.0 \ .Components[0].Details.Os=linux \ - .ApiVersion=1.0.0 \ - .MinAPIVersion=1.0.0 \ + .ApiVersion=1.40 \ + .MinAPIVersion=1.24 \ .Os=linux done diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index 9cfed263a..6022be5f6 100644 --- a/test/e2e/create_test.go +++ b/test/e2e/create_test.go @@ -345,6 +345,17 @@ var _ = Describe("Podman create", func() { Expect(session).To(Not(Equal(0))) }) + It("podman create --signature-policy", func() { + SkipIfRemote() // SigPolicy not handled by remote + session := podmanTest.Podman([]string{"create", "--pull=always", "--signature-policy", "/no/such/file", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(0))) + + session = podmanTest.Podman([]string{"create", "--pull=always", "--signature-policy", "/etc/containers/policy.json", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman create with unset label", func() { // Alpine is assumed to have no labels here, which seems safe ctrName := "testctr" 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/import_test.go b/test/e2e/import_test.go index feedb2a31..9c6f4381d 100644 --- a/test/e2e/import_test.go +++ b/test/e2e/import_test.go @@ -152,4 +152,21 @@ var _ = Describe("Podman import", func() { Expect(imageData[0].Config.Cmd[0]).To(Equal("/bin/bash")) }) + It("podman import with signature", func() { + outfile := filepath.Join(podmanTest.TempDir, "container.tar") + _, ec, cid := podmanTest.RunLsContainer("") + Expect(ec).To(Equal(0)) + + export := podmanTest.Podman([]string{"export", "-o", outfile, cid}) + export.WaitWithDefaultTimeout() + Expect(export.ExitCode()).To(Equal(0)) + + importImage := podmanTest.Podman([]string{"import", "--signature-policy", "/no/such/file", outfile}) + importImage.WaitWithDefaultTimeout() + Expect(importImage.ExitCode()).To(Not(Equal(0))) + + result := podmanTest.Podman([]string{"import", "--signature-policy", "/etc/containers/policy.json", outfile}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + }) }) 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/test/e2e/run_test.go b/test/e2e/run_test.go index a67f7df92..cbfb6bf59 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -58,6 +58,17 @@ var _ = Describe("Podman run", func() { Expect(session.ExitCode()).To(Equal(0)) }) + It("podman run --signature-policy", func() { + SkipIfRemote() // SigPolicy not handled by remote + session := podmanTest.Podman([]string{"run", "--pull=always", "--signature-policy", "/no/such/file", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(0))) + + session = podmanTest.Podman([]string{"run", "--pull=always", "--signature-policy", "/etc/containers/policy.json", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman run a container based on on a short name with localhost", func() { tag := podmanTest.Podman([]string{"tag", nginx, "localhost/libpod/alpine_nginx:latest"}) tag.WaitWithDefaultTimeout() diff --git a/test/system/250-systemd.bats b/test/system/250-systemd.bats index bbb5a10fb..9bd3e15a1 100644 --- a/test/system/250-systemd.bats +++ b/test/system/250-systemd.bats @@ -41,7 +41,8 @@ function teardown() { fi cname=$(random_string) - run_podman create --name $cname --label "io.containers.autoupdate=image" --detach $IMAGE top + # See #7407 for --pull=always. + run_podman create --pull=always --name $cname --label "io.containers.autoupdate=image" --detach $IMAGE top run_podman generate systemd --new $cname echo "$output" > "$UNIT_FILE" 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 |