diff options
52 files changed, 520 insertions, 175 deletions
@@ -3,7 +3,7 @@ Podman Service Interface and API description. The master version of this docume in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in the upstream libpod repository. ## Index -[func Attach(name: string) ](#Attach) +[func Attach(name: string, detachKeys: string, start: bool) ](#Attach) [func AttachControl(name: string) ](#AttachControl) @@ -119,6 +119,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [func PodStateData(name: string) string](#PodStateData) +[func Ps(opts: PsOpts) PsContainer](#Ps) + [func PullImage(name: string, certDir: string, creds: string, signaturePolicy: string, tlsVerify: ) MoreResponse](#PullImage) [func PushImage(name: string, tag: string, tlsverify: , signaturePolicy: string, creds: string, certDir: string, compress: bool, format: string, removeSignatures: bool, signBy: string) MoreResponse](#PushImage) @@ -225,6 +227,10 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [type PodmanInfo](#PodmanInfo) +[type PsContainer](#PsContainer) + +[type PsOpts](#PsOpts) + [type Runlabel](#Runlabel) [type Sockets](#Sockets) @@ -261,8 +267,9 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in ### <a name="Attach"></a>func Attach <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> -method Attach(name: [string](https://godoc.org/builtin#string)) </div> - +method Attach(name: [string](https://godoc.org/builtin#string), detachKeys: [string](https://godoc.org/builtin#string), start: [bool](https://godoc.org/builtin#bool)) </div> +Attach takes the name or ID of a container and sets up a the ability to remotely attach to its console. The start +bool is whether you wish to start the container in question first. ### <a name="AttachControl"></a>func AttachControl <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> @@ -857,6 +864,11 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.PausePod '{"name": "fooba method PodStateData(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> PodStateData returns inspectr level information of a given pod in string form. This call is for development of Podman only and generally should not be used. +### <a name="Ps"></a>func Ps +<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> + +method Ps(opts: [PsOpts](#PsOpts)) [PsContainer](#PsContainer)</div> + ### <a name="PullImage"></a>func PullImage <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> @@ -1749,6 +1761,82 @@ insecure_registries [[]string](#[]string) store [InfoStore](#InfoStore) podman [InfoPodmanBinary](#InfoPodmanBinary) +### <a name="PsContainer"></a>type PsContainer + + + +id [string](https://godoc.org/builtin#string) + +image [string](https://godoc.org/builtin#string) + +command [string](https://godoc.org/builtin#string) + +created [string](https://godoc.org/builtin#string) + +ports [string](https://godoc.org/builtin#string) + +names [string](https://godoc.org/builtin#string) + +isInfra [bool](https://godoc.org/builtin#bool) + +status [string](https://godoc.org/builtin#string) + +state [string](https://godoc.org/builtin#string) + +pidNum [int](https://godoc.org/builtin#int) + +rootFsSize [int](https://godoc.org/builtin#int) + +rwSize [int](https://godoc.org/builtin#int) + +pod [string](https://godoc.org/builtin#string) + +createdAt [string](https://godoc.org/builtin#string) + +exitedAt [string](https://godoc.org/builtin#string) + +startedAt [string](https://godoc.org/builtin#string) + +labels [map[string]](#map[string]) + +nsPid [string](https://godoc.org/builtin#string) + +cgroup [string](https://godoc.org/builtin#string) + +ipc [string](https://godoc.org/builtin#string) + +mnt [string](https://godoc.org/builtin#string) + +net [string](https://godoc.org/builtin#string) + +pidNs [string](https://godoc.org/builtin#string) + +user [string](https://godoc.org/builtin#string) + +uts [string](https://godoc.org/builtin#string) + +mounts [string](https://godoc.org/builtin#string) +### <a name="PsOpts"></a>type PsOpts + + + +all [bool](https://godoc.org/builtin#bool) + +filters [](#) + +last [](#) + +latest [](#) + +noTrunc [](#) + +pod [](#) + +quiet [](#) + +sort [](#) + +sync [](#) ### <a name="Runlabel"></a>type Runlabel Runlabel describes the required input for container runlabel @@ -29,7 +29,7 @@ This project tests all builds against each supported version of Fedora, the late 1. Further work on the podman pod command 1. Further improvements on rootless containers -## [Shortcomings of Rootless Podman](https://github.com/containers/libpod/blob/master/docs/rootless.md) +## [Shortcomings of Rootless Podman](https://github.com/containers/libpod/blob/master/rootless.md) ## Out of scope diff --git a/cmd/podman/cliconfig/commands.go b/cmd/podman/cliconfig/commands.go index 3361c14b8..00b66e32a 100644 --- a/cmd/podman/cliconfig/commands.go +++ b/cmd/podman/cliconfig/commands.go @@ -1,6 +1,8 @@ package cliconfig -import "github.com/sirupsen/logrus" +import ( + "github.com/sirupsen/logrus" +) // GlobalIsSet is a compatibility method for urfave func (p *PodmanCommand) GlobalIsSet(opt string) bool { diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index f7ac0de6c..2692ace36 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -88,12 +88,13 @@ type CheckpointValues struct { type CommitValues struct { PodmanCommand - Change []string - Format string - Message string - Author string - Pause bool - Quiet bool + Change []string + Format string + Message string + Author string + Pause bool + Quiet bool + IncludeVolumes bool } type ContainersPrune struct { diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go index 9fea1494b..6156fc2f8 100644 --- a/cmd/podman/commands.go +++ b/cmd/podman/commands.go @@ -13,7 +13,6 @@ func getMainCommands() []*cobra.Command { rootCommands := []*cobra.Command{ _commitCommand, _execCommand, - _generateCommand, _playCommand, _loginCommand, _logoutCommand, @@ -71,12 +70,6 @@ func getContainerSubCommands() []*cobra.Command { } } -func getGenerateSubCommands() []*cobra.Command { - return []*cobra.Command{ - _containerKubeCommand, - } -} - // Commands that the local client implements func getPlaySubCommands() []*cobra.Command { return []*cobra.Command{ diff --git a/cmd/podman/commit.go b/cmd/podman/commit.go index f7e206856..0077ff297 100644 --- a/cmd/podman/commit.go +++ b/cmd/podman/commit.go @@ -2,19 +2,19 @@ package main import ( "fmt" - "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/spf13/cobra" "io" "os" "strings" "github.com/containers/buildah" "github.com/containers/image/manifest" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" + "github.com/spf13/cobra" ) var ( @@ -47,7 +47,7 @@ func init() { flags.StringVarP(&commitCommand.Author, "author", "a", "", "Set the author for the image committed") flags.BoolVarP(&commitCommand.Pause, "pause", "p", false, "Pause container during commit") flags.BoolVarP(&commitCommand.Quiet, "quiet", "q", false, "Suppress output") - + flags.BoolVar(&commitCommand.IncludeVolumes, "include-volumes", false, "Include container volumes as image volumes") } func commitCmd(c *cliconfig.CommitValues) error { @@ -109,11 +109,12 @@ func commitCmd(c *cliconfig.CommitValues) error { PreferredManifestType: mimeType, } options := libpod.ContainerCommitOptions{ - CommitOptions: coptions, - Pause: c.Pause, - Message: c.Message, - Changes: c.Change, - Author: c.Author, + CommitOptions: coptions, + Pause: c.Pause, + IncludeVolumes: c.IncludeVolumes, + Message: c.Message, + Changes: c.Change, + Author: c.Author, } newImage, err := ctr.Commit(getContext(), reference, options) if err != nil { diff --git a/cmd/podman/create.go b/cmd/podman/create.go index 1af3920dd..3267e5b7b 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -66,7 +66,7 @@ func createCmd(c *cliconfig.CreateValues) error { } func createInit(c *cliconfig.PodmanCommand) error { - if c.Bool("trace") { + if !remote && c.Bool("trace") { span, _ := opentracing.StartSpanFromContext(Ctx, "createInit") defer span.Finish() } diff --git a/cmd/podman/generate.go b/cmd/podman/generate.go index 197fd26a6..a0637ecb2 100644 --- a/cmd/podman/generate.go +++ b/cmd/podman/generate.go @@ -14,10 +14,15 @@ var ( Long: generateDescription, RunE: commandRunE(), } + + // Commands that are universally implemented + generateCommands = []*cobra.Command{ + _containerKubeCommand, + } ) func init() { generateCommand.Command = _generateCommand - generateCommand.AddCommand(getGenerateSubCommands()...) + generateCommand.AddCommand(generateCommands...) generateCommand.SetUsageTemplate(UsageTemplate()) } diff --git a/cmd/podman/generate_kube.go b/cmd/podman/generate_kube.go index c58372899..30818403b 100644 --- a/cmd/podman/generate_kube.go +++ b/cmd/podman/generate_kube.go @@ -3,13 +3,11 @@ package main import ( "fmt" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/adapter" podmanVersion "github.com/containers/libpod/version" "github.com/ghodss/yaml" "github.com/pkg/errors" "github.com/spf13/cobra" - "k8s.io/api/core/v1" ) var ( @@ -42,14 +40,12 @@ func init() { func generateKubeYAMLCmd(c *cliconfig.GenerateKubeValues) error { var ( - podYAML *v1.Pod - container *libpod.Container - err error - output []byte - pod *libpod.Pod + //podYAML *v1.Pod + err error + output []byte + //pod *libpod.Pod marshalledPod []byte marshalledService []byte - servicePorts []v1.ServicePort ) args := c.InputArgs @@ -57,43 +53,27 @@ func generateKubeYAMLCmd(c *cliconfig.GenerateKubeValues) error { return errors.Errorf("you must provide exactly one container|pod ID or name") } - runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - // Get the container in question - container, err = runtime.LookupContainer(args[0]) + podYAML, serviceYAML, err := runtime.GenerateKube(c) if err != nil { - pod, err = runtime.LookupPod(args[0]) - if err != nil { - return err - } - podYAML, servicePorts, err = pod.GenerateForKube() - } else { - if len(container.Dependencies()) > 0 { - return errors.Wrapf(libpod.ErrNotImplemented, "containers with dependencies") - } - podYAML, err = container.GenerateForKube() + return err } + // Marshall the results + marshalledPod, err = yaml.Marshal(podYAML) if err != nil { return err } - if c.Service { - serviceYAML := libpod.GenerateKubeServiceFromV1Pod(podYAML, servicePorts) marshalledService, err = yaml.Marshal(serviceYAML) if err != nil { return err } } - // Marshall the results - marshalledPod, err = yaml.Marshal(podYAML) - if err != nil { - return err - } - header := `# Generation of Kubernetes YAML is still under development! # # Save the output of this file and use kubectl create -f to import diff --git a/cmd/podman/imagefilters/filters.go b/cmd/podman/imagefilters/filters.go index 2932d61c0..aa5776599 100644 --- a/cmd/podman/imagefilters/filters.go +++ b/cmd/podman/imagefilters/filters.go @@ -37,9 +37,12 @@ func CreatedAfterFilter(createTime time.Time) ResultFilter { } // DanglingFilter allows you to filter images for dangling images -func DanglingFilter() ResultFilter { +func DanglingFilter(danglingImages bool) ResultFilter { return func(i *adapter.ContainerImage) bool { - return i.Dangling() + if danglingImages { + return i.Dangling() + } + return !i.Dangling() } } diff --git a/cmd/podman/images.go b/cmd/podman/images.go index 6133450be..c38d7035d 100644 --- a/cmd/podman/images.go +++ b/cmd/podman/images.go @@ -5,6 +5,7 @@ import ( "fmt" "reflect" "sort" + "strconv" "strings" "time" "unicode" @@ -318,13 +319,14 @@ func getImagesJSONOutput(ctx context.Context, images []*adapter.ContainerImage) func generateImagesOutput(ctx context.Context, images []*adapter.ContainerImage, opts imagesOptions) error { templateMap := GenImageOutputMap() - if len(images) == 0 { - return nil - } var out formats.Writer switch opts.format { case formats.JSONString: + // If 0 images are present, print nothing for JSON + if len(images) == 0 { + return nil + } imagesOutput := getImagesJSONOutput(ctx, images) out = formats.JSONStructArray{Output: imagesToGeneric([]imagesTemplateParams{}, imagesOutput)} default: @@ -359,6 +361,9 @@ func CreateFilterFuncs(ctx context.Context, r *adapter.LocalRuntime, filters []s var filterFuncs []imagefilters.ResultFilter for _, filter := range filters { splitFilter := strings.Split(filter, "=") + if len(splitFilter) != 2 { + return nil, errors.Errorf("invalid filter syntax %s", filter) + } switch splitFilter[0] { case "before": before, err := r.NewImageFromLocal(splitFilter[1]) @@ -373,7 +378,11 @@ func CreateFilterFuncs(ctx context.Context, r *adapter.LocalRuntime, filters []s } filterFuncs = append(filterFuncs, imagefilters.CreatedAfterFilter(after.Created())) case "dangling": - filterFuncs = append(filterFuncs, imagefilters.DanglingFilter()) + danglingImages, err := strconv.ParseBool(splitFilter[1]) + if err != nil { + return nil, errors.Wrapf(err, "invalid filter dangling=%s", splitFilter[1]) + } + filterFuncs = append(filterFuncs, imagefilters.DanglingFilter(danglingImages)) case "label": labelFilter := strings.Join(splitFilter[1:], "=") filterFuncs = append(filterFuncs, imagefilters.LabelFilter(ctx, labelFilter)) diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 35a94b3db..e8c3e14ea 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -36,6 +36,7 @@ var mainCommands = []*cobra.Command{ _createCommand, _eventsCommand, _exportCommand, + _generateCommand, _historyCommand, &_imagesCommand, _importCommand, diff --git a/cmd/podman/pull.go b/cmd/podman/pull.go index 491d3a8c2..7cc7b65b3 100644 --- a/cmd/podman/pull.go +++ b/cmd/podman/pull.go @@ -61,7 +61,12 @@ func init() { // pullCmd gets the data from the command line and calls pullImage // to copy an image from a registry to a local machine -func pullCmd(c *cliconfig.PullValues) error { +func pullCmd(c *cliconfig.PullValues) (retError error) { + defer func() { + if retError != nil && exitCode == 0 { + exitCode = 1 + } + }() if c.Bool("trace") { span, _ := opentracing.StartSpanFromContext(Ctx, "pullCmd") defer span.Finish() @@ -163,7 +168,7 @@ func pullCmd(c *cliconfig.PullValues) error { for _, name := range names { newImage, err := runtime.New(getContext(), name, c.String("signature-policy"), authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, true, nil) if err != nil { - println(errors.Wrapf(err, "error pulling image %q", name)) + logrus.Errorf("error pulling image %q", name) foundImage = false continue } diff --git a/cmd/podman/run.go b/cmd/podman/run.go index bac5c3c18..d3158de6b 100644 --- a/cmd/podman/run.go +++ b/cmd/podman/run.go @@ -38,7 +38,7 @@ func init() { } func runCmd(c *cliconfig.RunValues) error { - if c.Bool("trace") { + if !remote && c.Bool("trace") { span, _ := opentracing.StartSpanFromContext(Ctx, "runCmd") defer span.Finish() } diff --git a/cmd/podman/run_test.go b/cmd/podman/run_test.go index 27b34c323..af9e6923c 100644 --- a/cmd/podman/run_test.go +++ b/cmd/podman/run_test.go @@ -83,7 +83,7 @@ func getRuntimeSpec(c *cliconfig.PodmanCommand) (*spec.Spec, error) { createConfig, err := parseCreateOpts(c, runtime, "alpine", generateAlpineImageData()) */ ctx := getContext() - genericResults := shared.NewIntermediateLayer(c) + genericResults := shared.NewIntermediateLayer(c, false) createConfig, err := shared.ParseCreateOpts(ctx, &genericResults, nil, "alpine", generateAlpineImageData()) if err != nil { return nil, err diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go index 7bef62355..e14276bdf 100644 --- a/cmd/podman/shared/container.go +++ b/cmd/podman/shared/container.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + v1 "k8s.io/api/core/v1" "os" "path/filepath" "regexp" @@ -938,3 +939,37 @@ func envSliceToMap(env []string) map[string]string { } return m } + +// GenerateKube generates kubernetes yaml based on a pod or container +func GenerateKube(name string, service bool, r *libpod.Runtime) (*v1.Pod, *v1.Service, error) { + var ( + pod *libpod.Pod + podYAML *v1.Pod + err error + container *libpod.Container + servicePorts []v1.ServicePort + serviceYAML v1.Service + ) + // Get the container in question + container, err = r.LookupContainer(name) + if err != nil { + pod, err = r.LookupPod(name) + if err != nil { + return nil, nil, err + } + podYAML, servicePorts, err = pod.GenerateForKube() + } else { + if len(container.Dependencies()) > 0 { + return nil, nil, errors.Wrapf(libpod.ErrNotImplemented, "containers with dependencies") + } + podYAML, err = container.GenerateForKube() + } + if err != nil { + return nil, nil, err + } + + if service { + serviceYAML = libpod.GenerateKubeServiceFromV1Pod(podYAML, servicePorts) + } + return podYAML, &serviceYAML, nil +} diff --git a/cmd/podman/shared/intermediate.go b/cmd/podman/shared/intermediate.go index 9afbd68c8..2e1827561 100644 --- a/cmd/podman/shared/intermediate.go +++ b/cmd/podman/shared/intermediate.go @@ -360,7 +360,7 @@ func newCRStringArray(c *cliconfig.PodmanCommand, flag string) CRStringArray { } // NewIntermediateLayer creates a GenericCLIResults from a create or run cli-command -func NewIntermediateLayer(c *cliconfig.PodmanCommand) GenericCLIResults { +func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIResults { m := make(map[string]GenericCLIResult) m["add-host"] = newCRStringSlice(c, "add-host") @@ -458,8 +458,10 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand) GenericCLIResults { m["volumes-from"] = newCRStringSlice(c, "volumes-from") m["workdir"] = newCRString(c, "workdir") // global flag - m["trace"] = newCRBool(c, "trace") - m["syslog"] = newCRBool(c, "syslog") + if !remote { + m["trace"] = newCRBool(c, "trace") + m["syslog"] = newCRBool(c, "syslog") + } return GenericCLIResults{m, c.InputArgs} } diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index ae830f3e6..b5295273a 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -98,6 +98,11 @@ type ImageSearchFilter ( star_count: int ) +type KubePodService ( + pod: string, + service: string +) + type Container ( id: string, image: string, @@ -658,7 +663,9 @@ method PauseContainer(name: string) -> (container: string) # See also [PauseContainer](#PauseContainer). method UnpauseContainer(name: string) -> (container: string) -method Attach(name: string) -> () +# Attach takes the name or ID of a container and sets up a the ability to remotely attach to its console. The start +# bool is whether you wish to start the container in question first. +method Attach(name: string, detachKeys: string, start: bool) -> () method AttachControl(name: string) -> () @@ -1122,11 +1129,7 @@ method ImagesPrune(all: bool) -> (pruned: []string) # GenerateKube generates a Kubernetes v1 Pod description of a Podman container or pod # and its containers. The description is in YAML. See also [ReplayKube](ReplayKube). -# method GenerateKube() -> (notimplemented: NotImplemented) - -# GenerateKubeService generates a Kubernetes v1 Service description of a Podman container or pod -# and its containers. The description is in YAML. See also [GenerateKube](GenerateKube). -# method GenerateKubeService() -> (notimplemented: NotImplemented) +method GenerateKube(name: string, service: bool) -> (pod: KubePodService) # ReplayKube recreates a pod and its containers based on a Kubernetes v1 Pod description (in YAML) # like that created by GenerateKube. See also [GenerateKube](GenerateKube). diff --git a/docs/podman-commit.1.md b/docs/podman-commit.1.md index acde51859..7c74d7a33 100644 --- a/docs/podman-commit.1.md +++ b/docs/podman-commit.1.md @@ -39,6 +39,10 @@ not specifically set, the default format used is _oci_. Write the image ID to the file. +**--include-volumes** + +Include in the committed image any volumes added to the container by the `--volume` or `--mount` options to the `podman create` and `podman run` commands. + **--message, -m** Set commit message for committed image. The message field is not supported in _oci_ format. diff --git a/install.md b/install.md index 5fe150db2..80ae03208 100644 --- a/install.md +++ b/install.md @@ -57,6 +57,7 @@ sudo yum module install -y container-tools:1.0 sudo apt-get update -qq sudo apt-get install -qq -y software-properties-common sudo add-apt-repository -y ppa:projectatomic/ppa +sudo apt-get update -qq sudo apt-get -qq -y install podman ``` diff --git a/libpod/container.go b/libpod/container.go index de4674222..4bf9a1ba9 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -614,7 +614,7 @@ func (c *Container) NewNetNS() bool { func (c *Container) PortMappings() ([]ocicni.PortMapping, error) { // First check if the container belongs to a network namespace (like a pod) if len(c.config.NetNsCtr) > 0 { - netNsCtr, err := c.runtime.LookupContainer(c.config.NetNsCtr) + netNsCtr, err := c.runtime.GetContainer(c.config.NetNsCtr) if err != nil { return nil, errors.Wrapf(err, "unable to lookup network namespace for container %s", c.ID()) } diff --git a/libpod/container_commit.go b/libpod/container_commit.go index 0604a550b..db67f7a30 100644 --- a/libpod/container_commit.go +++ b/libpod/container_commit.go @@ -20,10 +20,11 @@ import ( //libpod type ContainerCommitOptions struct { buildah.CommitOptions - Pause bool - Author string - Message string - Changes []string + Pause bool + IncludeVolumes bool + Author string + Message string + Changes []string } // ChangeCmds is the list of valid Changes commands to passed to the Commit call @@ -113,9 +114,11 @@ func (c *Container) Commit(ctx context.Context, destImage string, options Contai // User importBuilder.SetUser(c.User()) // Volumes - for _, v := range c.config.UserVolumes { - if v != "" { - importBuilder.AddVolume(v) + if options.IncludeVolumes { + for _, v := range c.config.UserVolumes { + if v != "" { + importBuilder.AddVolume(v) + } } } // Workdir diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 3c7319963..aa6448f4a 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -350,7 +350,7 @@ func (c *Container) teardownStorage() error { artifacts := filepath.Join(c.config.StaticDir, artifactsDir) if err := os.RemoveAll(artifacts); err != nil { - return errors.Wrapf(err, "error removing artifacts %q", artifacts) + return errors.Wrapf(err, "error removing container %s artifacts %q", c.ID(), artifacts) } if err := c.cleanupStorage(); err != nil { @@ -676,7 +676,7 @@ func (c *Container) getAllDependencies(visited map[string]*Container) error { } for _, depID := range depIDs { if _, ok := visited[depID]; !ok { - dep, err := c.runtime.state.LookupContainer(depID) + dep, err := c.runtime.state.Container(depID) if err != nil { return err } @@ -1113,13 +1113,13 @@ func (c *Container) cleanup(ctx context.Context) error { // Remove healthcheck unit/timer file if it execs if c.config.HealthCheckConfig != nil { if err := c.removeTimer(); err != nil { - logrus.Error(err) + logrus.Errorf("Error removing timer for container %s healthcheck: %v", c.ID(), err) } } // Clean up network namespace, if present if err := c.cleanupNetwork(); err != nil { - lastError = err + lastError = errors.Wrapf(err, "error removing container %s network", c.ID()) } // Unmount storage @@ -1127,7 +1127,7 @@ func (c *Container) cleanup(ctx context.Context) error { if lastError != nil { logrus.Errorf("Error unmounting container %s storage: %v", c.ID(), err) } else { - lastError = err + lastError = errors.Wrapf(err, "error unmounting container %s storage", c.ID()) } } diff --git a/libpod/kube.go b/libpod/kube.go index 484127870..260269b2e 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -69,7 +69,7 @@ func (p *Pod) getInfraContainer() (*Container, error) { if err != nil { return nil, err } - return p.runtime.LookupContainer(infraID) + return p.runtime.GetContainer(infraID) } // GenerateKubeServiceFromV1Pod creates a v1 service object from a v1 pod object diff --git a/libpod/oci_linux.go b/libpod/oci_linux.go index 8c0abad80..01f7c3649 100644 --- a/libpod/oci_linux.go +++ b/libpod/oci_linux.go @@ -3,15 +3,20 @@ package libpod import ( + "fmt" "os" "os/exec" "path/filepath" + "runtime" "strings" "syscall" "github.com/containerd/cgroups" + "github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/utils" + pmount "github.com/containers/storage/pkg/mount" spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -91,6 +96,54 @@ func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string, restor return err } } + + // if we are running a non privileged container, be sure to umount some kernel paths so they are not + // bind mounted inside the container at all. + if !ctr.config.Privileged && !rootless.IsRootless() { + ch := make(chan error) + go func() { + runtime.LockOSThread() + err := func() error { + fd, err := os.Open(fmt.Sprintf("/proc/%d/task/%d/ns/mnt", os.Getpid(), unix.Gettid())) + if err != nil { + return err + } + defer fd.Close() + + // create a new mountns on the current thread + if err = unix.Unshare(unix.CLONE_NEWNS); err != nil { + return err + } + defer unix.Setns(int(fd.Fd()), unix.CLONE_NEWNS) + + // don't spread our mounts around. We are setting only /sys to be slave + // so that the cleanup process is still able to umount the storage and the + // changes are propagated to the host. + err = unix.Mount("/sys", "/sys", "none", unix.MS_REC|unix.MS_SLAVE, "") + if err != nil { + return errors.Wrapf(err, "cannot make /sys slave") + } + + mounts, err := pmount.GetMounts() + if err != nil { + return err + } + for _, m := range mounts { + if !strings.HasPrefix(m.Mountpoint, "/sys/kernel") { + continue + } + err = unix.Unmount(m.Mountpoint, 0) + if err != nil { + return errors.Wrapf(err, "cannot unmount %s", m.Mountpoint) + } + } + return r.createOCIContainer(ctr, cgroupParent, restoreOptions) + }() + ch <- err + }() + err := <-ch + return err + } } return r.createOCIContainer(ctr, cgroupParent, restoreOptions) } diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 800b42851..48c254c0f 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -372,7 +372,7 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool, // Clean up network namespace, cgroups, mounts if err := c.cleanup(ctx); err != nil { if cleanupErr == nil { - cleanupErr = err + cleanupErr = errors.Wrapf(err, "error cleaning up container %s", c.ID()) } else { logrus.Errorf("cleanup network, cgroups, mounts: %v", err) } @@ -404,12 +404,14 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool, // Deallocate the container's lock if err := c.lock.Free(); err != nil { if cleanupErr == nil { - cleanupErr = err + cleanupErr = errors.Wrapf(err, "error freeing lock for container %s", c.ID()) } else { logrus.Errorf("free container lock: %v", err) } } + c.newContainerEvent(events.Remove) + if !removeVolume { return cleanupErr } @@ -425,7 +427,6 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool, } } - c.newContainerEvent(events.Remove) return cleanupErr } @@ -547,16 +548,6 @@ func (r *Runtime) GetLatestContainer() (*Container, error) { return ctrs[lastCreatedIndex], nil } -// Export is the libpod portion of exporting a container to a tar file -func (r *Runtime) Export(name string, path string) error { - ctr, err := r.LookupContainer(name) - if err != nil { - return err - } - return ctr.Export(path) - -} - // RemoveContainersFromStorage attempt to remove containers from storage that do not exist in libpod database func (r *Runtime) RemoveContainersFromStorage(ctrs []string) { for _, i := range ctrs { diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index 1bb1aab87..931c55a57 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -255,14 +255,17 @@ func (r *LocalRuntime) Log(c *cliconfig.LogsValues, options *libpod.LogOptions) // CreateContainer creates a libpod container func (r *LocalRuntime) CreateContainer(ctx context.Context, c *cliconfig.CreateValues) (string, error) { - results := shared.NewIntermediateLayer(&c.PodmanCommand) + results := shared.NewIntermediateLayer(&c.PodmanCommand, false) ctr, _, err := shared.CreateContainer(ctx, &results, r.Runtime) - return ctr.ID(), err + if err != nil { + return "", err + } + return ctr.ID(), nil } // Run a libpod container func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode int) (int, error) { - results := shared.NewIntermediateLayer(&c.PodmanCommand) + results := shared.NewIntermediateLayer(&c.PodmanCommand, false) ctr, createConfig, err := shared.CreateContainer(ctx, &results, r.Runtime) if err != nil { diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go index 31727fd0e..50cff9fa0 100644 --- a/pkg/adapter/containers_remote.go +++ b/pkg/adapter/containers_remote.go @@ -331,7 +331,7 @@ func (r *LocalRuntime) CreateContainer(ctx context.Context, c *cliconfig.CreateV // TODO need to add attach when that function becomes available return "", errors.New("the remote client only supports detached containers") } - results := shared.NewIntermediateLayer(&c.PodmanCommand) + results := shared.NewIntermediateLayer(&c.PodmanCommand, true) return iopodman.CreateContainer().Call(r.Conn, results.MakeVarlink()) } @@ -344,23 +344,21 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode // being run. // TODO the exit codes for run need to be figured out for remote connections - results := shared.NewIntermediateLayer(&c.PodmanCommand) + results := shared.NewIntermediateLayer(&c.PodmanCommand, true) cid, err := iopodman.CreateContainer().Call(r.Conn, results.MakeVarlink()) if err != nil { return 0, err } - _, err = iopodman.StartContainer().Call(r.Conn, cid) - if err != nil { + if c.Bool("detach") { + _, err := iopodman.StartContainer().Call(r.Conn, cid) + fmt.Println(cid) return 0, err } - errChan, err := r.attach(ctx, os.Stdin, os.Stdout, cid) + + errChan, err := r.attach(ctx, os.Stdin, os.Stdout, cid, true) if err != nil { return 0, err } - if c.Bool("detach") { - fmt.Println(cid) - return 0, err - } finalError := <-errChan return 0, finalError } @@ -441,7 +439,7 @@ func (r *LocalRuntime) Ps(c *cliconfig.PsValues, opts shared.PsOptions) ([]share return psContainers, nil } -func (r *LocalRuntime) attach(ctx context.Context, stdin, stdout *os.File, cid string) (chan error, error) { +func (r *LocalRuntime) attach(ctx context.Context, stdin, stdout *os.File, cid string, start bool) (chan error, error) { var ( oldTermState *term.State ) @@ -471,8 +469,8 @@ func (r *LocalRuntime) attach(ctx context.Context, stdin, stdout *os.File, cid s term.SetRawTerminal(os.Stdin.Fd()) } - - _, err = iopodman.Attach().Send(r.Conn, varlink.Upgrade, cid) + // TODO add detach keys support + _, err = iopodman.Attach().Send(r.Conn, varlink.Upgrade, cid, "", start) if err != nil { restoreTerminal(oldTermState) return nil, err @@ -533,7 +531,7 @@ func (r *LocalRuntime) Attach(ctx context.Context, c *cliconfig.AttachValues) er if c.NoStdin { inputStream = nil } - errChan, err := r.attach(ctx, inputStream, os.Stdout, c.InputArgs[0]) + errChan, err := r.attach(ctx, inputStream, os.Stdout, c.InputArgs[0], false) if err != nil { return err } diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go index d45bdb56d..6aafed550 100644 --- a/pkg/adapter/runtime.go +++ b/pkg/adapter/runtime.go @@ -7,6 +7,7 @@ import ( "context" "io" "io/ioutil" + "k8s.io/api/core/v1" "os" "text/template" @@ -404,27 +405,7 @@ func (r *LocalRuntime) Diff(c *cliconfig.DiffValues, to string) ([]archive.Chang return r.Runtime.GetDiff("", to) } -// func (r *LocalRuntime) joinContainerOrCreateRootlessUserNS(ctr *libpod.Container) (bool, int, error) { -// if os.Geteuid() == 0 { -// return false, 0, nil -// } -// s, err := ctr.State() -// if err != nil { -// return false, -1, err -// } -// opts := rootless.Opts{ -// Argument: ctr.ID(), -// } -// if s == libpod.ContainerStateRunning || s == libpod.ContainerStatePaused { -// data, err := ioutil.ReadFile(ctr.Config().ConmonPidFile) -// if err != nil { -// return false, -1, errors.Wrapf(err, "Container %s cannot read conmon PID file %q", ctr.ID(), ctr.Config().ConmonPidFile) -// } -// conmonPid, err := strconv.Atoi(string(data)) -// if err != nil { -// return false, -1, errors.Wrapf(err, "Container %s cannot parse PID %q", ctr.ID(), data) -// } -// return rootless.JoinDirectUserAndMountNSWithOpts(uint(conmonPid), &opts) -// } -// return rootless.BecomeRootInUserNSWithOpts(&opts) -// } +// GenerateKube creates kubernetes email from containers and pods +func (r *LocalRuntime) GenerateKube(c *cliconfig.GenerateKubeValues) (*v1.Pod, *v1.Service, error) { + return shared.GenerateKube(c.InputArgs[0], c.Service, r.Runtime) +} diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go index 807a9ad8f..71f7380db 100644 --- a/pkg/adapter/runtime_remote.go +++ b/pkg/adapter/runtime_remote.go @@ -5,9 +5,11 @@ package adapter import ( "bufio" "context" + "encoding/json" "fmt" "io" "io/ioutil" + v1 "k8s.io/api/core/v1" "os" "strings" "text/template" @@ -858,3 +860,20 @@ func stringToChangeType(change string) archive.ChangeType { return archive.ChangeModify } } + +// GenerateKube creates kubernetes email from containers and pods +func (r *LocalRuntime) GenerateKube(c *cliconfig.GenerateKubeValues) (*v1.Pod, *v1.Service, error) { + var ( + pod v1.Pod + service v1.Service + ) + reply, err := iopodman.GenerateKube().Call(r.Conn, c.InputArgs[0], c.Service) + if err != nil { + return nil, nil, errors.Wrap(err, "unable to create kubernetes YAML") + } + if err := json.Unmarshal([]byte(reply.Pod), &pod); err != nil { + return nil, nil, err + } + err = json.Unmarshal([]byte(reply.Service), &service) + return &pod, &service, err +} diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c index 9cb79ed4d..d6a2793a7 100644 --- a/pkg/rootless/rootless_linux.c +++ b/pkg/rootless/rootless_linux.c @@ -16,6 +16,8 @@ #include <sys/types.h> #include <sys/prctl.h> #include <dirent.h> +#include <termios.h> +#include <sys/ioctl.h> static const char *_max_user_namespaces = "/proc/sys/user/max_user_namespaces"; static const char *_unprivileged_user_namespaces = "/proc/sys/kernel/unprivileged_userns_clone"; @@ -178,6 +180,11 @@ reexec_userns_join (int userns, int mountns) _exit (EXIT_FAILURE); } + if (isatty (1) && ioctl (1, TIOCSCTTY, 0) == -1) { + fprintf (stderr, "cannot ioctl(TIOCSCTTY): %s\n", strerror (errno)); + _exit (EXIT_FAILURE); + } + if (setns (userns, 0) < 0) { fprintf (stderr, "cannot setns: %s\n", strerror (errno)); diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index 1d1b1713d..2c99f41a4 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -28,6 +28,10 @@ extern int reexec_userns_join(int userns, int mountns); */ import "C" +const ( + numSig = 65 // max number of signals +) + func runInUser() error { os.Setenv("_CONTAINERS_USERNS_CONFIGURED", "done") return nil @@ -283,7 +287,15 @@ func BecomeRootInUserNS() (bool, int, error) { c := make(chan os.Signal, 1) - gosignal.Notify(c) + signals := []os.Signal{} + for sig := 0; sig < numSig; sig++ { + if sig == int(syscall.SIGTSTP) { + continue + } + signals = append(signals, syscall.Signal(sig)) + } + + gosignal.Notify(c, signals...) defer gosignal.Reset() go func() { for s := range c { diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go index 9b6bd089e..0371b6d4d 100644 --- a/pkg/spec/spec.go +++ b/pkg/spec/spec.go @@ -132,6 +132,9 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint Options: []string{"rprivate", "nosuid", "noexec", "nodev", r, "rbind"}, } g.AddMount(sysMnt) + if !config.Privileged && isRootless { + g.AddLinuxMaskedPaths("/sys/kernel") + } } if isRootless { nGids, err := getAvailableGids() diff --git a/pkg/varlinkapi/attach.go b/pkg/varlinkapi/attach.go index 53c4d1ff6..9e2a265be 100644 --- a/pkg/varlinkapi/attach.go +++ b/pkg/varlinkapi/attach.go @@ -3,29 +3,17 @@ package varlinkapi import ( + "bufio" "io" "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/varlinkapi/virtwriter" + "github.com/sirupsen/logrus" "k8s.io/client-go/tools/remotecommand" ) -// Close is method to close the writer - -// Attach ... -func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string) error { - var finalErr error - resize := make(chan remotecommand.TerminalSize) - errChan := make(chan error) - - if !call.WantsUpgrade() { - return call.ReplyErrorOccurred("client must use upgraded connection to attach") - } - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } +func setupStreams(call iopodman.VarlinkCall) (*bufio.Reader, *bufio.Writer, *io.PipeReader, *io.PipeWriter, *libpod.AttachStreams) { // These are the varlink sockets reader := call.Call.Reader @@ -49,6 +37,24 @@ func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string) error { // Runc eats the error stream AttachError: true, } + return reader, writer, pr, pw, &streams +} + +// Attach connects to a containers console +func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys string, start bool) error { + var finalErr error + resize := make(chan remotecommand.TerminalSize) + errChan := make(chan error) + + if !call.WantsUpgrade() { + return call.ReplyErrorOccurred("client must use upgraded connection to attach") + } + ctr, err := i.Runtime.LookupContainer(name) + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + + reader, writer, _, pw, streams := setupStreams(call) go func() { if err := virtwriter.Reader(reader, nil, nil, pw, resize); err != nil { @@ -56,20 +62,42 @@ func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string) error { } }() + if start { + finalErr = startAndAttach(ctr, streams, detachKeys, resize, errChan) + } else { + finalErr = attach(ctr, streams, detachKeys, resize, errChan) + } + + if finalErr != libpod.ErrDetach && finalErr != nil { + logrus.Error(finalErr) + } + quitWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.Quit) + _, err = quitWriter.Write([]byte("HANG-UP")) + // TODO error handling is not quite right here yet + return call.Writer.Flush() +} + +func attach(ctr *libpod.Container, streams *libpod.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error { go func() { - // TODO allow for customizable detach keys - if err := ctr.Attach(&streams, "", resize); err != nil { + if err := ctr.Attach(streams, detachKeys, resize); err != nil { errChan <- err } }() + attachError := <-errChan + return attachError +} +func startAndAttach(ctr *libpod.Container, streams *libpod.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error { + var finalErr error + attachChan, err := ctr.StartAndAttach(getContext(), streams, detachKeys, resize, false) + if err != nil { + return err + } select { - // Blocking on an error - case finalErr = <-errChan: - // Need to close up shop - _ = finalErr + case attachChanErr := <-attachChan: + finalErr = attachChanErr + case chanError := <-errChan: + finalErr = chanError } - quitWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.Quit) - _, err = quitWriter.Write([]byte("HANG-UP")) - return call.Writer.Flush() + return finalErr } diff --git a/pkg/varlinkapi/generate.go b/pkg/varlinkapi/generate.go new file mode 100644 index 000000000..bc600c397 --- /dev/null +++ b/pkg/varlinkapi/generate.go @@ -0,0 +1,30 @@ +// +build varlink + +package varlinkapi + +import ( + "encoding/json" + "github.com/containers/libpod/cmd/podman/shared" + iopodman "github.com/containers/libpod/cmd/podman/varlink" +) + +// GenerateKube ... +func (i *LibpodAPI) GenerateKube(call iopodman.VarlinkCall, name string, service bool) error { + pod, serv, err := shared.GenerateKube(name, service, i.Runtime) + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + podB, err := json.Marshal(pod) + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + servB, err := json.Marshal(serv) + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + + return call.ReplyGenerateKube(iopodman.KubePodService{ + Pod: string(podB), + Service: string(servB), + }) +} diff --git a/test/e2e/commit_test.go b/test/e2e/commit_test.go index fe4ae64cf..93e1ea7af 100644 --- a/test/e2e/commit_test.go +++ b/test/e2e/commit_test.go @@ -131,7 +131,7 @@ var _ = Describe("Podman commit", func() { Expect(check.ExitCode()).To(Equal(0)) }) - It("podman commit with volume mounts", func() { + It("podman commit with volumes mounts and no include-volumes", func() { s := podmanTest.Podman([]string{"run", "--name", "test1", "-v", "/tmp:/foo", "alpine", "date"}) s.WaitWithDefaultTimeout() Expect(s.ExitCode()).To(Equal(0)) @@ -145,6 +145,23 @@ var _ = Describe("Podman commit", func() { Expect(inspect.ExitCode()).To(Equal(0)) image := inspect.InspectImageJSON() _, ok := image[0].Config.Volumes["/foo"] + Expect(ok).To(BeFalse()) + }) + + It("podman commit with volume mounts and --include-volumes", func() { + s := podmanTest.Podman([]string{"run", "--name", "test1", "-v", "/tmp:/foo", "alpine", "date"}) + s.WaitWithDefaultTimeout() + Expect(s.ExitCode()).To(Equal(0)) + + c := podmanTest.Podman([]string{"commit", "--include-volumes", "test1", "newimage"}) + c.WaitWithDefaultTimeout() + Expect(c.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", "newimage"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + image := inspect.InspectImageJSON() + _, ok := image[0].Config.Volumes["/foo"] Expect(ok).To(BeTrue()) r := podmanTest.Podman([]string{"run", "newimage"}) diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index 6ed5ad2d8..105cba37c 100644 --- a/test/e2e/create_test.go +++ b/test/e2e/create_test.go @@ -70,6 +70,17 @@ var _ = Describe("Podman create", func() { Expect(podmanTest.NumberOfContainers()).To(Equal(1)) }) + It("podman create using existing name", func() { + session := podmanTest.Podman([]string{"create", "--name=foo", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + + session = podmanTest.Podman([]string{"create", "--name=foo", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + }) + It("podman create adds annotation", func() { session := podmanTest.Podman([]string{"create", "--annotation", "HELLO=WORLD", ALPINE, "ls"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go index a253dff63..48a964db4 100644 --- a/test/e2e/images_test.go +++ b/test/e2e/images_test.go @@ -43,6 +43,17 @@ var _ = Describe("Podman images", func() { Expect(session.LineInOuputStartsWith("docker.io/library/busybox")).To(BeTrue()) }) + It("podman images with no images prints header", func() { + rmi := podmanTest.Podman([]string{"rmi", "-a"}) + rmi.WaitWithDefaultTimeout() + Expect(rmi.ExitCode()).To(Equal(0)) + + session := podmanTest.Podman([]string{"images"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(Equal(1)) + }) + It("podman image List", func() { session := podmanTest.Podman([]string{"image", "list"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/prune_test.go b/test/e2e/prune_test.go index 869ca3289..682f7ff2b 100644 --- a/test/e2e/prune_test.go +++ b/test/e2e/prune_test.go @@ -82,7 +82,7 @@ var _ = Describe("Podman rm", func() { prune.WaitWithDefaultTimeout() Expect(prune.ExitCode()).To(Equal(0)) - images := podmanTest.Podman([]string{"images", "-a"}) + images := podmanTest.Podman([]string{"images", "-aq"}) images.WaitWithDefaultTimeout() // all images are unused, so they all should be deleted! Expect(len(images.OutputToStringArray())).To(Equal(0)) @@ -95,7 +95,7 @@ var _ = Describe("Podman rm", func() { prune.WaitWithDefaultTimeout() Expect(prune.ExitCode()).To(Equal(0)) - images := podmanTest.Podman([]string{"images", "-a"}) + images := podmanTest.Podman([]string{"images", "-aq"}) images.WaitWithDefaultTimeout() // all images are unused, so they all should be deleted! Expect(len(images.OutputToStringArray())).To(Equal(0)) diff --git a/test/e2e/pull_test.go b/test/e2e/pull_test.go index de6d4ea09..4e4e80d56 100644 --- a/test/e2e/pull_test.go +++ b/test/e2e/pull_test.go @@ -38,6 +38,12 @@ var _ = Describe("Podman pull", func() { }) + It("podman pull from docker a not existing image", func() { + session := podmanTest.Podman([]string{"pull", "ibetthisdoesntexistthere:foo"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(0))) + }) + It("podman pull from docker with tag", func() { session := podmanTest.Podman([]string{"pull", "busybox:glibc"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/rmi_test.go b/test/e2e/rmi_test.go index 78d175637..e034f24cf 100644 --- a/test/e2e/rmi_test.go +++ b/test/e2e/rmi_test.go @@ -270,7 +270,7 @@ RUN find $LOCAL fmt.Println(session.OutputToString()) Expect(session.ExitCode()).To(Equal(0)) - images := podmanTest.Podman([]string{"images", "--all"}) + images := podmanTest.Podman([]string{"images", "-aq"}) images.WaitWithDefaultTimeout() Expect(images.ExitCode()).To(Equal(0)) Expect(len(images.OutputToStringArray())).To(Equal(0)) diff --git a/test/system/005-info.bats b/test/system/005-info.bats index 7dcc78838..c64b011bd 100644 --- a/test/system/005-info.bats +++ b/test/system/005-info.bats @@ -3,6 +3,8 @@ load helpers @test "podman info - basic test" { + skip_if_remote + run_podman info expected_keys=" @@ -26,6 +28,8 @@ RunRoot: } @test "podman info - json" { + skip_if_remote + run_podman info --format=json expr_nvr="[a-z0-9-]\\\+-[a-z0-9.]\\\+-[a-z0-9]\\\+\." diff --git a/test/system/010-images.bats b/test/system/010-images.bats index 1c9577e34..380623078 100644 --- a/test/system/010-images.bats +++ b/test/system/010-images.bats @@ -25,11 +25,12 @@ load helpers @test "podman images - json" { + # 'created': podman includes fractional seconds, podman-remote does not tests=" names[0] | $PODMAN_TEST_IMAGE_FQN id | [0-9a-f]\\\{64\\\} digest | sha256:[0-9a-f]\\\{64\\\} -created | [0-9-]\\\+T[0-9:]\\\+\\\.[0-9]\\\+Z +created | [0-9-]\\\+T[0-9:.]\\\+Z size | [0-9]\\\+ " diff --git a/test/system/015-help.bats b/test/system/015-help.bats index 8e07b8822..a987f04bc 100644 --- a/test/system/015-help.bats +++ b/test/system/015-help.bats @@ -87,6 +87,8 @@ function check_help() { @test "podman help - basic tests" { + skip_if_remote + # Called with no args -- start with 'podman --help'. check_help() will # recurse for any subcommands. check_help diff --git a/test/system/030-run.bats b/test/system/030-run.bats index 8ae68f33d..bdbe724ef 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -3,6 +3,8 @@ load helpers @test "podman run - basic tests" { + skip_if_remote + rand=$(random_string 30) tests=" true | 0 | @@ -31,4 +33,16 @@ echo $rand | 0 | $rand done < <(parse_table "$tests") } +@test "podman run - uidmapping has no /sys/kernel mounts" { + skip_if_rootless "cannot umount as rootless" + + run_podman run --rm --uidmap 0:100:10000 $IMAGE mount + run grep /sys/kernel <(echo "$output") + is "$output" "" "unwanted /sys/kernel in 'mount' output" + + run_podman run --rm --net host --uidmap 0:100:10000 $IMAGE mount + run grep /sys/kernel <(echo "$output") + is "$output" "" "unwanted /sys/kernel in 'mount' output (with --net=host)" +} + # vim: filetype=sh diff --git a/test/system/035-logs.bats b/test/system/035-logs.bats index 055865c8d..5736e0939 100644 --- a/test/system/035-logs.bats +++ b/test/system/035-logs.bats @@ -6,6 +6,8 @@ load helpers @test "podman logs - basic test" { + skip_if_remote + rand_string=$(random_string 40) run_podman create $IMAGE echo $rand_string diff --git a/test/system/060-mount.bats b/test/system/060-mount.bats index e249b2883..7570f3ac4 100644 --- a/test/system/060-mount.bats +++ b/test/system/060-mount.bats @@ -6,6 +6,7 @@ load helpers @test "podman mount - basic test" { # Only works with root (FIXME: does it work with rootless + vfs?) skip_if_rootless "mount does not work rootless" + skip_if_remote f_path=/tmp/tmpfile_$(random_string 8) f_content=$(random_string 30) diff --git a/test/system/070-build.bats b/test/system/070-build.bats index 25eb36c58..c6a25093f 100644 --- a/test/system/070-build.bats +++ b/test/system/070-build.bats @@ -6,6 +6,8 @@ load helpers @test "podman build - basic test" { + skip_if_remote + rand_filename=$(random_string 20) rand_content=$(random_string 50) diff --git a/test/system/075-exec.bats b/test/system/075-exec.bats index a12d28b32..11cb98269 100644 --- a/test/system/075-exec.bats +++ b/test/system/075-exec.bats @@ -6,6 +6,8 @@ load helpers @test "podman exec - basic test" { + skip_if_remote + rand_filename=$(random_string 20) rand_content=$(random_string 50) diff --git a/test/system/110-history.bats b/test/system/110-history.bats index 84a1e42b4..5dc221d61 100644 --- a/test/system/110-history.bats +++ b/test/system/110-history.bats @@ -24,7 +24,7 @@ load helpers @test "podman history - json" { tests=" id | [0-9a-f]\\\{64\\\} -created | [0-9-]\\\+T[0-9:]\\\+\\\.[0-9]\\\+Z +created | [0-9-]\\\+T[0-9:.]\\\+Z size | -\\\?[0-9]\\\+ " diff --git a/test/system/400-unprivileged-access.bats b/test/system/400-unprivileged-access.bats index c195d71eb..0358b3beb 100644 --- a/test/system/400-unprivileged-access.bats +++ b/test/system/400-unprivileged-access.bats @@ -7,6 +7,7 @@ load helpers @test "podman container storage is not accessible by unprivileged users" { skip_if_rootless "test meaningless without suid" + skip_if_remote run_podman run --name c_uidmap --uidmap 0:10000:10000 $IMAGE true run_podman run --name c_uidmap_v --uidmap 0:10000:10000 -v foo:/foo $IMAGE true diff --git a/test/system/helpers.bash b/test/system/helpers.bash index 431228498..29ef19ecc 100644 --- a/test/system/helpers.bash +++ b/test/system/helpers.bash @@ -225,6 +225,16 @@ function skip_if_rootless() { skip "${1:-not applicable under rootless podman}" } +#################### +# skip_if_remote # ...with an optional message +#################### +function skip_if_remote() { + if [[ ! "$PODMAN" =~ -remote ]]; then + return + fi + + skip "${1:-test does not work with podman-remote}" +} ######### # die # Abort with helpful message |