diff options
100 files changed, 1227 insertions, 1772 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 5a9dbcb54..80c954ca0 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -371,6 +371,8 @@ testing_crun_task: networking_script: '${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/networking.sh' setup_environment_script: '$SCRIPT_BASE/setup_environment.sh |& ${TIMESTAMP}' install_crun_script: 'dnf install -y crun' + # FIXME: use the package once all the fixes are in a release + override_crun_script: 'setenforce 0; yum builddep -y crun && (git clone --depth=1 https://github.com/containers/crun && cd crun && ./autogen.sh && ./configure --prefix=/usr && make -j4 && make install) && rm -rf crun' unit_test_script: '$SCRIPT_BASE/unit_test.sh |& ${TIMESTAMP}' integration_test_script: '$SCRIPT_BASE/integration_test.sh |& ${TIMESTAMP}' system_test_script: '$SCRIPT_BASE/system_test.sh |& ${TIMESTAMP}' diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 2946f0b91..621430670 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -58,4 +58,10 @@ Briefly describe the problem you are having in a few paragraphs. (paste your output here) ``` +**Package info (e.g. output of `rpm -q podman` or `apt list podman`):** + +``` +(paste your output here) +``` + **Additional environment details (AWS, VirtualBox, physical, etc.):** @@ -49,8 +49,6 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [func GenerateKube(name: string, service: bool) KubePodService](#GenerateKube) -[func GenerateSystemd(name: string, restart: string, timeout: int, useName: bool) string](#GenerateSystemd) - [func GetAttachSockets(name: string) Sockets](#GetAttachSockets) [func GetContainer(id: string) Container](#GetContainer) @@ -87,6 +85,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [func GetVolumes(args: []string, all: bool) Volume](#GetVolumes) +[func HealthCheckRun(nameOrID: string) string](#HealthCheckRun) + [func HistoryImage(name: string) ImageHistory](#HistoryImage) [func ImageExists(name: string) int](#ImageExists) @@ -480,11 +480,6 @@ error will be returned. See also [ImportImage](ImportImage). method GenerateKube(name: [string](https://godoc.org/builtin#string), service: [bool](https://godoc.org/builtin#bool)) [KubePodService](#KubePodService)</div> 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). -### <a name="GenerateSystemd"></a>func GenerateSystemd -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method GenerateSystemd(name: [string](https://godoc.org/builtin#string), restart: [string](https://godoc.org/builtin#string), timeout: [int](https://godoc.org/builtin#int), useName: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div> - ### <a name="GetAttachSockets"></a>func GetAttachSockets <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> @@ -681,6 +676,12 @@ GetVersion returns version and build information of the podman service method GetVolumes(args: [[]string](#[]string), all: [bool](https://godoc.org/builtin#bool)) [Volume](#Volume)</div> GetVolumes gets slice of the volumes on a remote host +### <a name="HealthCheckRun"></a>func HealthCheckRun +<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> + +method HealthCheckRun(nameOrID: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> +HealthCheckRun executes defined container's healthcheck command +and returns the container's health status. ### <a name="HistoryImage"></a>func HistoryImage <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> @@ -1981,8 +1982,6 @@ mountPoint [string](https://godoc.org/builtin#string) driver [string](https://godoc.org/builtin#string) options [map[string]](#map[string]) - -scope [string](https://godoc.org/builtin#string) ### <a name="VolumeCreateOpts"></a>type VolumeCreateOpts diff --git a/Dockerfile b/Dockerfile index 3185586b9..1f51cd874 100644 --- a/Dockerfile +++ b/Dockerfile @@ -68,13 +68,13 @@ RUN set -x \ && rm -rf "$GOPATH" # Install CNI plugins -ENV CNI_COMMIT 7480240de9749f9a0a5c8614b17f1f03e0c06ab9 +ENV CNI_COMMIT 485be65581341430f9106a194a98f0f2412245fb RUN set -x \ - && export GOPATH="$(mktemp -d)" \ + && export GOPATH="$(mktemp -d)" GOCACHE="$(mktemp -d)" \ && git clone https://github.com/containernetworking/plugins.git "$GOPATH/src/github.com/containernetworking/plugins" \ && cd "$GOPATH/src/github.com/containernetworking/plugins" \ && git checkout -q "$CNI_COMMIT" \ - && ./build.sh \ + && ./build_linux.sh \ && mkdir -p /usr/libexec/cni \ && cp bin/* /usr/libexec/cni \ && rm -rf "$GOPATH" diff --git a/Dockerfile.centos b/Dockerfile.centos index bbb582840..513c4bdfd 100644 --- a/Dockerfile.centos +++ b/Dockerfile.centos @@ -27,13 +27,13 @@ RUN yum -y install btrfs-progs-devel \ iptables && yum clean all # Install CNI plugins -ENV CNI_COMMIT 7480240de9749f9a0a5c8614b17f1f03e0c06ab9 +ENV CNI_COMMIT 485be65581341430f9106a194a98f0f2412245fb RUN set -x \ - && export GOPATH="$(mktemp -d)" \ + && export GOPATH="$(mktemp -d)" GOCACHE="$(mktemp -d)" \ && git clone https://github.com/containernetworking/plugins.git "$GOPATH/src/github.com/containernetworking/plugins" \ && cd "$GOPATH/src/github.com/containernetworking/plugins" \ && git checkout -q "$CNI_COMMIT" \ - && ./build.sh \ + && ./build_linux.sh \ && mkdir -p /usr/libexec/cni \ && cp bin/* /usr/libexec/cni \ && rm -rf "$GOPATH" diff --git a/Dockerfile.fedora b/Dockerfile.fedora index 54ab690e9..429597f4f 100644 --- a/Dockerfile.fedora +++ b/Dockerfile.fedora @@ -27,35 +27,24 @@ RUN dnf -y install btrfs-progs-devel \ xz \ slirp4netns \ container-selinux \ + containernetworking-plugins \ iptables && dnf clean all -# Install CNI plugins -ENV CNI_COMMIT 412b6d31280682bb4fab4446f113c22ff1886554 -RUN set -x \ - && export GOPATH="$(mktemp -d)" \ - && git clone https://github.com/containernetworking/plugins.git "$GOPATH/src/github.com/containernetworking/plugins" \ - && cd "$GOPATH/src/github.com/containernetworking/plugins" \ - && git checkout -q "$CNI_COMMIT" \ - && ./build.sh \ - && mkdir -p /usr/libexec/cni \ - && cp bin/* /usr/libexec/cni \ - && rm -rf "$GOPATH" - # Install ginkgo RUN set -x \ - && export GOPATH=/go \ + && export GOPATH=/go GOCACHE="$(mktemp -d)" \ && go get -u github.com/onsi/ginkgo/ginkgo \ && install -D -m 755 "$GOPATH"/bin/ginkgo /usr/bin/ # Install gomega RUN set -x \ - && export GOPATH=/go \ + && export GOPATH=/go GOCACHE="$(mktemp -d)" \ && go get github.com/onsi/gomega/... # Install conmon ENV CONMON_COMMIT 6f3572558b97bc60dd8f8c7f0807748e6ce2c440 RUN set -x \ - && export GOPATH="$(mktemp -d)" \ + && export GOPATH="$(mktemp -d)" GOCACHE="$(mktemp -d)" \ && git clone https://github.com/containers/conmon.git "$GOPATH/src/github.com/containers/conmon.git" \ && cd "$GOPATH/src/github.com/containers/conmon.git" \ && git fetch origin --tags \ @@ -137,7 +137,8 @@ into other tools. Podman specializes in all of the commands and functions that help you to maintain and modify OCI images, such as pulling and tagging. It also allows you to create, run, and maintain those containers -created from those images. +created from those images. For building container images via Dockerfiles, Podman uses Buildah's +golang API and can be installed independently from Buildah. A major difference between Podman and Buildah is their concept of a container. Podman allows users to create "traditional containers" where the intent of these containers is diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index f7c78908f..9bc47333d 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -156,6 +156,7 @@ type GenerateKubeValues struct { type GenerateSystemdValues struct { PodmanCommand Name bool + Files bool RestartPolicy string StopTimeout int } @@ -300,6 +301,7 @@ type PodCreateValues struct { LabelFile []string Labels []string Name string + Hostname string PodIDFile string Publish []string Share string @@ -422,6 +424,7 @@ type PushValues struct { CertDir string Compress bool Creds string + Digestfile string Format string Quiet bool RemoveSignatures bool diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go index 27f3fc214..77c76d1b7 100644 --- a/cmd/podman/commands.go +++ b/cmd/podman/commands.go @@ -11,6 +11,7 @@ const remoteclient = false // Commands that the local client implements func getMainCommands() []*cobra.Command { rootCommands := []*cobra.Command{ + _cpCommand, _playCommand, _loginCommand, _logoutCommand, @@ -39,6 +40,7 @@ func getImageSubCommands() []*cobra.Command { func getContainerSubCommands() []*cobra.Command { return []*cobra.Command{ + _cpCommand, _cleanupCommand, _mountCommand, _refreshCommand, @@ -71,10 +73,3 @@ func getSystemSubCommands() []*cobra.Command { _migrateCommand, } } - -// Commands that the local client implements -func getHealthcheckSubCommands() []*cobra.Command { - return []*cobra.Command{ - _healthcheckrunCommand, - } -} diff --git a/cmd/podman/commands_remoteclient.go b/cmd/podman/commands_remoteclient.go index 278fe229c..a278761c1 100644 --- a/cmd/podman/commands_remoteclient.go +++ b/cmd/podman/commands_remoteclient.go @@ -47,8 +47,3 @@ func getTrustSubCommands() []*cobra.Command { func getSystemSubCommands() []*cobra.Command { return []*cobra.Command{} } - -// Commands that the remoteclient implements -func getHealthcheckSubCommands() []*cobra.Command { - return []*cobra.Command{} -} diff --git a/cmd/podman/container.go b/cmd/podman/container.go index 557f5fafa..66b58f06e 100644 --- a/cmd/podman/container.go +++ b/cmd/podman/container.go @@ -55,7 +55,6 @@ var ( _commitCommand, _containerExistsCommand, _contInspectSubCommand, - _cpCommand, _diffCommand, _execCommand, _exportCommand, diff --git a/cmd/podman/cp.go b/cmd/podman/cp.go index ad7253ac0..5e1ca8312 100644 --- a/cmd/podman/cp.go +++ b/cmd/podman/cp.go @@ -55,7 +55,6 @@ func init() { flags.BoolVar(&cpCommand.Pause, "pause", false, "Pause the container while copying") cpCommand.SetHelpTemplate(HelpTemplate()) cpCommand.SetUsageTemplate(UsageTemplate()) - rootCmd.AddCommand(cpCommand.Command) } func cpCmd(c *cliconfig.CpValues) error { diff --git a/cmd/podman/generate.go b/cmd/podman/generate.go index 98bfb00a1..196556bc5 100644 --- a/cmd/podman/generate.go +++ b/cmd/podman/generate.go @@ -18,11 +18,14 @@ var ( // Commands that are universally implemented generateCommands = []*cobra.Command{ _containerKubeCommand, - _containerSystemdCommand, } ) func init() { + // Systemd-service generation is not supported for remote-clients. + if !remoteclient { + generateCommands = append(generateCommands, _containerSystemdCommand) + } generateCommand.Command = _generateCommand generateCommand.AddCommand(generateCommands...) generateCommand.SetUsageTemplate(UsageTemplate()) diff --git a/cmd/podman/generate_systemd.go b/cmd/podman/generate_systemd.go index 222fc4c98..aa202a68d 100644 --- a/cmd/podman/generate_systemd.go +++ b/cmd/podman/generate_systemd.go @@ -5,7 +5,6 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/pkg/adapter" - "github.com/containers/libpod/pkg/systemdgen" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -40,7 +39,10 @@ func init() { containerSystemdCommand.SetHelpTemplate(HelpTemplate()) containerSystemdCommand.SetUsageTemplate(UsageTemplate()) flags := containerSystemdCommand.Flags() - flags.BoolVarP(&containerSystemdCommand.Name, "name", "n", false, "use the container name instead of ID") + flags.BoolVarP(&containerSystemdCommand.Name, "name", "n", false, "use the container/pod name instead of ID") + if !remoteclient { + flags.BoolVarP(&containerSystemdCommand.Files, "files", "f", false, "generate files instead of printing to stdout") + } flags.IntVarP(&containerSystemdCommand.StopTimeout, "timeout", "t", -1, "stop timeout override") flags.StringVar(&containerSystemdCommand.RestartPolicy, "restart-policy", "on-failure", "applicable systemd restart-policy") } @@ -56,10 +58,6 @@ func generateSystemdCmd(c *cliconfig.GenerateSystemdValues) error { if c.Flag("timeout").Changed && c.StopTimeout < 0 { return errors.New("timeout value must be 0 or greater") } - // Make sure the input restart policy is valid - if err := systemdgen.ValidateRestartPolicy(c.RestartPolicy); err != nil { - return err - } unit, err := runtime.GenerateSystemd(c) if err != nil { diff --git a/cmd/podman/healthcheck.go b/cmd/podman/healthcheck.go index 9fb099ffa..140206dbe 100644 --- a/cmd/podman/healthcheck.go +++ b/cmd/podman/healthcheck.go @@ -16,11 +16,12 @@ var healthcheckCommand = cliconfig.PodmanCommand{ } // Commands that are universally implemented -var healthcheckCommands []*cobra.Command +var healthcheckCommands = []*cobra.Command{ + _healthcheckrunCommand, +} func init() { healthcheckCommand.AddCommand(healthcheckCommands...) - healthcheckCommand.AddCommand(getHealthcheckSubCommands()...) healthcheckCommand.SetUsageTemplate(UsageTemplate()) rootCmd.AddCommand(healthcheckCommand.Command) } diff --git a/cmd/podman/libpodruntime/runtime.go b/cmd/podman/libpodruntime/runtime.go index ee9e57966..a133549ea 100644 --- a/cmd/podman/libpodruntime/runtime.go +++ b/cmd/podman/libpodruntime/runtime.go @@ -15,25 +15,30 @@ import ( // GetRuntimeMigrate gets a libpod runtime that will perform a migration of existing containers func GetRuntimeMigrate(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(ctx, c, false, true, false) + return getRuntime(ctx, c, false, true, false, true) +} + +// GetRuntimeDisableFDs gets a libpod runtime that will disable sd notify +func GetRuntimeDisableFDs(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { + return getRuntime(ctx, c, false, false, false, false) } // GetRuntimeRenumber gets a libpod runtime that will perform a lock renumber func GetRuntimeRenumber(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(ctx, c, true, false, false) + return getRuntime(ctx, c, true, false, false, true) } // GetRuntime generates a new libpod runtime configured by command line options func GetRuntime(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(ctx, c, false, false, false) + return getRuntime(ctx, c, false, false, false, true) } // GetRuntimeNoStore generates a new libpod runtime configured by command line options func GetRuntimeNoStore(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(ctx, c, false, false, true) + return getRuntime(ctx, c, false, false, true, true) } -func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber, migrate, noStore bool) (*libpod.Runtime, error) { +func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber, migrate, noStore, withFDS bool) (*libpod.Runtime, error) { options := []libpod.RuntimeOption{} storageOpts := storage.StoreOptions{} storageSet := false @@ -165,6 +170,10 @@ func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber, migra infraCommand, _ := c.Flags().GetString("infra-command") options = append(options, libpod.WithDefaultInfraCommand(infraCommand)) } + + if withFDS { + options = append(options, libpod.WithEnableSDNotify()) + } if c.Flags().Changed("config") { return libpod.NewRuntimeFromConfig(ctx, c.GlobalFlags.Config, options...) } diff --git a/cmd/podman/main_local.go b/cmd/podman/main_local.go index 587c8260f..648dc166e 100644 --- a/cmd/podman/main_local.go +++ b/cmd/podman/main_local.go @@ -140,7 +140,7 @@ func setupRootless(cmd *cobra.Command, args []string) error { became, ret, err := rootless.TryJoinFromFilePaths("", false, []string{pausePidPath}) if err != nil { logrus.Errorf("cannot join pause process. You may need to remove %s and stop all containers", pausePidPath) - logrus.Errorf("you can use `system migrate` to recreate the pause process") + logrus.Errorf("you can use `%s system migrate` to recreate the pause process", os.Args[0]) logrus.Errorf(err.Error()) os.Exit(1) } diff --git a/cmd/podman/pod_create.go b/cmd/podman/pod_create.go index d04c85dba..ad3c00aa8 100644 --- a/cmd/podman/pod_create.go +++ b/cmd/podman/pod_create.go @@ -52,6 +52,7 @@ func init() { flags.StringSliceVar(&podCreateCommand.LabelFile, "label-file", []string{}, "Read in a line delimited file of labels") flags.StringSliceVarP(&podCreateCommand.Labels, "label", "l", []string{}, "Set metadata on pod (default [])") flags.StringVarP(&podCreateCommand.Name, "name", "n", "", "Assign a name to the pod") + flags.StringVarP(&podCreateCommand.Hostname, "hostname", "", "", "Set a hostname to the pod") flags.StringVar(&podCreateCommand.PodIDFile, "pod-id-file", "", "Write the pod ID to the file") flags.StringSliceVarP(&podCreateCommand.Publish, "publish", "p", []string{}, "Publish a container's port, or a range of ports, to the host (default [])") flags.StringVar(&podCreateCommand.Share, "share", shared.DefaultKernelNamespaces, "A comma delimited list of kernel namespaces the pod will share") diff --git a/cmd/podman/push.go b/cmd/podman/push.go index 43df8c2de..13ebe8a1f 100644 --- a/cmd/podman/push.go +++ b/cmd/podman/push.go @@ -51,6 +51,7 @@ func init() { pushCommand.SetUsageTemplate(UsageTemplate()) flags := pushCommand.Flags() flags.StringVar(&pushCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") + flags.StringVar(&pushCommand.Digestfile, "digestfile", "", "After copying the image, write the digest of the resulting image to the file") flags.StringVarP(&pushCommand.Format, "format", "f", "", "Manifest type (oci, v2s1, or v2s2) to use when pushing an image using the 'dir:' transport (default is manifest type of source)") flags.BoolVarP(&pushCommand.Quiet, "quiet", "q", false, "Don't output progress information when pushing images") flags.BoolVar(&pushCommand.RemoveSignatures, "remove-signatures", false, "Discard any pre-existing signatures in the image") @@ -143,5 +144,5 @@ func pushCmd(c *cliconfig.PushValues) error { SignBy: signBy, } - return runtime.Push(getContext(), srcName, destName, manifestType, c.Authfile, c.SignaturePolicy, writer, c.Compress, so, &dockerRegistryOptions, nil) + return runtime.Push(getContext(), srcName, destName, manifestType, c.Authfile, c.String("digestfile"), c.SignaturePolicy, writer, c.Compress, so, &dockerRegistryOptions, nil) } diff --git a/cmd/podman/shared/funcs.go b/cmd/podman/shared/funcs.go index 2ceb9cdcb..bb4eed1e3 100644 --- a/cmd/podman/shared/funcs.go +++ b/cmd/podman/shared/funcs.go @@ -6,6 +6,7 @@ import ( "path/filepath" "strings" + "github.com/containers/libpod/pkg/util" "github.com/google/shlex" ) @@ -13,13 +14,14 @@ func GetAuthFile(authfile string) string { if authfile != "" { return authfile } + authfile = os.Getenv("REGISTRY_AUTH_FILE") if authfile != "" { return authfile } - runtimeDir := os.Getenv("XDG_RUNTIME_DIR") - if runtimeDir != "" { - return filepath.Join(runtimeDir, "containers/auth.json") + + if runtimeDir, err := util.GetRuntimeDir(); err == nil { + return filepath.Join(runtimeDir, "auth.json") } return "" } diff --git a/cmd/podman/shared/intermediate.go b/cmd/podman/shared/intermediate.go index c6c32f8a9..5aaac8687 100644 --- a/cmd/podman/shared/intermediate.go +++ b/cmd/podman/shared/intermediate.go @@ -114,7 +114,7 @@ func (f GenericCLIResults) findResult(flag string) GenericCLIResult { if ok { return val } - logrus.Errorf("unable to find flag %s", flag) + logrus.Debugf("unable to find flag %s", flag) return nil } @@ -366,12 +366,10 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes m["add-host"] = newCRStringSlice(c, "add-host") m["annotation"] = newCRStringSlice(c, "annotation") m["attach"] = newCRStringSlice(c, "attach") - m["authfile"] = newCRString(c, "authfile") m["blkio-weight"] = newCRString(c, "blkio-weight") m["blkio-weight-device"] = newCRStringSlice(c, "blkio-weight-device") m["cap-add"] = newCRStringSlice(c, "cap-add") m["cap-drop"] = newCRStringSlice(c, "cap-drop") - m["cgroupns"] = newCRString(c, "cgroupns") m["cgroup-parent"] = newCRString(c, "cgroup-parent") m["cidfile"] = newCRString(c, "cidfile") m["conmon-pidfile"] = newCRString(c, "conmon-pidfile") @@ -395,7 +393,6 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes m["dns-search"] = newCRStringSlice(c, "dns-search") m["entrypoint"] = newCRString(c, "entrypoint") m["env"] = newCRStringArray(c, "env") - m["env-host"] = newCRBool(c, "env-host") m["env-file"] = newCRStringSlice(c, "env-file") m["expose"] = newCRStringSlice(c, "expose") m["gidmap"] = newCRStringSlice(c, "gidmap") @@ -407,7 +404,6 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes m["healthcheck-start-period"] = newCRString(c, "health-start-period") m["healthcheck-timeout"] = newCRString(c, "health-timeout") m["hostname"] = newCRString(c, "hostname") - m["http-proxy"] = newCRBool(c, "http-proxy") m["image-volume"] = newCRString(c, "image-volume") m["init"] = newCRBool(c, "init") m["init-path"] = newCRString(c, "init-path") @@ -465,6 +461,10 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes m["workdir"] = newCRString(c, "workdir") // global flag if !remote { + m["authfile"] = newCRString(c, "authfile") + m["cgroupns"] = newCRString(c, "cgroupns") + m["env-host"] = newCRBool(c, "env-host") + m["http-proxy"] = newCRBool(c, "http-proxy") m["trace"] = newCRBool(c, "trace") m["syslog"] = newCRBool(c, "syslog") } diff --git a/cmd/podman/stats.go b/cmd/podman/stats.go index 2f696445e..25514ec75 100644 --- a/cmd/podman/stats.go +++ b/cmd/podman/stats.go @@ -134,9 +134,13 @@ func statsCmd(c *cliconfig.StatsValues) error { initialStats, err := ctr.GetContainerStats(&libpod.ContainerStats{}) if err != nil { // when doing "all", dont worry about containers that are not running - if c.All && errors.Cause(err) == define.ErrCtrRemoved || errors.Cause(err) == define.ErrNoSuchCtr || errors.Cause(err) == define.ErrCtrStateInvalid { + cause := errors.Cause(err) + if c.All && (cause == define.ErrCtrRemoved || cause == define.ErrNoSuchCtr || cause == define.ErrCtrStateInvalid) { continue } + if cause == cgroups.ErrCgroupV1Rootless { + err = cause + } return err } containerStats[ctr.ID()] = initialStats diff --git a/cmd/podman/varlink.go b/cmd/podman/varlink.go index 92315cd6b..cd21e3574 100644 --- a/cmd/podman/varlink.go +++ b/cmd/podman/varlink.go @@ -53,7 +53,7 @@ func init() { func varlinkCmd(c *cliconfig.VarlinkValues) error { varlinkURI := adapter.DefaultAddress if rootless.IsRootless() { - xdg, err := util.GetRootlessRuntimeDir() + xdg, err := util.GetRuntimeDir() if err != nil { return err } @@ -79,7 +79,7 @@ func varlinkCmd(c *cliconfig.VarlinkValues) error { timeout := time.Duration(c.Timeout) * time.Millisecond // Create a single runtime for varlink - runtime, err := libpodruntime.GetRuntime(getContext(), &c.PodmanCommand) + runtime, err := libpodruntime.GetRuntimeDisableFDs(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index 2e7dee94d..2873d9761 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -7,8 +7,7 @@ type Volume ( labels: [string]string, mountPoint: string, driver: string, - options: [string]string, - scope: string + options: [string]string ) type NotImplemented ( @@ -545,6 +544,10 @@ method GetContainersByStatus(status: []string) -> (containerS: []Container) method Top (nameOrID: string, descriptors: []string) -> (top: []string) +# HealthCheckRun executes defined container's healthcheck command +# and returns the container's health status. +method HealthCheckRun (nameOrID: string) -> (healthCheckStatus: string) + # GetContainer returns information about a single container. If a container # with the given id doesn't exist, a [ContainerNotFound](#ContainerNotFound) # error will be returned. See also [ListContainers](ListContainers) and @@ -1238,8 +1241,6 @@ method GetLayersMapWithImageInfo() -> (layerMap: string) # BuildImageHierarchyMap is for the development of Podman and should not be used. method BuildImageHierarchyMap(name: string) -> (imageInfo: string) -method GenerateSystemd(name: string, restart: string, timeout: int, useName: bool) -> (unit: string) - # ImageNotFound means the image could not be found by the provided name or ID in local storage. error ImageNotFound (id: string, reason: string) diff --git a/cmd/podman/volume_create.go b/cmd/podman/volume_create.go index 0897ab705..617f701a4 100644 --- a/cmd/podman/volume_create.go +++ b/cmd/podman/volume_create.go @@ -38,7 +38,6 @@ func init() { flags.StringVar(&volumeCreateCommand.Driver, "driver", "", "Specify volume driver name (default local)") flags.StringSliceVarP(&volumeCreateCommand.Label, "label", "l", []string{}, "Set metadata for a volume (default [])") flags.StringSliceVarP(&volumeCreateCommand.Opt, "opt", "o", []string{}, "Set driver specific options (default [])") - } func volumeCreateCmd(c *cliconfig.VolumeCreateValues) error { diff --git a/cmd/podman/volume_inspect.go b/cmd/podman/volume_inspect.go index 1ebc5ce60..94c99a58c 100644 --- a/cmd/podman/volume_inspect.go +++ b/cmd/podman/volume_inspect.go @@ -1,6 +1,9 @@ package main import ( + "fmt" + + "github.com/containers/buildah/pkg/formats" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/pkg/adapter" "github.com/pkg/errors" @@ -53,5 +56,24 @@ func volumeInspectCmd(c *cliconfig.VolumeInspectValues) error { if err != nil { return err } - return generateVolLsOutput(vols, volumeLsOptions{Format: c.Format}) + + switch c.Format { + case "", formats.JSONString: + // Normal format - JSON string + jsonOut, err := json.MarshalIndent(vols, "", " ") + if err != nil { + return errors.Wrapf(err, "error marshalling inspect JSON") + } + fmt.Println(string(jsonOut)) + default: + // It's a Go template. + interfaces := make([]interface{}, len(vols)) + for i, vol := range vols { + interfaces[i] = vol + } + out := formats.StdoutTemplateArray{Output: interfaces, Template: c.Format} + return out.Out() + } + + return nil } diff --git a/cni/87-podman-bridge.conflist b/cni/87-podman-bridge.conflist index a5e241c80..729d543ec 100644 --- a/cni/87-podman-bridge.conflist +++ b/cni/87-podman-bridge.conflist @@ -1,5 +1,5 @@ { - "cniVersion": "0.3.0", + "cniVersion": "0.4.0", "name": "podman", "plugins": [ { @@ -20,6 +20,10 @@ "capabilities": { "portMappings": true } + }, + { + "type": "firewall", + "backend": "iptables" } ] } diff --git a/completions/bash/podman b/completions/bash/podman index 962c15a95..7280f4040 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -1758,6 +1758,7 @@ _podman_mount() { _podman_push() { local boolean_options=" --compress + --digestflag --help -h --quiet diff --git a/docs/podman-build.1.md b/docs/podman-build.1.md index 878b31080..8deb8811e 100644 --- a/docs/podman-build.1.md +++ b/docs/podman-build.1.md @@ -375,18 +375,18 @@ to podman build, the option given would be `--runtime-flag log-format=json`. Security Options - "label=user:USER" : Set the label user for the container - "label=role:ROLE" : Set the label role for the container - "label=type:TYPE" : Set the label type for the container - "label=level:LEVEL" : Set the label level for the container - "label=disable" : Turn off label confinement for the container - "no-new-privileges" : Not supported - - "seccomp=unconfined" : Turn off seccomp confinement for the container - "seccomp=profile.json : White listed syscalls seccomp Json file to be used as a seccomp filter - - "apparmor=unconfined" : Turn off apparmor confinement for the container - "apparmor=your-profile" : Set the apparmor confinement profile for the container +- `apparmor=unconfined` : Turn off apparmor confinement for the container +- `apparmor=your-profile` : Set the apparmor confinement profile for the container + +- `label=user:USER` : Set the label user for the container processes +- `label=role:ROLE` : Set the label role for the container processes +- `label=type:TYPE` : Set the label process type for the container processes +- `label=level:LEVEL` : Set the label level for the container processes +- `label=filetype:TYPE` : Set the label file type for the container files +- `label=disable` : Turn off label separation for the container + +- `seccomp=unconfined` : Turn off seccomp confinement for the container +- `seccomp=profile.json` : White listed syscalls seccomp Json file to be used as a seccomp filter **--shm-size**=*size* diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md index 1377f2a03..7634408f5 100644 --- a/docs/podman-create.1.md +++ b/docs/podman-create.1.md @@ -640,19 +640,20 @@ of the container is assumed to be managed externally. Security Options -"apparmor=unconfined" : Turn off apparmor confinement for the container -"apparmor=your-profile" : Set the apparmor confinement profile for the container +- `apparmor=unconfined` : Turn off apparmor confinement for the container +- `apparmor=your-profile` : Set the apparmor confinement profile for the container -"label=user:USER" : Set the label user for the container -"label=role:ROLE" : Set the label role for the container -"label=type:TYPE" : Set the label type for the container -"label=level:LEVEL" : Set the label level for the container -"label=disable" : Turn off label confinement for the container +- `label=user:USER` : Set the label user for the container processes +- `label=role:ROLE` : Set the label role for the container processes +- `label=type:TYPE` : Set the label process type for the container processes +- `label=level:LEVEL` : Set the label level for the container processes +- `label=filetype:TYPE` : Set the label file type for the container files +- `label=disable` : Turn off label separation for the container -"no-new-privileges" : Disable container processes from gaining additional privileges +- `no-new-privileges` : Disable container processes from gaining additional privileges -"seccomp=unconfined" : Turn off seccomp confinement for the container -"seccomp=profile.json : White listed syscalls seccomp Json file to be used as a seccomp filter +- `seccomp=unconfined` : Turn off seccomp confinement for the container +- `seccomp=profile.json` : White listed syscalls seccomp Json file to be used as a seccomp filter Note: Labeling can be disabled for all containers by setting label=false in the **libpod.conf** (`/etc/containers/libpod.conf`) file. diff --git a/docs/podman-generate-systemd.1.md b/docs/podman-generate-systemd.1.md index ea72fdfae..b4962f28b 100644 --- a/docs/podman-generate-systemd.1.md +++ b/docs/podman-generate-systemd.1.md @@ -4,16 +4,20 @@ podman-generate-systemd- Generate Systemd Unit file ## SYNOPSIS -**podman generate systemd** [*options*] *container* +**podman generate systemd** [*options*] *container|pod* ## DESCRIPTION -**podman generate systemd** will create a Systemd unit file that can be used to control a container. The -command will dynamically create the unit file and output it to stdout where it can be piped by the user -to a file. The options can be used to influence the results of the output as well. +**podman generate systemd** will create a systemd unit file that can be used to control a container or pod. +By default, the command will print the content of the unit files to stdout. +Note that this command is not supported for the remote client. ## OPTIONS: +**--files**, **-f** + +Generate files instead of printing to stdout. The generated files are named {container,pod}-{ID,name}.service and will be placed in the current working directory. + **--name**, **-n** Use the name of the container for the start, stop, and description in the unit file @@ -27,41 +31,66 @@ Set the systemd restart policy. The restart-policy must be one of: "no", "on-su "on-watchdog", "on-abort", or "always". The default policy is *on-failure*. ## Examples -Create a systemd unit file for a container running nginx: +Create and print a systemd unit file for a container running nginx with an *always* restart policy and 1-second timeout to stdout. ``` -$ sudo podman generate systemd nginx +$ podman create --name nginx nginx:latest +$ podman generate systemd --restart-policy=always -t 1 nginx +# container-de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6.service +# autogenerated by Podman 1.5.2 +# Wed Aug 21 09:46:45 CEST 2019 + [Unit] -Description=c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc Podman Container +Description=Podman container-de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6.service +Documentation=man:podman-generate-systemd(1) + [Service] -Restart=on-failure -ExecStart=/usr/bin/podman start c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc -ExecStop=/usr/bin/podman stop -t 10 c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc +Restart=always +ExecStart=/usr/bin/podman start de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6 +ExecStop=/usr/bin/podman stop -t 1 de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6 KillMode=none Type=forking -PIDFile=/var/run/containers/storage/overlay-containers/c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc/userdata/conmon.pid +PIDFile=/run/user/1000/overlay-containers/de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6/userdata/conmon.pid + [Install] WantedBy=multi-user.target ``` -Create a systemd unit file for a container running nginx with an *always* restart policy and 1-second timeout. +Create systemd unit files for a pod with two simple alpine containers. Note that these container services cannot be started or stopped individually via `systemctl`; they are managed by the pod service. You can still use `systemctl status` or journalctl to examine them. ``` -$ sudo podman generate systemd --restart-policy=always -t 1 nginx +$ podman pod create --name systemd-pod +$ podman create --pod systemd-pod alpine top +$ podman create --pod systemd-pod alpine top +$ podman generate systemd --files --name systemd-pod +/home/user/pod-systemd-pod.service +/home/user/container-amazing_chandrasekhar.service +/home/user/container-jolly_shtern.service +$ cat pod-systemd-pod.service +# pod-systemd-pod.service +# autogenerated by Podman 1.5.2 +# Wed Aug 21 09:52:37 CEST 2019 + [Unit] -Description=c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc Podman Container +Description=Podman pod-systemd-pod.service +Documentation=man:podman-generate-systemd(1) +Requires=container-amazing_chandrasekhar.service container-jolly_shtern.service +Before=container-amazing_chandrasekhar.service container-jolly_shtern.service + [Service] -Restart=always -ExecStart=/usr/bin/podman start c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc -ExecStop=/usr/bin/podman stop -t 1 c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc +Restart=on-failure +ExecStart=/usr/bin/podman start 77a818221650-infra +ExecStop=/usr/bin/podman stop -t 10 77a818221650-infra KillMode=none Type=forking -PIDFile=/var/run/containers/storage/overlay-containers/c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc/userdata/conmon.pid +PIDFile=/run/user/1000/overlay-containers/ccfd5c71a088768774ca7bd05888d55cc287698dde06f475c8b02f696a25adcd/userdata/conmon.pid + [Install] WantedBy=multi-user.target ``` ## SEE ALSO -podman(1), podman-container(1) +podman(1), podman-container(1), systemctl(1), systemd.unit(5), systemd.service(5) ## HISTORY +August 2019, Updated with pod support by Valentin Rothberg (rothberg at redhat dot com) April 2019, Originally compiled by Brent Baude (bbaude at redhat dot com) diff --git a/docs/podman-generate.1.md b/docs/podman-generate.1.md index 5a2386778..50050f2c1 100644 --- a/docs/podman-generate.1.md +++ b/docs/podman-generate.1.md @@ -11,10 +11,11 @@ The generate command will create structured output (like YAML) based on a contai ## COMMANDS -| Command | Man Page | Description | -| ------- | --------------------------------------------------- | ---------------------------------------------------------------------------- | -| kube | [podman-generate-kube(1)](podman-generate-kube.1.md)| Generate Kubernetes YAML based on a pod or container. | -| systemd | [podman-generate-systemd(1)](podman-generate-systemd.1.md)| Generate a systemd unit file for a container. | +| Command | Man Page | Description | +|---------|------------------------------------------------------------|-------------------------------------------------------------------------------------| +| kube | [podman-generate-kube(1)](podman-generate-kube.1.md) | Generate Kubernetes YAML based on a pod or container. | +| systemd | [podman-generate-systemd(1)](podman-generate-systemd.1.md) | Generate systemd unit file(s) for a container. Not supported for the remote client. | + ## SEE ALSO podman, podman-pod, podman-container diff --git a/docs/podman-push.1.md b/docs/podman-push.1.md index 2058a432c..29e4044a3 100644 --- a/docs/podman-push.1.md +++ b/docs/podman-push.1.md @@ -61,13 +61,17 @@ value can be entered. The password is entered without echo. **--cert-dir**=*path* Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. -Default certificates directory is _/etc/containers/certs.d_. (Not available for remote commands) (Not available for remote commands) +Default certificates directory is _/etc/containers/certs.d_. (Not available for remote commands) **--compress** Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type, compressed or uncompressed, as source) Note: This flag can only be set when using the **dir** transport +**--digestfile** *Digestfile* + +After copying the image, write the digest of the resulting image to the file. (Not available for remote commands) + **--format**, **-f**=*format* Manifest Type (oci, v2s1, or v2s2) to use when pushing an image to a directory using the 'dir:' transport (default is manifest type of source) @@ -93,19 +97,23 @@ TLS verification will be used unless the target registry is listed as an insecur ## EXAMPLE -This example extracts the imageID image to a local directory in docker format. +This example pushes the image specified by the imageID to a local directory in docker format. `# podman push imageID dir:/path/to/image` -This example extracts the imageID image to a local directory in oci format. +This example pushes the image specified by the imageID to a local directory in oci format. `# podman push imageID oci-archive:/path/to/layout:image:tag` -This example extracts the imageID image to a container registry named registry.example.com +This example pushes the image specified by the imageID to a container registry named registry.example.com `# podman push imageID docker://registry.example.com/repository:tag` -This example extracts the imageID image and puts into the local docker container store +This example pushes the image specified by the imageID to a container registry named registry.example.com and saves the digest in the specified digestfile. + + `# podman push --digestfile=/tmp/mydigest imageID docker://registry.example.com/repository:tag` + +This example pushes the image specified by the imageID and puts it into the local docker container store `# podman push imageID docker-daemon:image:tag` diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md index 4bbf5ce6f..447d4f282 100644 --- a/docs/podman-run.1.md +++ b/docs/podman-run.1.md @@ -665,11 +665,12 @@ Security Options - `apparmor=unconfined` : Turn off apparmor confinement for the container - `apparmor=your-profile` : Set the apparmor confinement profile for the container -- `label=user:USER` : Set the label user for the container -- `label=role:ROLE` : Set the label role for the container -- `label=type:TYPE` : Set the label type for the container -- `label=level:LEVEL` : Set the label level for the container -- `label=disable` : Turn off label confinement for the container +- `label=user:USER` : Set the label user for the container processes +- `label=role:ROLE` : Set the label role for the container processes +- `label=type:TYPE` : Set the label process type for the container processes +- `label=level:LEVEL` : Set the label level for the container processes +- `label=filetype:TYPE` : Set the label file type for the container files +- `label=disable` : Turn off label separation for the container - `no-new-privileges` : Disable container processes from gaining additional privileges diff --git a/docs/podman-stats.1.md b/docs/podman-stats.1.md index c1a87f210..2f604644f 100644 --- a/docs/podman-stats.1.md +++ b/docs/podman-stats.1.md @@ -9,6 +9,10 @@ podman\-stats - Display a live stream of 1 or more containers' resource usage st ## DESCRIPTION Display a live stream of one or more containers' resource usage statistics +Note: Podman stats will not work in rootless environments that use CGroups V1. +Podman stats relies on CGroup information for statistics, and CGroup v1 is not +supported for rootless use cases. + ## OPTIONS **--all**, **-a** @@ -69,14 +73,14 @@ a9f807ffaacd frosty_hodgkin -- 3.092MB / 16.7GB 0.02% -- / -- -- # podman stats --no-stream --format=json a9f80 [ { - "id": "a9f807ffaacd", - "name": "frosty_hodgkin", - "cpu_percent": "--", - "mem_usage": "3.092MB / 16.7GB", - "mem_percent": "0.02%", - "netio": "-- / --", - "blocki": "-- / --", - "pids": "2" + "id": "a9f807ffaacd", + "name": "frosty_hodgkin", + "cpu_percent": "--", + "mem_usage": "3.092MB / 16.7GB", + "mem_percent": "0.02%", + "netio": "-- / --", + "blocki": "-- / --", + "pids": "2" } ] ``` diff --git a/docs/tutorials/rootless_tutorial.md b/docs/tutorials/rootless_tutorial.md index 9453e3855..92595dd02 100644 --- a/docs/tutorials/rootless_tutorial.md +++ b/docs/tutorials/rootless_tutorial.md @@ -46,7 +46,7 @@ The format of this file is USERNAME:UID:RANGE This means the user johndoe is allocated UIDS 100000-165535 as well as their standard UID in the /etc/passwd file. NOTE: this is not currently supported with network installs. These files must be available locally to the host machine. It is not possible to configure this with LDAP or Active Directory. -If you update either the /etc/subuid or the /etc/subgid file, you need to stop all the running containers owned by the user and kill the pause process that is running on the system for that user. This can be done automatically by using the `[podman system migrate](https://github.com/containers/libpod/blob/master/docs/podman-system-migrate.1.md)` command which will stop all the containers for the user and will kill the pause process. +If you update either the /etc/subuid or the /etc/subgid file, you need to stop all the running containers owned by the user and kill the pause process that is running on the system for that user. This can be done automatically by using the [`podman system migrate`](https://github.com/containers/libpod/blob/master/docs/podman-system-migrate.1.md) command which will stop all the containers for the user and will kill the pause process. Rather than updating the files directly, the usermod program can be used to assign UIDs and GIDs to a user. @@ -20,7 +20,6 @@ require ( github.com/containers/storage v1.13.2 github.com/coreos/bbolt v1.3.3 // indirect github.com/coreos/etcd v3.3.13+incompatible // indirect - github.com/coreos/go-iptables v0.4.1 github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a github.com/cri-o/ocicni v0.1.1-0.20190702175919-7762645d18ca @@ -69,7 +68,7 @@ require ( github.com/opencontainers/runc v1.0.0-rc8 github.com/opencontainers/runtime-spec v0.1.2-0.20190618234442-a950415649c7 github.com/opencontainers/runtime-tools v0.9.0 - github.com/opencontainers/selinux v1.2.2 + github.com/opencontainers/selinux v1.3.0 github.com/opentracing/opentracing-go v1.1.0 github.com/pelletier/go-toml v1.4.0 // indirect github.com/pkg/errors v0.8.1 @@ -394,6 +394,8 @@ github.com/opencontainers/selinux v0.0.0-20190118194635-b707dfcb00a1 h1:V8Icxoi2 github.com/opencontainers/selinux v0.0.0-20190118194635-b707dfcb00a1/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs= github.com/opencontainers/selinux v1.2.2 h1:Kx9J6eDG5/24A6DtUquGSpJQ+m2MUTahn4FtGEe8bFg= github.com/opencontainers/selinux v1.2.2/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs= +github.com/opencontainers/selinux v1.3.0 h1:xsI95WzPZu5exzA6JzkLSfdr/DilzOhCJOqGe5TgR0g= +github.com/opencontainers/selinux v1.3.0/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs= github.com/openshift/imagebuilder v1.1.0 h1:oT704SkwMEzmIMU/+Uv1Wmvt+p10q3v2WuYMeFI18c4= github.com/openshift/imagebuilder v1.1.0/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo= github.com/opentracing/opentracing-go v0.0.0-20190218023034-25a84ff92183 h1:kwFCLTA0DYhH0JpGMBOZtVVhyRL5ec+unn4mnoJhQI0= @@ -440,6 +442,8 @@ github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNG github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.8.0/go.mod h1:fSI0j+IUQrDd7+ZtR9WKIGtoYAYAJUKcKhYLG25tN4g= +github.com/rhatdan/oci-selinux v0.0.0-20190809194358-225b95ae1d0b h1:9CE1lDQ/YC1deOJE/elAI+nbE1OzOxSvrs6JXwyn+1s= +github.com/rhatdan/oci-selinux v0.0.0-20190809194358-225b95ae1d0b/go.mod h1:T/CPBeRZLtTvck9OtpX3PGw/uDABnTuRPhyTacu5aSo= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= diff --git a/libpod.conf b/libpod.conf index 3bd3758b8..81fece5d2 100644 --- a/libpod.conf +++ b/libpod.conf @@ -120,7 +120,7 @@ runtime = "runc" # List of the OCI runtimes that support --format=json. When json is supported # libpod will use it for reporting nicer errors. -runtime_supports_json = ["runc"] +runtime_supports_json = ["crun", "runc"] # Paths to look for a valid OCI runtime (runc, runv, etc) # If the paths are empty or no valid path was found, then the `$PATH` diff --git a/libpod/container_api.go b/libpod/container_api.go index abcfcb271..9e59104cc 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -274,6 +274,11 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir } }() + // if the user is empty, we should inherit the user that the container is currently running with + if user == "" { + user = c.config.User + } + pid, attachChan, err := c.ociRuntime.execContainer(c, cmd, capList, env, tty, workDir, user, sessionID, streams, preserveFDs, resize, detachKeys) if err != nil { ec := define.ExecErrorCodeGeneric diff --git a/libpod/container_graph.go b/libpod/container_graph.go index 5aa51bc2f..f6988e1ac 100644 --- a/libpod/container_graph.go +++ b/libpod/container_graph.go @@ -16,14 +16,30 @@ type containerNode struct { dependedOn []*containerNode } -type containerGraph struct { +// ContainerGraph is a dependency graph based on a set of containers. +type ContainerGraph struct { nodes map[string]*containerNode noDepNodes []*containerNode notDependedOnNodes map[string]*containerNode } -func buildContainerGraph(ctrs []*Container) (*containerGraph, error) { - graph := new(containerGraph) +// DependencyMap returns the dependency graph as map with the key being a +// container and the value being the containers the key depends on. +func (cg *ContainerGraph) DependencyMap() (dependencies map[*Container][]*Container) { + dependencies = make(map[*Container][]*Container) + for _, node := range cg.nodes { + dependsOn := make([]*Container, len(node.dependsOn)) + for i, d := range node.dependsOn { + dependsOn[i] = d.container + } + dependencies[node.container] = dependsOn + } + return dependencies +} + +// BuildContainerGraph builds a dependency graph based on the container slice. +func BuildContainerGraph(ctrs []*Container) (*ContainerGraph, error) { + graph := new(ContainerGraph) graph.nodes = make(map[string]*containerNode) graph.notDependedOnNodes = make(map[string]*containerNode) @@ -78,7 +94,7 @@ func buildContainerGraph(ctrs []*Container) (*containerGraph, error) { // Detect cycles in a container graph using Tarjan's strongly connected // components algorithm // Return true if a cycle is found, false otherwise -func detectCycles(graph *containerGraph) (bool, error) { +func detectCycles(graph *ContainerGraph) (bool, error) { type nodeInfo struct { index int lowLink int diff --git a/libpod/container_graph_test.go b/libpod/container_graph_test.go index d1a52658d..38f03c59c 100644 --- a/libpod/container_graph_test.go +++ b/libpod/container_graph_test.go @@ -8,7 +8,7 @@ import ( ) func TestBuildContainerGraphNoCtrsIsEmpty(t *testing.T) { - graph, err := buildContainerGraph([]*Container{}) + graph, err := BuildContainerGraph([]*Container{}) assert.NoError(t, err) assert.Equal(t, 0, len(graph.nodes)) assert.Equal(t, 0, len(graph.noDepNodes)) @@ -24,7 +24,7 @@ func TestBuildContainerGraphOneCtr(t *testing.T) { ctr1, err := getTestCtr1(manager) assert.NoError(t, err) - graph, err := buildContainerGraph([]*Container{ctr1}) + graph, err := BuildContainerGraph([]*Container{ctr1}) assert.NoError(t, err) assert.Equal(t, 1, len(graph.nodes)) assert.Equal(t, 1, len(graph.noDepNodes)) @@ -49,7 +49,7 @@ func TestBuildContainerGraphTwoCtrNoEdge(t *testing.T) { ctr2, err := getTestCtr2(manager) assert.NoError(t, err) - graph, err := buildContainerGraph([]*Container{ctr1, ctr2}) + graph, err := BuildContainerGraph([]*Container{ctr1, ctr2}) assert.NoError(t, err) assert.Equal(t, 2, len(graph.nodes)) assert.Equal(t, 2, len(graph.noDepNodes)) @@ -76,7 +76,7 @@ func TestBuildContainerGraphTwoCtrOneEdge(t *testing.T) { assert.NoError(t, err) ctr2.config.UserNsCtr = ctr1.config.ID - graph, err := buildContainerGraph([]*Container{ctr1, ctr2}) + graph, err := BuildContainerGraph([]*Container{ctr1, ctr2}) assert.NoError(t, err) assert.Equal(t, 2, len(graph.nodes)) assert.Equal(t, 1, len(graph.noDepNodes)) @@ -99,7 +99,7 @@ func TestBuildContainerGraphTwoCtrCycle(t *testing.T) { ctr2.config.UserNsCtr = ctr1.config.ID ctr1.config.NetNsCtr = ctr2.config.ID - _, err = buildContainerGraph([]*Container{ctr1, ctr2}) + _, err = BuildContainerGraph([]*Container{ctr1, ctr2}) assert.Error(t, err) } @@ -116,7 +116,7 @@ func TestBuildContainerGraphThreeCtrNoEdges(t *testing.T) { ctr3, err := getTestCtrN("3", manager) assert.NoError(t, err) - graph, err := buildContainerGraph([]*Container{ctr1, ctr2, ctr3}) + graph, err := BuildContainerGraph([]*Container{ctr1, ctr2, ctr3}) assert.NoError(t, err) assert.Equal(t, 3, len(graph.nodes)) assert.Equal(t, 3, len(graph.noDepNodes)) @@ -150,7 +150,7 @@ func TestBuildContainerGraphThreeContainersTwoInCycle(t *testing.T) { ctr1.config.UserNsCtr = ctr2.config.ID ctr2.config.IPCNsCtr = ctr1.config.ID - _, err = buildContainerGraph([]*Container{ctr1, ctr2, ctr3}) + _, err = BuildContainerGraph([]*Container{ctr1, ctr2, ctr3}) assert.Error(t, err) } @@ -170,7 +170,7 @@ func TestBuildContainerGraphThreeContainersCycle(t *testing.T) { ctr2.config.IPCNsCtr = ctr3.config.ID ctr3.config.NetNsCtr = ctr1.config.ID - _, err = buildContainerGraph([]*Container{ctr1, ctr2, ctr3}) + _, err = BuildContainerGraph([]*Container{ctr1, ctr2, ctr3}) assert.Error(t, err) } @@ -190,7 +190,7 @@ func TestBuildContainerGraphThreeContainersNoCycle(t *testing.T) { ctr1.config.NetNsCtr = ctr3.config.ID ctr2.config.IPCNsCtr = ctr3.config.ID - graph, err := buildContainerGraph([]*Container{ctr1, ctr2, ctr3}) + graph, err := BuildContainerGraph([]*Container{ctr1, ctr2, ctr3}) assert.NoError(t, err) assert.Equal(t, 3, len(graph.nodes)) assert.Equal(t, 1, len(graph.noDepNodes)) @@ -215,7 +215,7 @@ func TestBuildContainerGraphFourContainersNoEdges(t *testing.T) { ctr4, err := getTestCtrN("4", manager) assert.NoError(t, err) - graph, err := buildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4}) + graph, err := BuildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4}) assert.NoError(t, err) assert.Equal(t, 4, len(graph.nodes)) assert.Equal(t, 4, len(graph.noDepNodes)) @@ -256,7 +256,7 @@ func TestBuildContainerGraphFourContainersTwoInCycle(t *testing.T) { ctr1.config.IPCNsCtr = ctr2.config.ID ctr2.config.UserNsCtr = ctr1.config.ID - _, err = buildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4}) + _, err = BuildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4}) assert.Error(t, err) } @@ -280,7 +280,7 @@ func TestBuildContainerGraphFourContainersAllInCycle(t *testing.T) { ctr3.config.NetNsCtr = ctr4.config.ID ctr4.config.UTSNsCtr = ctr1.config.ID - _, err = buildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4}) + _, err = BuildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4}) assert.Error(t, err) } @@ -303,7 +303,7 @@ func TestBuildContainerGraphFourContainersNoneInCycle(t *testing.T) { ctr1.config.NetNsCtr = ctr3.config.ID ctr2.config.UserNsCtr = ctr3.config.ID - graph, err := buildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4}) + graph, err := BuildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4}) assert.NoError(t, err) assert.Equal(t, 4, len(graph.nodes)) assert.Equal(t, 2, len(graph.noDepNodes)) diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 313f67963..f51b53e85 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -788,7 +788,7 @@ func (c *Container) startDependencies(ctx context.Context) error { } // Build a dependency graph of containers - graph, err := buildContainerGraph(depCtrs) + graph, err := BuildContainerGraph(depCtrs) if err != nil { return errors.Wrapf(err, "error generating dependency graph for container %s", c.ID()) } diff --git a/libpod/image/image.go b/libpod/image/image.go index cb7c390c6..1ff271a4d 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "io/ioutil" "os" "path/filepath" "strings" @@ -555,7 +556,7 @@ func (i *Image) UntagImage(tag string) error { // PushImageToHeuristicDestination pushes the given image to "destination", which is heuristically parsed. // Use PushImageToReference if the destination is known precisely. -func (i *Image) PushImageToHeuristicDestination(ctx context.Context, destination, manifestMIMEType, authFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { +func (i *Image) PushImageToHeuristicDestination(ctx context.Context, destination, manifestMIMEType, authFile, digestFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { if destination == "" { return errors.Wrapf(syscall.EINVAL, "destination image name must be specified") } @@ -573,11 +574,11 @@ func (i *Image) PushImageToHeuristicDestination(ctx context.Context, destination return err } } - return i.PushImageToReference(ctx, dest, manifestMIMEType, authFile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, additionalDockerArchiveTags) + return i.PushImageToReference(ctx, dest, manifestMIMEType, authFile, digestFile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, additionalDockerArchiveTags) } // PushImageToReference pushes the given image to a location described by the given path -func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageReference, manifestMIMEType, authFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { +func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageReference, manifestMIMEType, authFile, digestFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { sc := GetSystemContext(signaturePolicyPath, authFile, forceCompress) sc.BlobInfoCacheDir = filepath.Join(i.imageruntime.store.GraphRoot(), "cache") @@ -599,10 +600,22 @@ func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageRefere copyOptions := getCopyOptions(sc, writer, nil, dockerRegistryOptions, signingOptions, manifestMIMEType, additionalDockerArchiveTags) copyOptions.DestinationCtx.SystemRegistriesConfPath = registries.SystemRegistriesConfPath() // FIXME: Set this more globally. Probably no reason not to have it in every types.SystemContext, and to compute the value just once in one place. // Copy the image to the remote destination - _, err = cp.Image(ctx, policyContext, dest, src, copyOptions) + manifestBytes, err := cp.Image(ctx, policyContext, dest, src, copyOptions) if err != nil { return errors.Wrapf(err, "Error copying image to the remote destination") } + digest, err := manifest.Digest(manifestBytes) + if err != nil { + return errors.Wrapf(err, "error computing digest of manifest of new image %q", transports.ImageName(dest)) + } + + logrus.Debugf("Successfully pushed %s with digest %s", transports.ImageName(dest), digest.String()) + + if digestFile != "" { + if err = ioutil.WriteFile(digestFile, []byte(digest.String()), 0644); err != nil { + return errors.Wrapf(err, "failed to write digest to file %q", digestFile) + } + } i.newImageEvent(events.Push) return nil } @@ -1358,7 +1371,7 @@ func (i *Image) Save(ctx context.Context, source, format, output string, moreTag return err } } - if err := i.PushImageToReference(ctx, destRef, manifestType, "", "", writer, compress, SigningOptions{}, &DockerRegistryOptions{}, additionaltags); err != nil { + if err := i.PushImageToReference(ctx, destRef, manifestType, "", "", "", writer, compress, SigningOptions{}, &DockerRegistryOptions{}, additionaltags); err != nil { return errors.Wrapf(err, "unable to save %q", source) } i.newImageEvent(events.Save) diff --git a/libpod/image/pull.go b/libpod/image/pull.go index 78cfe3626..dbf3a4ef5 100644 --- a/libpod/image/pull.go +++ b/libpod/image/pull.go @@ -13,6 +13,7 @@ import ( dockerarchive "github.com/containers/image/docker/archive" "github.com/containers/image/docker/tarfile" ociarchive "github.com/containers/image/oci/archive" + oci "github.com/containers/image/oci/layout" is "github.com/containers/image/storage" "github.com/containers/image/transports" "github.com/containers/image/transports/alltransports" @@ -37,6 +38,9 @@ var ( DirTransport = directory.Transport.Name() // DockerTransport is the transport for docker registries DockerTransport = docker.Transport.Name() + // OCIDirTransport is the transport for pushing and pulling + // images to and from a directory containing an OCI image + OCIDirTransport = oci.Transport.Name() // AtomicTransport is the transport for atomic registries AtomicTransport = "atomic" // DefaultTransport is a prefix that we apply to an image name @@ -189,12 +193,12 @@ func (ir *Runtime) pullGoalFromImageReference(ctx context.Context, srcRef types. return ir.getSinglePullRefPairGoal(srcRef, dest) case DirTransport: - path := srcRef.StringWithinTransport() - image := path - if image[:1] == "/" { - // Set localhost as the registry so docker.io isn't prepended, and the path becomes the repository - image = DefaultLocalRegistry + image - } + image := toLocalImageName(srcRef.StringWithinTransport()) + return ir.getSinglePullRefPairGoal(srcRef, image) + + case OCIDirTransport: + split := strings.SplitN(srcRef.StringWithinTransport(), ":", 2) + image := toLocalImageName(split[0]) return ir.getSinglePullRefPairGoal(srcRef, image) default: @@ -202,6 +206,15 @@ func (ir *Runtime) pullGoalFromImageReference(ctx context.Context, srcRef types. } } +// toLocalImageName converts an image name into a 'localhost/' prefixed one +func toLocalImageName(imageName string) string { + return fmt.Sprintf( + "%s/%s", + DefaultLocalRegistry, + strings.TrimLeft(imageName, "/"), + ) +} + // pullImageFromHeuristicSource pulls an image based on inputName, which is heuristically parsed and may involve configured registries. // Use pullImageFromReference if the source is known precisely. func (ir *Runtime) pullImageFromHeuristicSource(ctx context.Context, inputName string, writer io.Writer, authfile, signaturePolicyPath string, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions, label *string) ([]string, error) { diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index bef3f7739..fd14b2f73 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -17,7 +17,6 @@ import ( cnitypes "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/plugins/pkg/ns" "github.com/containers/libpod/pkg/errorhandling" - "github.com/containers/libpod/pkg/firewall" "github.com/containers/libpod/pkg/netns" "github.com/containers/libpod/pkg/rootless" "github.com/cri-o/ocicni/pkg/ocicni" @@ -86,18 +85,6 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re networkStatus = append(networkStatus, resultCurrent) } - // Add firewall rules to ensure the container has network access. - // Will not be necessary once CNI firewall plugin merges upstream. - // https://github.com/containernetworking/plugins/pull/75 - for _, netStatus := range networkStatus { - firewallConf := &firewall.FirewallNetConf{ - PrevResult: netStatus, - } - if err := r.firewallBackend.Add(firewallConf); err != nil { - return nil, errors.Wrapf(err, "error adding firewall rules for container %s", ctr.ID()) - } - } - return networkStatus, nil } @@ -390,26 +377,12 @@ func (r *Runtime) closeNetNS(ctr *Container) error { } // Tear down a network namespace, undoing all state associated with it. -// The CNI firewall rules will be removed, the namespace will be unmounted, -// and the file descriptor associated with it closed. func (r *Runtime) teardownNetNS(ctr *Container) error { if ctr.state.NetNS == nil { // The container has no network namespace, we're set return nil } - // Remove firewall rules we added on configuring the container. - // Will not be necessary once CNI firewall plugin merges upstream. - // https://github.com/containernetworking/plugins/pull/75 - for _, netStatus := range ctr.state.NetworkStatus { - firewallConf := &firewall.FirewallNetConf{ - PrevResult: netStatus, - } - if err := r.firewallBackend.Del(firewallConf); err != nil { - return errors.Wrapf(err, "error removing firewall rules for container %s", ctr.ID()) - } - } - logrus.Debugf("Tearing down network namespace at %s for container %s", ctr.state.NetNS.Path(), ctr.ID()) var requestedIP net.IP diff --git a/libpod/oci.go b/libpod/oci.go index 2eb004b84..8a873ca5b 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -60,6 +60,7 @@ type OCIRuntime struct { noPivot bool reservePorts bool supportsJSON bool + sdNotify bool } // ociError is used to parse the OCI runtime JSON log. It is not part of the @@ -87,6 +88,7 @@ func newOCIRuntime(name string, paths []string, conmonPath string, runtimeCfg *R runtime.logSizeMax = runtimeCfg.MaxLogSize runtime.noPivot = runtimeCfg.NoPivotRoot runtime.reservePorts = runtimeCfg.EnablePortReservation + runtime.sdNotify = runtimeCfg.SDNotify // TODO: probe OCI runtime for feature and enable automatically if // available. @@ -211,7 +213,7 @@ func bindPorts(ports []ocicni.PortMapping) ([]*os.File, error) { func (r *OCIRuntime) updateContainerStatus(ctr *Container, useRuntime bool) error { exitFile := ctr.exitFilePath() - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return err } @@ -334,7 +336,7 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container, useRuntime bool) erro // Sets time the container was started, but does not save it. func (r *OCIRuntime) startContainer(ctr *Container) error { // TODO: streams should probably *not* be our STDIN/OUT/ERR - redirect to buffers? - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return err } @@ -354,7 +356,7 @@ func (r *OCIRuntime) startContainer(ctr *Container) error { // killContainer sends the given signal to the given container func (r *OCIRuntime) killContainer(ctr *Container, signal uint) error { logrus.Debugf("Sending signal %d to container %s", signal, ctr.ID()) - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return err } @@ -368,7 +370,7 @@ func (r *OCIRuntime) killContainer(ctr *Container, signal uint) error { // deleteContainer deletes a container from the OCI runtime func (r *OCIRuntime) deleteContainer(ctr *Container) error { - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return err } @@ -378,7 +380,7 @@ func (r *OCIRuntime) deleteContainer(ctr *Container) error { // pauseContainer pauses the given container func (r *OCIRuntime) pauseContainer(ctr *Container) error { - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return err } @@ -388,7 +390,7 @@ func (r *OCIRuntime) pauseContainer(ctr *Container) error { // unpauseContainer unpauses the given container func (r *OCIRuntime) unpauseContainer(ctr *Container) error { - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return err } diff --git a/libpod/oci_internal_linux.go b/libpod/oci_internal_linux.go index e2c73f5ed..48b7370e0 100644 --- a/libpod/oci_internal_linux.go +++ b/libpod/oci_internal_linux.go @@ -36,7 +36,7 @@ import ( func (r *OCIRuntime) createOCIContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) (err error) { var stderrBuf bytes.Buffer - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return err } @@ -247,10 +247,14 @@ func (r *OCIRuntime) configureConmonEnv(runtimeDir string) ([]string, []*os.File if notify, ok := os.LookupEnv("NOTIFY_SOCKET"); ok { env = append(env, fmt.Sprintf("NOTIFY_SOCKET=%s", notify)) } - if listenfds, ok := os.LookupEnv("LISTEN_FDS"); ok { - env = append(env, fmt.Sprintf("LISTEN_FDS=%s", listenfds), "LISTEN_PID=1") - fds := activation.Files(false) - extraFiles = append(extraFiles, fds...) + if !r.sdNotify { + if listenfds, ok := os.LookupEnv("LISTEN_FDS"); ok { + env = append(env, fmt.Sprintf("LISTEN_FDS=%s", listenfds), "LISTEN_PID=1") + fds := activation.Files(false) + extraFiles = append(extraFiles, fds...) + } + } else { + logrus.Debug("disabling SD notify") } return env, extraFiles, nil } @@ -445,6 +449,15 @@ func readConmonPipeData(pipe *os.File, ociLog string) (int, error) { select { case ss := <-ch: if ss.err != nil { + if ociLog != "" { + ociLogData, err := ioutil.ReadFile(ociLog) + if err == nil { + var ociErr ociError + if err := json.Unmarshal(ociLogData, &ociErr); err == nil { + return -1, getOCIRuntimeError(ociErr.Msg) + } + } + } return -1, errors.Wrapf(ss.err, "error reading container (probably exited) json message") } logrus.Debugf("Received: %d", ss.si.Data) @@ -472,10 +485,11 @@ func readConmonPipeData(pipe *os.File, ociLog string) (int, error) { } func getOCIRuntimeError(runtimeMsg string) error { - if match, _ := regexp.MatchString(".*permission denied.*", runtimeMsg); match { + r := strings.ToLower(runtimeMsg) + if match, _ := regexp.MatchString(".*permission denied.*|.*operation not permitted.*", r); match { return errors.Wrapf(define.ErrOCIRuntimePermissionDenied, "%s", strings.Trim(runtimeMsg, "\n")) } - if match, _ := regexp.MatchString(".*executable file not found in.*", runtimeMsg); match { + if match, _ := regexp.MatchString(".*executable file not found in.*|.*no such file or directory.*", r); match { return errors.Wrapf(define.ErrOCIRuntimeNotFound, "%s", strings.Trim(runtimeMsg, "\n")) } return errors.Wrapf(define.ErrOCIRuntime, "%s", strings.Trim(runtimeMsg, "\n")) diff --git a/libpod/oci_linux.go b/libpod/oci_linux.go index 45365203e..1613c3e68 100644 --- a/libpod/oci_linux.go +++ b/libpod/oci_linux.go @@ -208,7 +208,7 @@ func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty } }() - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return -1, nil, err } @@ -437,7 +437,7 @@ func (r *OCIRuntime) stopContainer(ctr *Container, timeout uint) error { args = []string{"kill", "--all", ctr.ID(), "KILL"} } - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return err } @@ -487,7 +487,7 @@ func (r *OCIRuntime) execStopContainer(ctr *Container, timeout uint) error { if len(execSessions) == 0 { return nil } - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return err } diff --git a/libpod/options.go b/libpod/options.go index 7fbd0016a..a7ddbec34 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -482,6 +482,15 @@ func WithEventsLogger(logger string) RuntimeOption { } } +// WithEnableSDNotify sets a runtime option so we know whether to disable socket/FD +// listening +func WithEnableSDNotify() RuntimeOption { + return func(rt *Runtime) error { + rt.config.SDNotify = true + return nil + } +} + // Container Creation Options // WithShmDir sets the directory that should be mounted on /dev/shm. @@ -1362,6 +1371,17 @@ func WithNamedVolumes(volumes []*ContainerNamedVolume) CtrCreateOption { } } +// WithHealthCheck adds the healthcheck to the container config +func WithHealthCheck(healthCheck *manifest.Schema2HealthConfig) CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return define.ErrCtrFinalized + } + ctr.config.HealthCheckConfig = healthCheck + return nil + } +} + // Volume Creation Options // WithVolumeName sets the name of the volume. @@ -1381,30 +1401,30 @@ func WithVolumeName(name string) VolumeCreateOption { } } -// WithVolumeLabels sets the labels of the volume. -func WithVolumeLabels(labels map[string]string) VolumeCreateOption { +// WithVolumeDriver sets the volume's driver. +// It is presently not implemented, but will be supported in a future Podman +// release. +func WithVolumeDriver(driver string) VolumeCreateOption { return func(volume *Volume) error { if volume.valid { return define.ErrVolumeFinalized } - volume.config.Labels = make(map[string]string) - for key, value := range labels { - volume.config.Labels[key] = value - } - - return nil + return define.ErrNotImplemented } } -// WithVolumeDriver sets the driver of the volume. -func WithVolumeDriver(driver string) VolumeCreateOption { +// WithVolumeLabels sets the labels of the volume. +func WithVolumeLabels(labels map[string]string) VolumeCreateOption { return func(volume *Volume) error { if volume.valid { return define.ErrVolumeFinalized } - volume.config.Driver = driver + volume.config.Labels = make(map[string]string) + for key, value := range labels { + volume.config.Labels[key] = value + } return nil } @@ -1488,6 +1508,24 @@ func WithPodName(name string) PodCreateOption { } } +// WithPodHostname sets the hostname of the pod. +func WithPodHostname(hostname string) PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + + // Check the hostname against a regex + if !nameRegex.MatchString(hostname) { + return regexError + } + + pod.config.Hostname = hostname + + return nil + } +} + // WithPodLabels sets the labels of a pod. func WithPodLabels(labels map[string]string) PodCreateOption { return func(pod *Pod) error { @@ -1673,14 +1711,3 @@ func WithInfraContainerPorts(bindings []ocicni.PortMapping) PodCreateOption { return nil } } - -// WithHealthCheck adds the healthcheck to the container config -func WithHealthCheck(healthCheck *manifest.Schema2HealthConfig) CtrCreateOption { - return func(ctr *Container) error { - if ctr.valid { - return define.ErrCtrFinalized - } - ctr.config.HealthCheckConfig = healthCheck - return nil - } -} diff --git a/libpod/pod.go b/libpod/pod.go index 60626bfd7..3b9bb9c60 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -36,6 +36,8 @@ type PodConfig struct { // Namespace the pod is in Namespace string `json:"namespace,omitempty"` + Hostname string `json:"hostname,omitempty"` + // Labels contains labels applied to the pod Labels map[string]string `json:"labels"` // CgroupParent contains the pod's CGroup parent diff --git a/libpod/pod_api.go b/libpod/pod_api.go index c7b0353bd..e2448e92a 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -37,7 +37,7 @@ func (p *Pod) Start(ctx context.Context) (map[string]error, error) { } // Build a dependency graph of containers in the pod - graph, err := buildContainerGraph(allCtrs) + graph, err := BuildContainerGraph(allCtrs) if err != nil { return nil, errors.Wrapf(err, "error generating dependency graph for pod %s", p.ID()) } @@ -289,7 +289,7 @@ func (p *Pod) Restart(ctx context.Context) (map[string]error, error) { } // Build a dependency graph of containers in the pod - graph, err := buildContainerGraph(allCtrs) + graph, err := BuildContainerGraph(allCtrs) if err != nil { return nil, errors.Wrapf(err, "error generating dependency graph for pod %s", p.ID()) } diff --git a/libpod/runtime.go b/libpod/runtime.go index 8a4eee081..4d6a80d0b 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -23,7 +23,6 @@ import ( "github.com/containers/libpod/libpod/events" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/libpod/lock" - "github.com/containers/libpod/pkg/firewall" sysreg "github.com/containers/libpod/pkg/registries" "github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/util" @@ -108,7 +107,6 @@ type Runtime struct { netPlugin ocicni.CNIPlugin conmonPath string imageRuntime *image.Runtime - firewallBackend firewall.FirewallBackend lockManager lock.Manager configuredFrom *runtimeConfiguredFrom @@ -252,6 +250,10 @@ type RuntimeConfig struct { EventsLogFilePath string `toml:"-events_logfile_path"` //DetachKeys is the sequence of keys used to detach a container DetachKeys string `toml:"detach_keys"` + + // SDNotify tells Libpod to allow containers to notify the host + // systemd of readiness using the SD_NOTIFY mechanism + SDNotify bool } // runtimeConfiguredFrom is a struct used during early runtime init to help @@ -365,7 +367,7 @@ func SetXdgDirs() error { if runtimeDir == "" { var err error - runtimeDir, err = util.GetRootlessRuntimeDir() + runtimeDir, err = util.GetRuntimeDir() if err != nil { return err } @@ -391,11 +393,11 @@ func getDefaultTmpDir() (string, error) { return "/var/run/libpod", nil } - rootlessRuntimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return "", err } - libpodRuntimeDir := filepath.Join(rootlessRuntimeDir, "libpod") + libpodRuntimeDir := filepath.Join(runtimeDir, "libpod") if err := os.Mkdir(libpodRuntimeDir, 0700|os.ModeSticky); err != nil { if !os.IsExist(err) { @@ -1106,17 +1108,6 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { runtime.netPlugin = netPlugin } - // Set up a firewall backend - backendType := "" - if rootless.IsRootless() { - backendType = "none" - } - fwBackend, err := firewall.GetBackend(backendType) - if err != nil { - return err - } - runtime.firewallBackend = fwBackend - // We now need to see if the system has restarted // We check for the presence of a file in our tmp directory to verify this // This check must be locked to prevent races diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index 5387eb587..ad6662f03 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -31,8 +31,8 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, imgID return nil, err } - // Set Pod hostname as Pod name - g.Config.Hostname = p.config.Name + // Set Pod hostname + g.Config.Hostname = p.config.Hostname isRootless := rootless.IsRootless() diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go index f38e6e7c1..05866d05a 100644 --- a/libpod/runtime_pod_linux.go +++ b/libpod/runtime_pod_linux.go @@ -52,6 +52,10 @@ func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (_ *Po pod.config.Name = name } + if pod.config.Hostname == "" { + pod.config.Hostname = pod.config.Name + } + // Allocate a lock for the pod lock, err := r.lockManager.AllocateLock() if err != nil { @@ -200,7 +204,7 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) // Get the conmon CGroup conmonCgroupPath := filepath.Join(p.state.CgroupPath, "conmon") conmonCgroup, err := cgroups.Load(conmonCgroupPath) - if err != nil && err != cgroups.ErrCgroupDeleted { + if err != nil && err != cgroups.ErrCgroupDeleted && err != cgroups.ErrCgroupV1Rootless { removalErr = errors.Wrapf(err, "error retrieving pod %s conmon cgroup %s", p.ID(), conmonCgroupPath) } @@ -262,7 +266,7 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) // hard - instead, just log errors. conmonCgroupPath := filepath.Join(p.state.CgroupPath, "conmon") conmonCgroup, err := cgroups.Load(conmonCgroupPath) - if err != nil && err != cgroups.ErrCgroupDeleted { + if err != nil && err != cgroups.ErrCgroupDeleted && err != cgroups.ErrCgroupV1Rootless { if removalErr == nil { removalErr = errors.Wrapf(err, "error retrieving pod %s conmon cgroup", p.ID()) } else { @@ -279,7 +283,7 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) } } cgroup, err := cgroups.Load(p.state.CgroupPath) - if err != nil && err != cgroups.ErrCgroupDeleted { + if err != nil && err != cgroups.ErrCgroupDeleted && err != cgroups.ErrCgroupV1Rootless { if removalErr == nil { removalErr = errors.Wrapf(err, "error retrieving pod %s cgroup", p.ID()) } else { diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go index ac6fd02c3..84703787d 100644 --- a/libpod/runtime_volume_linux.go +++ b/libpod/runtime_volume_linux.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "strings" + "time" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/events" @@ -42,14 +43,10 @@ func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption) if volume.config.Name == "" { volume.config.Name = stringid.GenerateNonCryptoID() } - // TODO: support for other volume drivers if volume.config.Driver == "" { volume.config.Driver = "local" } - // TODO: determine when the scope is global and set it to that - if volume.config.Scope == "" { - volume.config.Scope = "local" - } + volume.config.CreatedTime = time.Now() // Create the mountpoint of this volume volPathRoot := filepath.Join(r.config.VolumePath, volume.config.Name) diff --git a/libpod/volume.go b/libpod/volume.go index 9ed2ff087..74126b49b 100644 --- a/libpod/volume.go +++ b/libpod/volume.go @@ -1,5 +1,9 @@ package libpod +import ( + "time" +) + // Volume is the type used to create named volumes // TODO: all volumes should be created using this and the Volume API type Volume struct { @@ -15,10 +19,10 @@ type VolumeConfig struct { Name string `json:"name"` Labels map[string]string `json:"labels"` - MountPoint string `json:"mountPoint"` Driver string `json:"driver"` + MountPoint string `json:"mountPoint"` + CreatedTime time.Time `json:"createdAt,omitempty"` Options map[string]string `json:"options"` - Scope string `json:"scope"` IsCtrSpecific bool `json:"ctrSpecific"` UID int `json:"uid"` GID int `json:"gid"` @@ -29,6 +33,18 @@ func (v *Volume) Name() string { return v.config.Name } +// Driver retrieves the volume's driver. +func (v *Volume) Driver() string { + return v.config.Driver +} + +// Scope retrieves the volume's scope. +// Libpod does not implement volume scoping, and this is provided solely for +// Docker compatibility. It returns only "local". +func (v *Volume) Scope() string { + return "local" +} + // Labels returns the volume's labels func (v *Volume) Labels() map[string]string { labels := make(map[string]string) @@ -43,11 +59,6 @@ func (v *Volume) MountPoint() string { return v.config.MountPoint } -// Driver returns the volume's driver -func (v *Volume) Driver() string { - return v.config.Driver -} - // Options return the volume's options func (v *Volume) Options() map[string]string { options := make(map[string]string) @@ -58,14 +69,25 @@ func (v *Volume) Options() map[string]string { return options } -// Scope returns the scope of the volume -func (v *Volume) Scope() string { - return v.config.Scope -} - // IsCtrSpecific returns whether this volume was created specifically for a // given container. Images with this set to true will be removed when the // container is removed with the Volumes parameter set to true. func (v *Volume) IsCtrSpecific() bool { return v.config.IsCtrSpecific } + +// UID returns the UID the volume will be created as. +func (v *Volume) UID() int { + return v.config.UID +} + +// GID returns the GID the volume will be created as. +func (v *Volume) GID() int { + return v.config.GID +} + +// CreatedTime returns the time the volume was created at. It was not tracked +// for some time, so older volumes may not contain one. +func (v *Volume) CreatedTime() time.Time { + return v.config.CreatedTime +} diff --git a/libpod/volume_inspect.go b/libpod/volume_inspect.go new file mode 100644 index 000000000..87ed9d340 --- /dev/null +++ b/libpod/volume_inspect.go @@ -0,0 +1,70 @@ +package libpod + +import ( + "time" + + "github.com/containers/libpod/libpod/define" +) + +// InspectVolumeData is the output of Inspect() on a volume. It is matched to +// the format of 'docker volume inspect'. +type InspectVolumeData struct { + // Name is the name of the volume. + Name string `json:"Name"` + // Driver is the driver used to create the volume. + // This will be properly implemented in a future version. + Driver string `json:"Driver"` + // Mountpoint is the path on the host where the volume is mounted. + Mountpoint string `json:"Mountpoint"` + // CreatedAt is the date and time the volume was created at. This is not + // stored for older Libpod volumes; if so, it will be omitted. + CreatedAt time.Time `json:"CreatedAt,omitempty"` + // Status is presently unused and provided only for Docker compatibility. + // In the future it will be used to return information on the volume's + // current state. + Status map[string]string `json:"Status,omitempty"` + // Labels includes the volume's configured labels, key:value pairs that + // can be passed during volume creation to provide information for third + // party tools. + Labels map[string]string `json:"Labels"` + // Scope is unused and provided solely for Docker compatibility. It is + // unconditionally set to "local". + Scope string `json:"Scope"` + // Options is a set of options that were used when creating the volume. + // It is presently not used. + Options map[string]string `json:"Options"` + // UID is the UID that the volume was created with. + UID int `json:"UID,omitempty"` + // GID is the GID that the volume was created with. + GID int `json:"GID,omitempty"` + // ContainerSpecific indicates that the volume was created as part of a + // specific container, and will be removed when that container is + // removed. + ContainerSpecific bool `json:"ContainerSpecific,omitempty"` +} + +// Inspect provides detailed information about the configuration of the given +// volume. +func (v *Volume) Inspect() (*InspectVolumeData, error) { + if !v.valid { + return nil, define.ErrVolumeRemoved + } + + data := new(InspectVolumeData) + + data.Name = v.config.Name + data.Driver = v.config.Driver + data.Mountpoint = v.config.MountPoint + data.CreatedAt = v.config.CreatedTime + data.Labels = make(map[string]string) + for k, v := range v.config.Labels { + data.Labels[k] = v + } + data.Scope = v.Scope() + data.Options = make(map[string]string) + data.UID = v.config.UID + data.GID = v.config.GID + data.ContainerSpecific = v.config.IsCtrSpecific + + return data, nil +} diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index 45a9a54a3..41607145d 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -342,7 +342,8 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode if err := ctr.Start(ctx, c.IsSet("pod")); err != nil { // This means the command did not exist exitCode = 127 - if strings.Contains(err.Error(), "permission denied") || strings.Contains(err.Error(), "file not found") { + e := strings.ToLower(err.Error()) + if strings.Contains(e, "permission denied") || strings.Contains(e, "operation not permitted") || strings.Contains(e, "file not found") || strings.Contains(e, "no such file or directory") { exitCode = 126 } return exitCode, err @@ -405,12 +406,13 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode } // This means the command did not exist exitCode = 127 - if strings.Contains(err.Error(), "permission denied") { + e := strings.ToLower(err.Error()) + if strings.Contains(e, "permission denied") || strings.Contains(e, "operation not permitted") { exitCode = 126 } if c.IsSet("rm") { if deleteError := r.Runtime.RemoveContainer(ctx, ctr, true, false); deleteError != nil { - logrus.Errorf("unable to remove container %s after failing to start and attach to it", ctr.ID()) + logrus.Debugf("unable to remove container %s after failing to start and attach to it", ctr.ID()) } } return exitCode, err @@ -1092,28 +1094,145 @@ func (r *LocalRuntime) Port(c *cliconfig.PortValues) ([]*Container, error) { return portContainers, nil } -// GenerateSystemd creates a unit file for a container -func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (string, error) { - ctr, err := r.Runtime.LookupContainer(c.InputArgs[0]) +// generateServiceName generates the container name and the service name for systemd service. +func generateServiceName(c *cliconfig.GenerateSystemdValues, ctr *libpod.Container, pod *libpod.Pod) (string, string) { + var kind, name, ctrName string + if pod == nil { + kind = "container" + name = ctr.ID() + if c.Name { + name = ctr.Name() + } + ctrName = name + } else { + kind = "pod" + name = pod.ID() + ctrName = ctr.ID() + if c.Name { + name = pod.Name() + ctrName = ctr.Name() + } + } + return ctrName, fmt.Sprintf("%s-%s", kind, name) +} + +// generateSystemdgenContainerInfo is a helper to generate a +// systemdgen.ContainerInfo for `GenerateSystemd`. +func (r *LocalRuntime) generateSystemdgenContainerInfo(c *cliconfig.GenerateSystemdValues, nameOrID string, pod *libpod.Pod) (*systemdgen.ContainerInfo, bool, error) { + ctr, err := r.Runtime.LookupContainer(nameOrID) if err != nil { - return "", err + return nil, false, err } + timeout := int(ctr.StopTimeout()) if c.StopTimeout >= 0 { timeout = c.StopTimeout } - name := ctr.ID() - if c.Name { - name = ctr.Name() - } config := ctr.Config() conmonPidFile := config.ConmonPidFile if conmonPidFile == "" { - return "", errors.Errorf("conmon PID file path is empty, try to recreate the container with --conmon-pidfile flag") + return nil, true, errors.Errorf("conmon PID file path is empty, try to recreate the container with --conmon-pidfile flag") + } + + name, serviceName := generateServiceName(c, ctr, pod) + info := &systemdgen.ContainerInfo{ + ServiceName: serviceName, + ContainerName: name, + RestartPolicy: c.RestartPolicy, + PIDFile: conmonPidFile, + StopTimeout: timeout, + GenerateTimestamp: true, + } + + return info, true, nil +} + +// GenerateSystemd creates a unit file for a container or pod. +func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (string, error) { + // First assume it's a container. + if info, found, err := r.generateSystemdgenContainerInfo(c, c.InputArgs[0], nil); found && err != nil { + return "", err + } else if found && err == nil { + return systemdgen.CreateContainerSystemdUnit(info, c.Files) + } + + // We're either having a pod or garbage. + pod, err := r.Runtime.LookupPod(c.InputArgs[0]) + if err != nil { + return "", err + } + + // Error out if the pod has no infra container, which we require to be the + // main service. + if !pod.HasInfraContainer() { + return "", fmt.Errorf("error generating systemd unit files: Pod %q has no infra container", pod.Name()) + } + + // Generate a systemdgen.ContainerInfo for the infra container. This + // ContainerInfo acts as the main service of the pod. + infraID, err := pod.InfraContainerID() + if err != nil { + return "", nil + } + podInfo, _, err := r.generateSystemdgenContainerInfo(c, infraID, pod) + if err != nil { + return "", nil + } + + // Compute the container-dependency graph for the Pod. + containers, err := pod.AllContainers() + if err != nil { + return "", err + } + if len(containers) == 0 { + return "", fmt.Errorf("error generating systemd unit files: Pod %q has no containers", pod.Name()) + } + graph, err := libpod.BuildContainerGraph(containers) + if err != nil { + return "", err + } + + // Traverse the dependency graph and create systemdgen.ContainerInfo's for + // each container. + containerInfos := []*systemdgen.ContainerInfo{podInfo} + for ctr, dependencies := range graph.DependencyMap() { + // Skip the infra container as we already generated it. + if ctr.ID() == infraID { + continue + } + ctrInfo, _, err := r.generateSystemdgenContainerInfo(c, ctr.ID(), nil) + if err != nil { + return "", err + } + // Now add the container's dependencies and at the container as a + // required service of the infra container. + for _, dep := range dependencies { + if dep.ID() == infraID { + ctrInfo.BoundToServices = append(ctrInfo.BoundToServices, podInfo.ServiceName) + } else { + _, serviceName := generateServiceName(c, dep, nil) + ctrInfo.BoundToServices = append(ctrInfo.BoundToServices, serviceName) + } + } + podInfo.RequiredServices = append(podInfo.RequiredServices, ctrInfo.ServiceName) + containerInfos = append(containerInfos, ctrInfo) + } + + // Now generate the systemd service for all containers. + builder := strings.Builder{} + for i, info := range containerInfos { + if i > 0 { + builder.WriteByte('\n') + } + out, err := systemdgen.CreateContainerSystemdUnit(info, c.Files) + if err != nil { + return "", err + } + builder.WriteString(out) } - return systemdgen.CreateSystemdUnitAsString(name, ctr.ID(), c.RestartPolicy, conmonPidFile, timeout) + return builder.String(), nil } // GetNamespaces returns namespace information about a container for PS diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go index 5a26f537f..590fef43f 100644 --- a/pkg/adapter/containers_remote.go +++ b/pkg/adapter/containers_remote.go @@ -951,7 +951,7 @@ func (r *LocalRuntime) Port(c *cliconfig.PortValues) ([]*Container, error) { // GenerateSystemd creates a systemd until for a container func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (string, error) { - return iopodman.GenerateSystemd().Call(r.Conn, c.InputArgs[0], c.RestartPolicy, int64(c.StopTimeout), c.Name) + return "", errors.New("systemd generation not supported for remote clients") } // GetNamespaces returns namespace information about a container for PS diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go index 1dd72babf..ded805de2 100644 --- a/pkg/adapter/pods.go +++ b/pkg/adapter/pods.go @@ -256,6 +256,10 @@ func (r *LocalRuntime) CreatePod(ctx context.Context, cli *cliconfig.PodCreateVa options = append(options, libpod.WithPodName(cli.Name)) } + if cli.Flag("hostname").Changed { + options = append(options, libpod.WithPodHostname(cli.Hostname)) + } + if cli.Infra { options = append(options, libpod.WithInfraContainer()) nsOptions, err := shared.GetNamespaceOptions(strings.Split(cli.Share, ",")) @@ -476,6 +480,12 @@ func (r *LocalRuntime) PlayKubeYAML(ctx context.Context, c *cliconfig.KubePlayVa podOptions = append(podOptions, libpod.WithPodName(podName)) // TODO for now we just used the default kernel namespaces; we need to add/subtract this from yaml + hostname := podYAML.Spec.Hostname + if hostname == "" { + hostname = podName + } + podOptions = append(podOptions, libpod.WithPodHostname(hostname)) + nsOptions, err := shared.GetNamespaceOptions(strings.Split(shared.DefaultKernelNamespaces, ",")) if err != nil { return nil, err diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go index ba988aaf7..0537308f8 100644 --- a/pkg/adapter/runtime.go +++ b/pkg/adapter/runtime.go @@ -201,16 +201,16 @@ func (r *LocalRuntime) RemoveVolumes(ctx context.Context, c *cliconfig.VolumeRmV } // Push is a wrapper to push an image to a registry -func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { +func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, digestfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { newImage, err := r.ImageRuntime().NewFromLocal(srcName) if err != nil { return err } - return newImage.PushImageToHeuristicDestination(ctx, destination, manifestMIMEType, authfile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, nil) + return newImage.PushImageToHeuristicDestination(ctx, destination, manifestMIMEType, authfile, digestfile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, nil) } // InspectVolumes returns a slice of volumes based on an arg list or --all -func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeInspectValues) ([]*Volume, error) { +func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeInspectValues) ([]*libpod.InspectVolumeData, error) { var ( volumes []*libpod.Volume err error @@ -230,7 +230,17 @@ func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeIn if err != nil { return nil, err } - return libpodVolumeToVolume(volumes), nil + + inspectVols := make([]*libpod.InspectVolumeData, 0, len(volumes)) + for _, vol := range volumes { + inspectOut, err := vol.Inspect() + if err != nil { + return nil, errors.Wrapf(err, "error inspecting volume %s", vol.Name()) + } + inspectVols = append(inspectVols, inspectOut) + } + + return inspectVols, nil } // Volumes returns a slice of localruntime volumes diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go index 420c9d0bb..8588966b6 100644 --- a/pkg/adapter/runtime_remote.go +++ b/pkg/adapter/runtime_remote.go @@ -21,7 +21,7 @@ import ( "github.com/containers/image/types" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/remoteclientconfig" - "github.com/containers/libpod/cmd/podman/varlink" + iopodman "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/events" @@ -619,7 +619,7 @@ func (r *LocalRuntime) RemoveVolumes(ctx context.Context, c *cliconfig.VolumeRmV return iopodman.VolumeRemove().Call(r.Conn, rmOpts) } -func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { +func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, digestfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { reply, err := iopodman.PushImage().Send(r.Conn, varlink.More, srcName, destination, forceCompress, manifestMIMEType, signingOptions.RemoveSignatures, signingOptions.SignBy) if err != nil { @@ -669,7 +669,6 @@ func varlinkVolumeToVolume(r *LocalRuntime, volumes []iopodman.Volume) []*Volume MountPoint: v.MountPoint, Driver: v.Driver, Options: v.Options, - Scope: v.Scope, } n := remoteVolume{ Runtime: r, @@ -813,7 +812,7 @@ func IsImageNotFound(err error) bool { // HealthCheck executes a container's healthcheck over a varlink connection func (r *LocalRuntime) HealthCheck(c *cliconfig.HealthCheckValues) (string, error) { - return "", define.ErrNotImplemented + return iopodman.HealthCheckRun().Call(r.Conn, c.InputArgs[0]) } // Events monitors libpod/podman events over a varlink connection diff --git a/pkg/adapter/volumes_remote.go b/pkg/adapter/volumes_remote.go index beacd943a..58f9ba625 100644 --- a/pkg/adapter/volumes_remote.go +++ b/pkg/adapter/volumes_remote.go @@ -29,5 +29,5 @@ func (v *Volume) MountPoint() string { // Scope returns the scope for an adapter.volume func (v *Volume) Scope() string { - return v.config.Scope + return "local" } diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go index 085718855..9711e8120 100644 --- a/pkg/cgroups/cgroups.go +++ b/pkg/cgroups/cgroups.go @@ -10,6 +10,7 @@ import ( "strconv" "strings" + "github.com/containers/libpod/pkg/rootless" systemdDbus "github.com/coreos/go-systemd/dbus" "github.com/godbus/dbus" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -19,7 +20,9 @@ import ( var ( // ErrCgroupDeleted means the cgroup was deleted - ErrCgroupDeleted = errors.New("cgroups: cgroup deleted") + ErrCgroupDeleted = errors.New("cgroup deleted") + // ErrCgroupV1Rootless means the cgroup v1 were attempted to be used in rootless environmen + ErrCgroupV1Rootless = errors.New("no support for CGroups V1 in rootless environments") ) // CgroupControl controls a cgroup hierarchy @@ -339,6 +342,9 @@ func Load(path string) (*CgroupControl, error) { p := control.getCgroupv1Path(name) if _, err := os.Stat(p); err != nil { if os.IsNotExist(err) { + if rootless.IsRootless() { + return nil, ErrCgroupV1Rootless + } // compatible with the error code // used by containerd/cgroups return nil, ErrCgroupDeleted diff --git a/pkg/firewall/common.go b/pkg/firewall/common.go deleted file mode 100644 index a65d4f03d..000000000 --- a/pkg/firewall/common.go +++ /dev/null @@ -1,55 +0,0 @@ -package firewall - -// Copyright 2016 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import ( - "net" - - "github.com/containernetworking/cni/pkg/types/current" -) - -// FirewallNetConf represents the firewall configuration. -// Nolint applied for firewall.Firewall... name duplication notice. -//nolint -type FirewallNetConf struct { - //types.NetConf - - // IptablesAdminChainName is an optional name to use instead of the default - // admin rules override chain name that includes the interface name. - IptablesAdminChainName string - - // FirewalldZone is an optional firewalld zone to place the interface into. If - // the firewalld backend is used but the zone is not given, it defaults - // to 'trusted' - FirewalldZone string - - PrevResult *current.Result -} - -// FirewallBackend is an interface to the system firewall, allowing addition and -// removal of firewall rules. -// Nolint applied for firewall.Firewall... name duplication notice. -//nolint -type FirewallBackend interface { - Add(*FirewallNetConf) error - Del(*FirewallNetConf) error -} - -func ipString(ip net.IPNet) string { - if ip.IP.To4() == nil { - return ip.IP.String() + "/128" - } - return ip.IP.String() + "/32" -} diff --git a/pkg/firewall/firewall_linux.go b/pkg/firewall/firewall_linux.go deleted file mode 100644 index 4ac45427b..000000000 --- a/pkg/firewall/firewall_linux.go +++ /dev/null @@ -1,47 +0,0 @@ -// +build linux - -// Copyright 2016 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package firewall - -import ( - "fmt" -) - -// GetBackend retrieves a firewall backend for adding or removing firewall rules -// on the system. -// Valid backend names are firewalld, iptables, and none. -// If the empty string is given, a firewalld backend will be returned if -// firewalld is running, and an iptables backend will be returned otherwise. -func GetBackend(backend string) (FirewallBackend, error) { - switch backend { - case "firewalld": - return newFirewalldBackend() - case "iptables": - return newIptablesBackend() - case "none": - return newNoneBackend() - case "": - // Default to firewalld if it's running - if isFirewalldRunning() { - return newFirewalldBackend() - } - - // Otherwise iptables - return newIptablesBackend() - default: - return nil, fmt.Errorf("unrecognized firewall backend %q", backend) - } -} diff --git a/pkg/firewall/firewall_none.go b/pkg/firewall/firewall_none.go deleted file mode 100644 index 9add24842..000000000 --- a/pkg/firewall/firewall_none.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package firewall - -import ( - "fmt" -) - -// FirewallNone is a firewall backend for environments where manipulating the -// system firewall is unsupported (for example, when running without root). -// Nolint applied to avoid firewall.FirewallNone name duplication notes. -//nolint -type FirewallNone struct{} - -func newNoneBackend() (FirewallBackend, error) { - return &FirewallNone{}, nil -} - -// Add adds a rule to the system firewall. -// No action is taken and an error is unconditionally returned as this backend -// does not support manipulating the firewall. -func (f *FirewallNone) Add(conf *FirewallNetConf) error { - return fmt.Errorf("cannot modify system firewall rules") -} - -// Del deletes a rule from the system firewall. -// No action is taken and an error is unconditionally returned as this backend -// does not support manipulating the firewall. -func (f *FirewallNone) Del(conf *FirewallNetConf) error { - return fmt.Errorf("cannot modify system firewall rules") -} diff --git a/pkg/firewall/firewall_unsupported.go b/pkg/firewall/firewall_unsupported.go deleted file mode 100644 index 24c07a8a9..000000000 --- a/pkg/firewall/firewall_unsupported.go +++ /dev/null @@ -1,27 +0,0 @@ -// +build !linux - -// Copyright 2016 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package firewall - -import ( - "fmt" -) - -// GetBackend retrieves a firewall backend for adding or removing firewall rules -// on the system. -func GetBackend(backend string) (FirewallBackend, error) { - return nil, fmt.Errorf("firewall backends are not presently supported on this OS") -} diff --git a/pkg/firewall/firewalld.go b/pkg/firewall/firewalld.go deleted file mode 100644 index 15e845cb7..000000000 --- a/pkg/firewall/firewalld.go +++ /dev/null @@ -1,122 +0,0 @@ -// +build linux - -// Copyright 2018 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package firewall - -import ( - "fmt" - "github.com/sirupsen/logrus" - "strings" - - "github.com/godbus/dbus" -) - -const ( - dbusName = "org.freedesktop.DBus" - dbusPath = "/org/freedesktop/DBus" - dbusGetNameOwnerMethod = "GetNameOwner" - - firewalldName = "org.fedoraproject.FirewallD1" - firewalldPath = "/org/fedoraproject/FirewallD1" - firewalldZoneInterface = "org.fedoraproject.FirewallD1.zone" - firewalldAddSourceMethod = "addSource" - firewalldRemoveSourceMethod = "removeSource" - - errZoneAlreadySet = "ZONE_ALREADY_SET" -) - -// Only used for testcases to override the D-Bus connection -var testConn *dbus.Conn - -type fwdBackend struct { - conn *dbus.Conn -} - -// fwdBackend implements the FirewallBackend interface -var _ FirewallBackend = &fwdBackend{} - -func getConn() (*dbus.Conn, error) { - if testConn != nil { - return testConn, nil - } - return dbus.SystemBus() -} - -// isFirewalldRunning checks whether firewalld is running. -func isFirewalldRunning() bool { - conn, err := getConn() - if err != nil { - return false - } - - dbusObj := conn.Object(dbusName, dbusPath) - var res string - if err := dbusObj.Call(dbusName+"."+dbusGetNameOwnerMethod, 0, firewalldName).Store(&res); err != nil { - return false - } - - return true -} - -func newFirewalldBackend() (FirewallBackend, error) { - conn, err := getConn() - if err != nil { - return nil, err - } - - backend := &fwdBackend{ - conn: conn, - } - return backend, nil -} - -func getFirewalldZone(conf *FirewallNetConf) string { - if conf.FirewalldZone != "" { - return conf.FirewalldZone - } - - return "trusted" -} - -func (fb *fwdBackend) Add(conf *FirewallNetConf) error { - zone := getFirewalldZone(conf) - - for _, ip := range conf.PrevResult.IPs { - ipStr := ipString(ip.Address) - // Add a firewalld rule which assigns the given source IP to the given zone - firewalldObj := fb.conn.Object(firewalldName, firewalldPath) - var res string - if err := firewalldObj.Call(firewalldZoneInterface+"."+firewalldAddSourceMethod, 0, zone, ipStr).Store(&res); err != nil { - if !strings.Contains(err.Error(), errZoneAlreadySet) { - return fmt.Errorf("failed to add the address %v to %v zone: %v", ipStr, zone, err) - } - } - } - return nil -} - -func (fb *fwdBackend) Del(conf *FirewallNetConf) error { - for _, ip := range conf.PrevResult.IPs { - ipStr := ipString(ip.Address) - // Remove firewalld rules which assigned the given source IP to the given zone - firewalldObj := fb.conn.Object(firewalldName, firewalldPath) - var res string - if err := firewalldObj.Call(firewalldZoneInterface+"."+firewalldRemoveSourceMethod, 0, getFirewalldZone(conf), ipStr).Store(&res); err != nil { - logrus.Errorf("unable to store firewallobj") - } - } - return nil -} diff --git a/pkg/firewall/iptables.go b/pkg/firewall/iptables.go deleted file mode 100644 index 169ddc1d7..000000000 --- a/pkg/firewall/iptables.go +++ /dev/null @@ -1,195 +0,0 @@ -// +build linux - -// Copyright 2016 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// This is a "meta-plugin". It reads in its own netconf, it does not create -// any network interface but just changes the network sysctl. - -package firewall - -import ( - "fmt" - "github.com/sirupsen/logrus" - "net" - - "github.com/coreos/go-iptables/iptables" -) - -func getPrivChainRules(ip string) [][]string { - var rules [][]string - rules = append(rules, []string{"-d", ip, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}) - rules = append(rules, []string{"-s", ip, "-j", "ACCEPT"}) - return rules -} - -func ensureChain(ipt *iptables.IPTables, table, chain string) error { - chains, err := ipt.ListChains(table) - if err != nil { - return fmt.Errorf("failed to list iptables chains: %v", err) - } - for _, ch := range chains { - if ch == chain { - return nil - } - } - - return ipt.NewChain(table, chain) -} - -func generateFilterRule(privChainName string) []string { - return []string{"-m", "comment", "--comment", "CNI firewall plugin rules", "-j", privChainName} -} - -func cleanupRules(ipt *iptables.IPTables, privChainName string, rules [][]string) { - for _, rule := range rules { - if err := ipt.Delete("filter", privChainName, rule...); err != nil { - logrus.Errorf("failed to delete iptables rule %s", privChainName) - } - } -} - -func ensureFirstChainRule(ipt *iptables.IPTables, chain string, rule []string) error { - exists, err := ipt.Exists("filter", chain, rule...) - if !exists && err == nil { - err = ipt.Insert("filter", chain, 1, rule...) - } - return err -} - -func (ib *iptablesBackend) setupChains(ipt *iptables.IPTables) error { - privRule := generateFilterRule(ib.privChainName) - adminRule := generateFilterRule(ib.adminChainName) - - // Ensure our private chains exist - if err := ensureChain(ipt, "filter", ib.privChainName); err != nil { - return err - } - if err := ensureChain(ipt, "filter", ib.adminChainName); err != nil { - return err - } - - // Ensure our filter rule exists in the forward chain - if err := ensureFirstChainRule(ipt, "FORWARD", privRule); err != nil { - return err - } - - // Ensure our admin override chain rule exists in our private chain - if err := ensureFirstChainRule(ipt, ib.privChainName, adminRule); err != nil { - return err - } - - return nil -} - -func protoForIP(ip net.IPNet) iptables.Protocol { - if ip.IP.To4() != nil { - return iptables.ProtocolIPv4 - } - return iptables.ProtocolIPv6 -} - -func (ib *iptablesBackend) addRules(conf *FirewallNetConf, ipt *iptables.IPTables, proto iptables.Protocol) error { - rules := make([][]string, 0) - for _, ip := range conf.PrevResult.IPs { - if protoForIP(ip.Address) == proto { - rules = append(rules, getPrivChainRules(ipString(ip.Address))...) - } - } - - if len(rules) > 0 { - if err := ib.setupChains(ipt); err != nil { - return err - } - - // Clean up on any errors - var err error - defer func() { - if err != nil { - cleanupRules(ipt, ib.privChainName, rules) - } - }() - - for _, rule := range rules { - err = ipt.AppendUnique("filter", ib.privChainName, rule...) - if err != nil { - return err - } - } - } - - return nil -} - -func (ib *iptablesBackend) delRules(conf *FirewallNetConf, ipt *iptables.IPTables, proto iptables.Protocol) error { - rules := make([][]string, 0) - for _, ip := range conf.PrevResult.IPs { - if protoForIP(ip.Address) == proto { - rules = append(rules, getPrivChainRules(ipString(ip.Address))...) - } - } - - if len(rules) > 0 { - cleanupRules(ipt, ib.privChainName, rules) - } - - return nil -} - -type iptablesBackend struct { - protos map[iptables.Protocol]*iptables.IPTables - privChainName string - adminChainName string -} - -// iptablesBackend implements the FirewallBackend interface -var _ FirewallBackend = &iptablesBackend{} - -func newIptablesBackend() (FirewallBackend, error) { - adminChainName := "CNI-ADMIN" - - backend := &iptablesBackend{ - privChainName: "CNI-FORWARD", - adminChainName: adminChainName, - protos: make(map[iptables.Protocol]*iptables.IPTables), - } - - for _, proto := range []iptables.Protocol{iptables.ProtocolIPv4, iptables.ProtocolIPv6} { - ipt, err := iptables.NewWithProtocol(proto) - if err != nil { - return nil, fmt.Errorf("could not initialize iptables protocol %v: %v", proto, err) - } - backend.protos[proto] = ipt - } - - return backend, nil -} - -func (ib *iptablesBackend) Add(conf *FirewallNetConf) error { - for proto, ipt := range ib.protos { - if err := ib.addRules(conf, ipt, proto); err != nil { - return err - } - } - return nil -} - -func (ib *iptablesBackend) Del(conf *FirewallNetConf) error { - for proto, ipt := range ib.protos { - if err := ib.delRules(conf, ipt, proto); err != nil { - logrus.Errorf("failed to delete iptables backend rule %s", conf.IptablesAdminChainName) - } - } - return nil -} diff --git a/pkg/systemdgen/systemdgen.go b/pkg/systemdgen/systemdgen.go index 06c5ebde5..09d3c6fd5 100644 --- a/pkg/systemdgen/systemdgen.go +++ b/pkg/systemdgen/systemdgen.go @@ -1,29 +1,59 @@ package systemdgen import ( + "bytes" "fmt" + "io/ioutil" "os" + "path/filepath" + "sort" + "text/template" + "time" + "github.com/containers/libpod/version" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) -var template = `[Unit] -Description=%s Podman Container -[Service] -Restart=%s -ExecStart=%s start %s -ExecStop=%s stop -t %d %s -KillMode=none -Type=forking -PIDFile=%s -[Install] -WantedBy=multi-user.target` +// ContainerInfo contains data required for generating a container's systemd +// unit file. +type ContainerInfo struct { + // ServiceName of the systemd service. + ServiceName string + // Name or ID of the container. + ContainerName string + // InfraContainer of the pod. + InfraContainer string + // StopTimeout sets the timeout Podman waits before killing the container + // during service stop. + StopTimeout int + // RestartPolicy of the systemd unit (e.g., no, on-failure, always). + RestartPolicy string + // PIDFile of the service. Required for forking services. Must point to the + // PID of the associated conmon process. + PIDFile string + // GenerateTimestamp, if set the generated unit file has a time stamp. + GenerateTimestamp bool + // BoundToServices are the services this service binds to. Note that this + // service runs after them. + BoundToServices []string + // RequiredServices are services this service requires. Note that this + // service runs before them. + RequiredServices []string + // PodmanVersion for the header. Will be set internally. Will be auto-filled + // if left empty. + PodmanVersion string + // Executable is the path to the podman executable. Will be auto-filled if + // left empty. + Executable string + // TimeStamp at the time of creating the unit file. Will be set internally. + TimeStamp string +} var restartPolicies = []string{"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", "always"} -// ValidateRestartPolicy checks that the user-provided policy is valid -func ValidateRestartPolicy(restart string) error { +// validateRestartPolicy checks that the user-provided policy is valid. +func validateRestartPolicy(restart string) error { for _, i := range restartPolicies { if i == restart { return nil @@ -32,28 +62,87 @@ func ValidateRestartPolicy(restart string) error { return errors.Errorf("%s is not a valid restart policy", restart) } -// CreateSystemdUnitAsString takes variables to create a systemd unit file used to control -// a libpod container -func CreateSystemdUnitAsString(name, cid, restart, pidFile string, stopTimeout int) (string, error) { - podmanExe := getPodmanExecutable() - return createSystemdUnitAsString(podmanExe, name, cid, restart, pidFile, stopTimeout) -} +const containerTemplate = `# {{.ServiceName}}.service +# autogenerated by Podman {{.PodmanVersion}} +{{- if .TimeStamp}} +# {{.TimeStamp}} +{{- end}} + +[Unit] +Description=Podman {{.ServiceName}}.service +Documentation=man:podman-generate-systemd(1) +{{- if .BoundToServices}} +RefuseManualStart=yes +RefuseManualStop=yes +BindsTo={{- range $index, $value := .BoundToServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}} +After={{- range $index, $value := .BoundToServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}} +{{- end}} +{{- if .RequiredServices}} +Requires={{- range $index, $value := .RequiredServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}} +Before={{- range $index, $value := .RequiredServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}} +{{- end}} + +[Service] +Restart={{.RestartPolicy}} +ExecStart={{.Executable}} start {{.ContainerName}} +ExecStop={{.Executable}} stop {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}} {{.ContainerName}} +KillMode=none +Type=forking +PIDFile={{.PIDFile}} + +[Install] +WantedBy=multi-user.target` -func createSystemdUnitAsString(exe, name, cid, restart, pidFile string, stopTimeout int) (string, error) { - if err := ValidateRestartPolicy(restart); err != nil { +// CreateContainerSystemdUnit creates a systemd unit file for a container. +func CreateContainerSystemdUnit(info *ContainerInfo, generateFiles bool) (string, error) { + if err := validateRestartPolicy(info.RestartPolicy); err != nil { return "", err } - unit := fmt.Sprintf(template, name, restart, exe, name, exe, stopTimeout, name, pidFile) - return unit, nil -} + // Make sure the executable is set. + if info.Executable == "" { + executable, err := os.Executable() + if err != nil { + executable = "/usr/bin/podman" + logrus.Warnf("Could not obtain podman executable location, using default %s", executable) + } + info.Executable = executable + } + + if info.PodmanVersion == "" { + info.PodmanVersion = version.Version + } + if info.GenerateTimestamp { + info.TimeStamp = fmt.Sprintf("%v", time.Now().Format(time.UnixDate)) + } -func getPodmanExecutable() string { - podmanExe, err := os.Executable() + // Sort the slices to assure a deterministic output. + sort.Strings(info.RequiredServices) + sort.Strings(info.BoundToServices) + + // Generate the template and compile it. + templ, err := template.New("systemd_service_file").Parse(containerTemplate) if err != nil { - podmanExe = "/usr/bin/podman" - logrus.Warnf("Could not obtain podman executable location, using default %s", podmanExe) + return "", errors.Wrap(err, "error parsing systemd service template") } - return podmanExe + var buf bytes.Buffer + if err := templ.Execute(&buf, info); err != nil { + return "", err + } + + if !generateFiles { + return buf.String(), nil + } + + buf.WriteByte('\n') + cwd, err := os.Getwd() + if err != nil { + return "", errors.Wrap(err, "error getting current working directory") + } + path := filepath.Join(cwd, fmt.Sprintf("%s.service", info.ServiceName)) + if err := ioutil.WriteFile(path, buf.Bytes(), 0644); err != nil { + return "", errors.Wrap(err, "error generating systemd unit") + } + return path, nil } diff --git a/pkg/systemdgen/systemdgen_test.go b/pkg/systemdgen/systemdgen_test.go index e413b24ce..1ddb0c514 100644 --- a/pkg/systemdgen/systemdgen_test.go +++ b/pkg/systemdgen/systemdgen_test.go @@ -5,36 +5,41 @@ import ( ) func TestValidateRestartPolicy(t *testing.T) { - type args struct { + type ContainerInfo struct { restart string } tests := []struct { - name string - args args - wantErr bool + name string + ContainerInfo ContainerInfo + wantErr bool }{ - {"good-on", args{restart: "no"}, false}, - {"good-on-success", args{restart: "on-success"}, false}, - {"good-on-failure", args{restart: "on-failure"}, false}, - {"good-on-abnormal", args{restart: "on-abnormal"}, false}, - {"good-on-watchdog", args{restart: "on-watchdog"}, false}, - {"good-on-abort", args{restart: "on-abort"}, false}, - {"good-always", args{restart: "always"}, false}, - {"fail", args{restart: "foobar"}, true}, - {"failblank", args{restart: ""}, true}, + {"good-on", ContainerInfo{restart: "no"}, false}, + {"good-on-success", ContainerInfo{restart: "on-success"}, false}, + {"good-on-failure", ContainerInfo{restart: "on-failure"}, false}, + {"good-on-abnormal", ContainerInfo{restart: "on-abnormal"}, false}, + {"good-on-watchdog", ContainerInfo{restart: "on-watchdog"}, false}, + {"good-on-abort", ContainerInfo{restart: "on-abort"}, false}, + {"good-always", ContainerInfo{restart: "always"}, false}, + {"fail", ContainerInfo{restart: "foobar"}, true}, + {"failblank", ContainerInfo{restart: ""}, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := ValidateRestartPolicy(tt.args.restart); (err != nil) != tt.wantErr { + if err := validateRestartPolicy(tt.ContainerInfo.restart); (err != nil) != tt.wantErr { t.Errorf("ValidateRestartPolicy() error = %v, wantErr %v", err, tt.wantErr) } }) } } -func TestCreateSystemdUnitAsString(t *testing.T) { - goodID := `[Unit] -Description=639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 Podman Container +func TestCreateContainerSystemdUnit(t *testing.T) { + goodID := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service +# autogenerated by Podman CI + +[Unit] +Description=Podman container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service +Documentation=man:podman-generate-systemd(1) + [Service] Restart=always ExecStart=/usr/bin/podman start 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 @@ -42,11 +47,17 @@ ExecStop=/usr/bin/podman stop -t 10 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6 KillMode=none Type=forking PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid + [Install] WantedBy=multi-user.target` - goodName := `[Unit] -Description=foobar Podman Container + goodName := `# container-foobar.service +# autogenerated by Podman CI + +[Unit] +Description=Podman container-foobar.service +Documentation=man:podman-generate-systemd(1) + [Service] Restart=always ExecStart=/usr/bin/podman start foobar @@ -54,56 +65,121 @@ ExecStop=/usr/bin/podman stop -t 10 foobar KillMode=none Type=forking PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid + +[Install] +WantedBy=multi-user.target` + + goodNameBoundTo := `# container-foobar.service +# autogenerated by Podman CI + +[Unit] +Description=Podman container-foobar.service +Documentation=man:podman-generate-systemd(1) +RefuseManualStart=yes +RefuseManualStop=yes +BindsTo=a.service b.service c.service pod.service +After=a.service b.service c.service pod.service + +[Service] +Restart=always +ExecStart=/usr/bin/podman start foobar +ExecStop=/usr/bin/podman stop -t 10 foobar +KillMode=none +Type=forking +PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid + +[Install] +WantedBy=multi-user.target` + + podGoodName := `# pod-123abc.service +# autogenerated by Podman CI + +[Unit] +Description=Podman pod-123abc.service +Documentation=man:podman-generate-systemd(1) +Requires=container-1.service container-2.service +Before=container-1.service container-2.service + +[Service] +Restart=always +ExecStart=/usr/bin/podman start jadda-jadda-infra +ExecStop=/usr/bin/podman stop -t 10 jadda-jadda-infra +KillMode=none +Type=forking +PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid + [Install] WantedBy=multi-user.target` - type args struct { - exe string - name string - cid string - restart string - pidFile string - stopTimeout int - } tests := []struct { name string - args args + info ContainerInfo want string wantErr bool }{ {"good with id", - args{ - "/usr/bin/podman", - "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", - "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", - "always", - "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - 10, + ContainerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + ContainerName: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", }, goodID, false, }, {"good with name", - args{ - "/usr/bin/podman", - "foobar", - "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", - "always", - "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - 10, + ContainerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "container-foobar", + ContainerName: "foobar", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", }, goodName, false, }, + {"good with name and bound to", + ContainerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "container-foobar", + ContainerName: "foobar", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + BoundToServices: []string{"pod", "a", "b", "c"}, + }, + goodNameBoundTo, + false, + }, + {"pod", + ContainerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "pod-123abc", + ContainerName: "jadda-jadda-infra", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + RequiredServices: []string{"container-1", "container-2"}, + }, + podGoodName, + false, + }, {"bad restart policy", - args{ - "/usr/bin/podman", - "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", - "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", - "never", - "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - 10, + ContainerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + RestartPolicy: "never", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", }, "", true, @@ -111,13 +187,13 @@ WantedBy=multi-user.target` } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := createSystemdUnitAsString(tt.args.exe, tt.args.name, tt.args.cid, tt.args.restart, tt.args.pidFile, tt.args.stopTimeout) + got, err := CreateContainerSystemdUnit(&tt.info, false) if (err != nil) != tt.wantErr { - t.Errorf("CreateSystemdUnitAsString() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("CreateContainerSystemdUnit() error = \n%v, wantErr \n%v", err, tt.wantErr) return } if got != tt.want { - t.Errorf("CreateSystemdUnitAsString() = %v, want %v", got, tt.want) + t.Errorf("CreateContainerSystemdUnit() = \n%v, want \n%v", got, tt.want) } }) } diff --git a/pkg/util/utils_supported.go b/pkg/util/utils_supported.go index c7c8787a0..707e193e9 100644 --- a/pkg/util/utils_supported.go +++ b/pkg/util/utils_supported.go @@ -16,8 +16,8 @@ import ( "github.com/sirupsen/logrus" ) -// GetRootlessRuntimeDir returns the runtime directory when running as non root -func GetRootlessRuntimeDir() (string, error) { +// GetRuntimeDir returns the runtime directory +func GetRuntimeDir() (string, error) { var rootlessRuntimeDirError error rootlessRuntimeDirOnce.Do(func() { @@ -100,7 +100,7 @@ func GetRootlessConfigHomeDir() (string, error) { // GetRootlessPauseProcessPidPath returns the path to the file that holds the pid for // the pause process func GetRootlessPauseProcessPidPath() (string, error) { - runtimeDir, err := GetRootlessRuntimeDir() + runtimeDir, err := GetRuntimeDir() if err != nil { return "", err } diff --git a/pkg/util/utils_windows.go b/pkg/util/utils_windows.go index e781e6717..9bba2d1ee 100644 --- a/pkg/util/utils_windows.go +++ b/pkg/util/utils_windows.go @@ -25,12 +25,12 @@ func GetRootlessPauseProcessPidPath() (string, error) { return "", errors.Wrap(errNotImplemented, "GetRootlessPauseProcessPidPath") } -// GetRootlessRuntimeDir returns the runtime directory when running as non root -func GetRootlessRuntimeDir() (string, error) { - return "", errors.Wrap(errNotImplemented, "GetRootlessRuntimeDir") +// GetRuntimeDir returns the runtime directory +func GetRuntimeDir() (string, error) { + return "", errors.New("this function is not implemented for windows") } // GetRootlessConfigHomeDir returns the config home directory when running as non root func GetRootlessConfigHomeDir() (string, error) { - return "", errors.Wrap(errNotImplemented, "GetRootlessConfigHomeDir") + return "", errors.New("this function is not implemented for windows") } diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go index c7aa5233f..2dcdbc089 100644 --- a/pkg/varlinkapi/containers.go +++ b/pkg/varlinkapi/containers.go @@ -14,7 +14,7 @@ import ( "time" "github.com/containers/libpod/cmd/podman/shared" - "github.com/containers/libpod/cmd/podman/varlink" + iopodman "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/logs" @@ -864,3 +864,16 @@ func (i *LibpodAPI) ExecContainer(call iopodman.VarlinkCall, opts iopodman.ExecO return ecErr.Error } + +//HealthCheckRun executes defined container's healthcheck command and returns the container's health status. +func (i *LibpodAPI) HealthCheckRun(call iopodman.VarlinkCall, nameOrID string) error { + hcStatus, err := i.Runtime.HealthCheck(nameOrID) + if err != nil && hcStatus != libpod.HealthCheckFailure { + return call.ReplyErrorOccurred(err.Error()) + } + status := libpod.HealthCheckUnhealthy + if hcStatus == libpod.HealthCheckSuccess { + status = libpod.HealthCheckHealthy + } + return call.ReplyHealthCheckRun(status) +} diff --git a/pkg/varlinkapi/generate.go b/pkg/varlinkapi/generate.go index 9dc20d582..19010097d 100644 --- a/pkg/varlinkapi/generate.go +++ b/pkg/varlinkapi/generate.go @@ -4,9 +4,9 @@ package varlinkapi import ( "encoding/json" + "github.com/containers/libpod/cmd/podman/shared" iopodman "github.com/containers/libpod/cmd/podman/varlink" - "github.com/containers/libpod/pkg/systemdgen" ) // GenerateKube ... @@ -29,24 +29,3 @@ func (i *LibpodAPI) GenerateKube(call iopodman.VarlinkCall, name string, service Service: string(servB), }) } - -// GenerateSystemd ... -func (i *LibpodAPI) GenerateSystemd(call iopodman.VarlinkCall, nameOrID, restart string, stopTimeout int64, useName bool) error { - ctr, err := i.Runtime.LookupContainer(nameOrID) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - timeout := int(ctr.StopTimeout()) - if stopTimeout >= 0 { - timeout = int(stopTimeout) - } - name := ctr.ID() - if useName { - name = ctr.Name() - } - unit, err := systemdgen.CreateSystemdUnitAsString(name, ctr.ID(), restart, ctr.Config().StaticDir, timeout) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyGenerateSystemd(unit) -} diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go index fe7f11b4d..a1fdf5955 100644 --- a/pkg/varlinkapi/images.go +++ b/pkg/varlinkapi/images.go @@ -353,7 +353,7 @@ func (i *LibpodAPI) PushImage(call iopodman.VarlinkCall, name, tag string, compr output := bytes.NewBuffer([]byte{}) c := make(chan error) go func() { - err := newImage.PushImageToHeuristicDestination(getContext(), destname, manifestType, "", "", output, compress, so, &dockerRegistryOptions, nil) + err := newImage.PushImageToHeuristicDestination(getContext(), destname, manifestType, "", "", "", output, compress, so, &dockerRegistryOptions, nil) c <- err close(c) }() @@ -615,7 +615,7 @@ func (i *LibpodAPI) ExportImage(call iopodman.VarlinkCall, name, destination str return err } - if err := newImage.PushImageToHeuristicDestination(getContext(), destination, "", "", "", nil, compress, image.SigningOptions{}, &image.DockerRegistryOptions{}, additionalTags); err != nil { + if err := newImage.PushImageToHeuristicDestination(getContext(), destination, "", "", "", "", nil, compress, image.SigningOptions{}, &image.DockerRegistryOptions{}, additionalTags); err != nil { return call.ReplyErrorOccurred(err.Error()) } return call.ReplyExportImage(newImage.ID()) diff --git a/pkg/varlinkapi/volumes.go b/pkg/varlinkapi/volumes.go index 19ba38e7c..6dd86d831 100644 --- a/pkg/varlinkapi/volumes.go +++ b/pkg/varlinkapi/volumes.go @@ -68,7 +68,6 @@ func (i *LibpodAPI) GetVolumes(call iopodman.VarlinkCall, args []string, all boo MountPoint: v.MountPoint(), Name: v.Name(), Options: v.Options(), - Scope: v.Scope(), } volumes = append(volumes, newVol) } diff --git a/test/e2e/exec_test.go b/test/e2e/exec_test.go index 3f9639fda..f3190978c 100644 --- a/test/e2e/exec_test.go +++ b/test/e2e/exec_test.go @@ -146,6 +146,25 @@ var _ = Describe("Podman exec", func() { Expect(session2.OutputToString()).To(Equal(testUser)) }) + It("podman exec with user from run", func() { + testUser := "guest" + setup := podmanTest.Podman([]string{"run", "--user", testUser, "-d", ALPINE, "top"}) + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + ctrID := setup.OutputToString() + + session := podmanTest.Podman([]string{"exec", ctrID, "whoami"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(testUser)) + + overrideUser := "root" + session = podmanTest.Podman([]string{"exec", "--user", overrideUser, ctrID, "whoami"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(overrideUser)) + }) + It("podman exec simple working directory test", func() { setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() @@ -171,16 +190,14 @@ var _ = Describe("Podman exec", func() { session := podmanTest.Podman([]string{"exec", "--workdir", "/missing", "test1", "pwd"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session.ExitCode()).To(Not(Equal(0))) session = podmanTest.Podman([]string{"exec", "-w", "/missing", "test1", "pwd"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session.ExitCode()).To(Not(Equal(0))) }) It("podman exec cannot be invoked", func() { - SkipIfNotRunc() - setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() Expect(setup.ExitCode()).To(Equal(0)) @@ -191,8 +208,6 @@ var _ = Describe("Podman exec", func() { }) It("podman exec command not found", func() { - SkipIfNotRunc() - setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() Expect(setup.ExitCode()).To(Equal(0)) diff --git a/test/e2e/generate_systemd_test.go b/test/e2e/generate_systemd_test.go index 5bb040206..314743a92 100644 --- a/test/e2e/generate_systemd_test.go +++ b/test/e2e/generate_systemd_test.go @@ -3,10 +3,11 @@ package integration import ( + "os" + . "github.com/containers/libpod/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "os" ) var _ = Describe("Podman generate systemd", func() { @@ -33,7 +34,7 @@ var _ = Describe("Podman generate systemd", func() { }) - It("podman generate systemd on bogus container", func() { + It("podman generate systemd on bogus container/pod", func() { session := podmanTest.Podman([]string{"generate", "systemd", "foobar"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Not(Equal(0))) @@ -51,6 +52,19 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Not(Equal(0))) }) + It("podman generate systemd good timeout value", func() { + session := podmanTest.Podman([]string{"create", "--name", "foobar", "alpine", "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"generate", "systemd", "--timeout", "1234", "foobar"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + found, _ := session.GrepString(" stop -t 1234 ") + Expect(found).To(BeTrue()) + }) + It("podman generate systemd", func() { n := podmanTest.Podman([]string{"run", "--name", "nginx", "-dt", nginx}) n.WaitWithDefaultTimeout() @@ -61,6 +75,23 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) }) + It("podman generate systemd --files --name", func() { + n := podmanTest.Podman([]string{"run", "--name", "nginx", "-dt", nginx}) + n.WaitWithDefaultTimeout() + Expect(n.ExitCode()).To(Equal(0)) + + session := podmanTest.Podman([]string{"generate", "systemd", "--files", "--name", "nginx"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + for _, file := range session.OutputToStringArray() { + os.Remove(file) + } + + found, _ := session.GrepString("/container-nginx.service") + Expect(found).To(BeTrue()) + }) + It("podman generate systemd with timeout", func() { n := podmanTest.Podman([]string{"run", "--name", "nginx", "-dt", nginx}) n.WaitWithDefaultTimeout() @@ -69,6 +100,81 @@ var _ = Describe("Podman generate systemd", func() { session := podmanTest.Podman([]string{"generate", "systemd", "--timeout", "5", "nginx"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) + + found, _ := session.GrepString("podman stop -t 5") + Expect(found).To(BeTrue()) }) + It("podman generate systemd pod --name", func() { + n := podmanTest.Podman([]string{"pod", "create", "--name", "foo"}) + n.WaitWithDefaultTimeout() + Expect(n.ExitCode()).To(Equal(0)) + + n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-1", "alpine", "top"}) + n.WaitWithDefaultTimeout() + Expect(n.ExitCode()).To(Equal(0)) + + n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-2", "alpine", "top"}) + n.WaitWithDefaultTimeout() + Expect(n.ExitCode()).To(Equal(0)) + + session := podmanTest.Podman([]string{"generate", "systemd", "--timeout", "42", "--name", "foo"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Grepping the output (in addition to unit tests) + found, _ := session.GrepString("# pod-foo.service") + Expect(found).To(BeTrue()) + + found, _ = session.GrepString("Requires=container-foo-1.service container-foo-2.service") + Expect(found).To(BeTrue()) + + found, _ = session.GrepString("# container-foo-1.service") + Expect(found).To(BeTrue()) + + found, _ = session.GrepString(" start foo-1") + Expect(found).To(BeTrue()) + + found, _ = session.GrepString("-infra") // infra container + Expect(found).To(BeTrue()) + + found, _ = session.GrepString("# container-foo-2.service") + Expect(found).To(BeTrue()) + + found, _ = session.GrepString(" stop -t 42 foo-2") + Expect(found).To(BeTrue()) + + found, _ = session.GrepString("BindsTo=pod-foo.service") + Expect(found).To(BeTrue()) + + found, _ = session.GrepString("PIDFile=") + Expect(found).To(BeTrue()) + + found, _ = session.GrepString("/userdata/conmon.pid") + Expect(found).To(BeTrue()) + }) + + It("podman generate systemd pod --name --files", func() { + n := podmanTest.Podman([]string{"pod", "create", "--name", "foo"}) + n.WaitWithDefaultTimeout() + Expect(n.ExitCode()).To(Equal(0)) + + n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-1", "alpine", "top"}) + n.WaitWithDefaultTimeout() + Expect(n.ExitCode()).To(Equal(0)) + + session := podmanTest.Podman([]string{"generate", "systemd", "--name", "--files", "foo"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + for _, file := range session.OutputToStringArray() { + os.Remove(file) + } + + found, _ := session.GrepString("/pod-foo.service") + Expect(found).To(BeTrue()) + + found, _ = session.GrepString("/container-foo-1.service") + Expect(found).To(BeTrue()) + }) }) diff --git a/test/e2e/healthcheck_run_test.go b/test/e2e/healthcheck_run_test.go index dafc8a837..e10aef427 100644 --- a/test/e2e/healthcheck_run_test.go +++ b/test/e2e/healthcheck_run_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/libpod_suite_remoteclient_test.go b/test/e2e/libpod_suite_remoteclient_test.go index a6cedfc58..7f33fec87 100644 --- a/test/e2e/libpod_suite_remoteclient_test.go +++ b/test/e2e/libpod_suite_remoteclient_test.go @@ -28,13 +28,6 @@ func SkipIfRootless() { } } -func SkipIfNotRunc() { - runtime := os.Getenv("OCI_RUNTIME") - if runtime != "" && filepath.Base(runtime) != "runc" { - ginkgo.Skip("Not using runc as runtime") - } -} - // Podman is the exec call to podman on the filesystem func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration { podmanSession := p.PodmanBase(args, false, false) diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go index 22cc14d6b..1df59dbe3 100644 --- a/test/e2e/libpod_suite_test.go +++ b/test/e2e/libpod_suite_test.go @@ -21,13 +21,6 @@ func SkipIfRootless() { } } -func SkipIfNotRunc() { - runtime := os.Getenv("OCI_RUNTIME") - if runtime != "" && filepath.Base(runtime) != "runc" { - ginkgo.Skip("Not using runc as runtime") - } -} - // Podman is the exec call to podman on the filesystem func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration { podmanSession := p.PodmanBase(args, false, false) diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index b0a9f2ead..af3cab379 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -21,6 +21,7 @@ metadata: app: {{ .Name }} name: {{ .Name }} spec: + hostname: {{ .Hostname }} containers: {{ with .Containers }} {{ range . }} @@ -66,6 +67,7 @@ status: {} type Pod struct { Name string + Hostname string Containers []Container } @@ -78,13 +80,13 @@ type Container struct { CapDrop []string } -func generateKubeYaml(ctrs []Container, fileName string) error { +func generateKubeYaml(name string, hostname string, ctrs []Container, fileName string) error { f, err := os.Create(fileName) if err != nil { return err } defer f.Close() - testPod := Pod{"test", ctrs} + testPod := Pod{name, hostname, ctrs} t, err := template.New("pod").Parse(yamlTemplate) if err != nil { @@ -127,7 +129,7 @@ var _ = Describe("Podman generate kube", func() { testContainer := Container{ctrCmd, ALPINE, ctrName, false, nil, nil} tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml") - err := generateKubeYaml([]Container{testContainer}, tempFile) + err := generateKubeYaml("test", "", []Container{testContainer}, tempFile) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", tempFile}) @@ -146,7 +148,7 @@ var _ = Describe("Podman generate kube", func() { testContainer := Container{ctrCmd, ALPINE, ctrName, false, nil, nil} tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml") - err := generateKubeYaml([]Container{testContainer}, tempFile) + err := generateKubeYaml("test", "", []Container{testContainer}, tempFile) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", tempFile}) @@ -164,6 +166,46 @@ var _ = Describe("Podman generate kube", func() { Expect(inspect.OutputToString()).To(ContainSubstring("hello")) }) + It("podman play kube test hostname", func() { + podName := "test" + ctrName := "testCtr" + ctrCmd := []string{"top"} + testContainer := Container{ctrCmd, ALPINE, ctrName, false, nil, nil} + tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml") + + err := generateKubeYaml(podName, "", []Container{testContainer}, tempFile) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", tempFile}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", ctrName, "--format", "{{ .Config.Hostname }}"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.OutputToString()).To(Equal(podName)) + }) + + It("podman play kube test with customized hostname", func() { + hostname := "myhostname" + ctrName := "testCtr" + ctrCmd := []string{"top"} + testContainer := Container{ctrCmd, ALPINE, ctrName, false, nil, nil} + tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml") + + err := generateKubeYaml("test", hostname, []Container{testContainer}, tempFile) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", tempFile}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", ctrName, "--format", "{{ .Config.Hostname }}"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.OutputToString()).To(Equal(hostname)) + }) + It("podman play kube cap add", func() { ctrName := "testCtr" ctrCmd := []string{"cat", "/proc/self/status"} @@ -171,7 +213,7 @@ var _ = Describe("Podman generate kube", func() { testContainer := Container{ctrCmd, ALPINE, ctrName, true, []string{capAdd}, nil} tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml") - err := generateKubeYaml([]Container{testContainer}, tempFile) + err := generateKubeYaml("test", "", []Container{testContainer}, tempFile) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", tempFile}) @@ -191,7 +233,7 @@ var _ = Describe("Podman generate kube", func() { testContainer := Container{ctrCmd, ALPINE, ctrName, true, []string{capDrop}, nil} tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml") - err := generateKubeYaml([]Container{testContainer}, tempFile) + err := generateKubeYaml("test", "", []Container{testContainer}, tempFile) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", tempFile}) diff --git a/test/e2e/pull_test.go b/test/e2e/pull_test.go index d6e7b44d1..68fcaf133 100644 --- a/test/e2e/pull_test.go +++ b/test/e2e/pull_test.go @@ -150,6 +150,34 @@ var _ = Describe("Podman pull", func() { session = podmanTest.PodmanNoCache([]string{"pull", imgPath}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.PodmanNoCache([]string{"images"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.LineInOutputContainsTag(filepath.Join("localhost", dirpath), "latest")).To(BeTrue()) + session = podmanTest.PodmanNoCache([]string{"rmi", "alpine"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + + It("podman pull from local OCI directory", func() { + podmanTest.RestoreArtifact(ALPINE) + dirpath := filepath.Join(podmanTest.TempDir, "alpine") + os.MkdirAll(dirpath, os.ModePerm) + imgPath := fmt.Sprintf("oci:%s", dirpath) + + session := podmanTest.PodmanNoCache([]string{"push", "alpine", imgPath}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.PodmanNoCache([]string{"rmi", "alpine"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.PodmanNoCache([]string{"pull", imgPath}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.PodmanNoCache([]string{"images"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.LineInOutputContainsTag(filepath.Join("localhost", dirpath), "latest")).To(BeTrue()) session = podmanTest.PodmanNoCache([]string{"rmi", "alpine"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) diff --git a/test/e2e/push_test.go b/test/e2e/push_test.go index cf6279f2f..4360eeece 100644 --- a/test/e2e/push_test.go +++ b/test/e2e/push_test.go @@ -76,6 +76,14 @@ var _ = Describe("Podman push", func() { push := podmanTest.PodmanNoCache([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"}) push.WaitWithDefaultTimeout() Expect(push.ExitCode()).To(Equal(0)) + + // Test --digestfile option + push2 := podmanTest.PodmanNoCache([]string{"push", "--tls-verify=false", "--digestfile=/tmp/digestfile.txt", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"}) + push2.WaitWithDefaultTimeout() + fi, err := os.Lstat("/tmp/digestfile.txt") + Expect(err).To(BeNil()) + Expect(fi.Name()).To(Equal("digestfile.txt")) + Expect(push2.ExitCode()).To(Equal(0)) }) It("podman push to local registry with authorization", func() { diff --git a/test/e2e/run_exit_test.go b/test/e2e/run_exit_test.go index b05849ddb..861d6b3b7 100644 --- a/test/e2e/run_exit_test.go +++ b/test/e2e/run_exit_test.go @@ -41,16 +41,12 @@ var _ = Describe("Podman run exit", func() { }) It("podman run exit 126", func() { - SkipIfNotRunc() - result := podmanTest.Podman([]string{"run", ALPINE, "/etc"}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(126)) }) It("podman run exit 127", func() { - SkipIfNotRunc() - result := podmanTest.Podman([]string{"run", ALPINE, "foobar"}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(127)) diff --git a/test/e2e/run_selinux_test.go b/test/e2e/run_selinux_test.go index a2228411e..dfe71531a 100644 --- a/test/e2e/run_selinux_test.go +++ b/test/e2e/run_selinux_test.go @@ -153,4 +153,16 @@ var _ = Describe("Podman run", func() { Expect(match).Should(BeTrue()) }) + It("podman run selinux file type setup test", func() { + session := podmanTest.Podman([]string{"run", "-it", "--security-opt", "label=type:spc_t", "--security-opt", "label=filetype:container_var_lib_t", fedoraMinimal, "ls", "-Z", "/dev"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + match, _ := session.GrepString("container_var_lib_t") + Expect(match).Should(BeTrue()) + + session = podmanTest.Podman([]string{"run", "-it", "--security-opt", "label=type:spc_t", "--security-opt", "label=filetype:foobar", fedoraMinimal, "ls", "-Z", "/dev"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(127)) + }) + }) diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index ce2044a72..6e102cfa5 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -417,7 +417,6 @@ var _ = Describe("Podman run", func() { It("podman run notify_socket", func() { SkipIfRemote() - SkipIfNotRunc() host := GetHostDistributionInfo() if host.Distribution != "rhel" && host.Distribution != "centos" && host.Distribution != "fedora" { @@ -629,7 +628,6 @@ var _ = Describe("Podman run", func() { }) It("podman run exit code on failure to exec", func() { - SkipIfNotRunc() session := podmanTest.Podman([]string{"run", ALPINE, "/etc"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(126)) diff --git a/test/e2e/start_test.go b/test/e2e/start_test.go index 2dbb9545b..fc1203ed1 100644 --- a/test/e2e/start_test.go +++ b/test/e2e/start_test.go @@ -101,8 +101,6 @@ var _ = Describe("Podman start", func() { }) It("podman failed to start with --rm should delete the container", func() { - SkipIfNotRunc() - session := podmanTest.Podman([]string{"create", "-it", "--rm", ALPINE, "foo"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -116,8 +114,6 @@ var _ = Describe("Podman start", func() { }) It("podman failed to start without --rm should NOT delete the container", func() { - SkipIfNotRunc() - session := podmanTest.Podman([]string{"create", "-it", ALPINE, "foo"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) diff --git a/test/system/070-build.bats b/test/system/070-build.bats index 5ef84e9b8..a9d2ed1b7 100644 --- a/test/system/070-build.bats +++ b/test/system/070-build.bats @@ -22,12 +22,24 @@ RUN apk add nginx RUN echo $rand_content > /$rand_filename EOF - run_podman build -t build_test --format=docker $tmpdir + # The 'apk' command can take a long time to fetch files; bump timeout + PODMAN_TIMEOUT=240 run_podman build -t build_test --format=docker $tmpdir is "$output" ".*STEP 4: COMMIT" "COMMIT seen in log" run_podman run --rm build_test cat /$rand_filename is "$output" "$rand_content" "reading generated file in image" - run_podman rmi build_test + run_podman rmi -f build_test } + +function teardown() { + # A timeout or other error in 'build' can leave behind stale images + # that podman can't even see and which will cascade into subsequent + # test failures. Try a last-ditch force-rm in cleanup, ignoring errors. + run_podman '?' rm -a -f + run_podman '?' rmi -f build_test + + basic_teardown +} + # vim: filetype=sh diff --git a/vendor/github.com/coreos/go-iptables/LICENSE b/vendor/github.com/coreos/go-iptables/LICENSE deleted file mode 100644 index 37ec93a14..000000000 --- a/vendor/github.com/coreos/go-iptables/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/coreos/go-iptables/NOTICE b/vendor/github.com/coreos/go-iptables/NOTICE deleted file mode 100644 index 23a0ada2f..000000000 --- a/vendor/github.com/coreos/go-iptables/NOTICE +++ /dev/null @@ -1,5 +0,0 @@ -CoreOS Project -Copyright 2018 CoreOS, Inc - -This product includes software developed at CoreOS, Inc. -(http://www.coreos.com/). diff --git a/vendor/github.com/coreos/go-iptables/iptables/iptables.go b/vendor/github.com/coreos/go-iptables/iptables/iptables.go deleted file mode 100644 index 9601bc78a..000000000 --- a/vendor/github.com/coreos/go-iptables/iptables/iptables.go +++ /dev/null @@ -1,532 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package iptables - -import ( - "bytes" - "fmt" - "io" - "net" - "os/exec" - "regexp" - "strconv" - "strings" - "syscall" -) - -// Adds the output of stderr to exec.ExitError -type Error struct { - exec.ExitError - cmd exec.Cmd - msg string - proto Protocol - exitStatus *int //for overriding -} - -func (e *Error) ExitStatus() int { - if e.exitStatus != nil { - return *e.exitStatus - } - return e.Sys().(syscall.WaitStatus).ExitStatus() -} - -func (e *Error) Error() string { - return fmt.Sprintf("running %v: exit status %v: %v", e.cmd.Args, e.ExitStatus(), e.msg) -} - -// IsNotExist returns true if the error is due to the chain or rule not existing -func (e *Error) IsNotExist() bool { - return e.ExitStatus() == 1 && - (e.msg == fmt.Sprintf("%s: Bad rule (does a matching rule exist in that chain?).\n", getIptablesCommand(e.proto)) || - e.msg == fmt.Sprintf("%s: No chain/target/match by that name.\n", getIptablesCommand(e.proto))) -} - -// Protocol to differentiate between IPv4 and IPv6 -type Protocol byte - -const ( - ProtocolIPv4 Protocol = iota - ProtocolIPv6 -) - -type IPTables struct { - path string - proto Protocol - hasCheck bool - hasWait bool - hasRandomFully bool - v1 int - v2 int - v3 int - mode string // the underlying iptables operating mode, e.g. nf_tables -} - -// New creates a new IPTables. -// For backwards compatibility, this always uses IPv4, i.e. "iptables". -func New() (*IPTables, error) { - return NewWithProtocol(ProtocolIPv4) -} - -// New creates a new IPTables for the given proto. -// The proto will determine which command is used, either "iptables" or "ip6tables". -func NewWithProtocol(proto Protocol) (*IPTables, error) { - path, err := exec.LookPath(getIptablesCommand(proto)) - if err != nil { - return nil, err - } - vstring, err := getIptablesVersionString(path) - v1, v2, v3, mode, err := extractIptablesVersion(vstring) - - checkPresent, waitPresent, randomFullyPresent := getIptablesCommandSupport(v1, v2, v3) - - ipt := IPTables{ - path: path, - proto: proto, - hasCheck: checkPresent, - hasWait: waitPresent, - hasRandomFully: randomFullyPresent, - v1: v1, - v2: v2, - v3: v3, - mode: mode, - } - return &ipt, nil -} - -// Proto returns the protocol used by this IPTables. -func (ipt *IPTables) Proto() Protocol { - return ipt.proto -} - -// Exists checks if given rulespec in specified table/chain exists -func (ipt *IPTables) Exists(table, chain string, rulespec ...string) (bool, error) { - if !ipt.hasCheck { - return ipt.existsForOldIptables(table, chain, rulespec) - - } - cmd := append([]string{"-t", table, "-C", chain}, rulespec...) - err := ipt.run(cmd...) - eerr, eok := err.(*Error) - switch { - case err == nil: - return true, nil - case eok && eerr.ExitStatus() == 1: - return false, nil - default: - return false, err - } -} - -// Insert inserts rulespec to specified table/chain (in specified pos) -func (ipt *IPTables) Insert(table, chain string, pos int, rulespec ...string) error { - cmd := append([]string{"-t", table, "-I", chain, strconv.Itoa(pos)}, rulespec...) - return ipt.run(cmd...) -} - -// Append appends rulespec to specified table/chain -func (ipt *IPTables) Append(table, chain string, rulespec ...string) error { - cmd := append([]string{"-t", table, "-A", chain}, rulespec...) - return ipt.run(cmd...) -} - -// AppendUnique acts like Append except that it won't add a duplicate -func (ipt *IPTables) AppendUnique(table, chain string, rulespec ...string) error { - exists, err := ipt.Exists(table, chain, rulespec...) - if err != nil { - return err - } - - if !exists { - return ipt.Append(table, chain, rulespec...) - } - - return nil -} - -// Delete removes rulespec in specified table/chain -func (ipt *IPTables) Delete(table, chain string, rulespec ...string) error { - cmd := append([]string{"-t", table, "-D", chain}, rulespec...) - return ipt.run(cmd...) -} - -// List rules in specified table/chain -func (ipt *IPTables) List(table, chain string) ([]string, error) { - args := []string{"-t", table, "-S", chain} - return ipt.executeList(args) -} - -// List rules (with counters) in specified table/chain -func (ipt *IPTables) ListWithCounters(table, chain string) ([]string, error) { - args := []string{"-t", table, "-v", "-S", chain} - return ipt.executeList(args) -} - -// ListChains returns a slice containing the name of each chain in the specified table. -func (ipt *IPTables) ListChains(table string) ([]string, error) { - args := []string{"-t", table, "-S"} - - result, err := ipt.executeList(args) - if err != nil { - return nil, err - } - - // Iterate over rules to find all default (-P) and user-specified (-N) chains. - // Chains definition always come before rules. - // Format is the following: - // -P OUTPUT ACCEPT - // -N Custom - var chains []string - for _, val := range result { - if strings.HasPrefix(val, "-P") || strings.HasPrefix(val, "-N") { - chains = append(chains, strings.Fields(val)[1]) - } else { - break - } - } - return chains, nil -} - -// Stats lists rules including the byte and packet counts -func (ipt *IPTables) Stats(table, chain string) ([][]string, error) { - args := []string{"-t", table, "-L", chain, "-n", "-v", "-x"} - lines, err := ipt.executeList(args) - if err != nil { - return nil, err - } - - appendSubnet := func(addr string) string { - if strings.IndexByte(addr, byte('/')) < 0 { - if strings.IndexByte(addr, '.') < 0 { - return addr + "/128" - } - return addr + "/32" - } - return addr - } - - ipv6 := ipt.proto == ProtocolIPv6 - - rows := [][]string{} - for i, line := range lines { - // Skip over chain name and field header - if i < 2 { - continue - } - - // Fields: - // 0=pkts 1=bytes 2=target 3=prot 4=opt 5=in 6=out 7=source 8=destination 9=options - line = strings.TrimSpace(line) - fields := strings.Fields(line) - - // The ip6tables verbose output cannot be naively split due to the default "opt" - // field containing 2 single spaces. - if ipv6 { - // Check if field 6 is "opt" or "source" address - dest := fields[6] - ip, _, _ := net.ParseCIDR(dest) - if ip == nil { - ip = net.ParseIP(dest) - } - - // If we detected a CIDR or IP, the "opt" field is empty.. insert it. - if ip != nil { - f := []string{} - f = append(f, fields[:4]...) - f = append(f, " ") // Empty "opt" field for ip6tables - f = append(f, fields[4:]...) - fields = f - } - } - - // Adjust "source" and "destination" to include netmask, to match regular - // List output - fields[7] = appendSubnet(fields[7]) - fields[8] = appendSubnet(fields[8]) - - // Combine "options" fields 9... into a single space-delimited field. - options := fields[9:] - fields = fields[:9] - fields = append(fields, strings.Join(options, " ")) - rows = append(rows, fields) - } - return rows, nil -} - -func (ipt *IPTables) executeList(args []string) ([]string, error) { - var stdout bytes.Buffer - if err := ipt.runWithOutput(args, &stdout); err != nil { - return nil, err - } - - rules := strings.Split(stdout.String(), "\n") - - // strip trailing newline - if len(rules) > 0 && rules[len(rules)-1] == "" { - rules = rules[:len(rules)-1] - } - - // nftables mode doesn't return an error code when listing a non-existent - // chain. Patch that up. - if len(rules) == 0 && ipt.mode == "nf_tables" { - v := 1 - return nil, &Error{ - cmd: exec.Cmd{Args: args}, - msg: fmt.Sprintf("%s: No chain/target/match by that name.\n", getIptablesCommand(ipt.proto)), - proto: ipt.proto, - exitStatus: &v, - } - } - - for i, rule := range rules { - rules[i] = filterRuleOutput(rule) - } - - return rules, nil -} - -// NewChain creates a new chain in the specified table. -// If the chain already exists, it will result in an error. -func (ipt *IPTables) NewChain(table, chain string) error { - return ipt.run("-t", table, "-N", chain) -} - -// ClearChain flushed (deletes all rules) in the specified table/chain. -// If the chain does not exist, a new one will be created -func (ipt *IPTables) ClearChain(table, chain string) error { - err := ipt.NewChain(table, chain) - - // the exit code for "this table already exists" is different for - // different iptables modes - existsErr := 1 - if ipt.mode == "nf_tables" { - existsErr = 4 - } - - eerr, eok := err.(*Error) - switch { - case err == nil: - return nil - case eok && eerr.ExitStatus() == existsErr: - // chain already exists. Flush (clear) it. - return ipt.run("-t", table, "-F", chain) - default: - return err - } -} - -// RenameChain renames the old chain to the new one. -func (ipt *IPTables) RenameChain(table, oldChain, newChain string) error { - return ipt.run("-t", table, "-E", oldChain, newChain) -} - -// DeleteChain deletes the chain in the specified table. -// The chain must be empty -func (ipt *IPTables) DeleteChain(table, chain string) error { - return ipt.run("-t", table, "-X", chain) -} - -// ChangePolicy changes policy on chain to target -func (ipt *IPTables) ChangePolicy(table, chain, target string) error { - return ipt.run("-t", table, "-P", chain, target) -} - -// Check if the underlying iptables command supports the --random-fully flag -func (ipt *IPTables) HasRandomFully() bool { - return ipt.hasRandomFully -} - -// Return version components of the underlying iptables command -func (ipt *IPTables) GetIptablesVersion() (int, int, int) { - return ipt.v1, ipt.v2, ipt.v3 -} - -// run runs an iptables command with the given arguments, ignoring -// any stdout output -func (ipt *IPTables) run(args ...string) error { - return ipt.runWithOutput(args, nil) -} - -// runWithOutput runs an iptables command with the given arguments, -// writing any stdout output to the given writer -func (ipt *IPTables) runWithOutput(args []string, stdout io.Writer) error { - args = append([]string{ipt.path}, args...) - if ipt.hasWait { - args = append(args, "--wait") - } else { - fmu, err := newXtablesFileLock() - if err != nil { - return err - } - ul, err := fmu.tryLock() - if err != nil { - return err - } - defer ul.Unlock() - } - - var stderr bytes.Buffer - cmd := exec.Cmd{ - Path: ipt.path, - Args: args, - Stdout: stdout, - Stderr: &stderr, - } - - if err := cmd.Run(); err != nil { - switch e := err.(type) { - case *exec.ExitError: - return &Error{*e, cmd, stderr.String(), ipt.proto, nil} - default: - return err - } - } - - return nil -} - -// getIptablesCommand returns the correct command for the given protocol, either "iptables" or "ip6tables". -func getIptablesCommand(proto Protocol) string { - if proto == ProtocolIPv6 { - return "ip6tables" - } else { - return "iptables" - } -} - -// Checks if iptables has the "-C" and "--wait" flag -func getIptablesCommandSupport(v1 int, v2 int, v3 int) (bool, bool, bool) { - return iptablesHasCheckCommand(v1, v2, v3), iptablesHasWaitCommand(v1, v2, v3), iptablesHasRandomFully(v1, v2, v3) -} - -// getIptablesVersion returns the first three components of the iptables version -// and the operating mode (e.g. nf_tables or legacy) -// e.g. "iptables v1.3.66" would return (1, 3, 66, legacy, nil) -func extractIptablesVersion(str string) (int, int, int, string, error) { - versionMatcher := regexp.MustCompile(`v([0-9]+)\.([0-9]+)\.([0-9]+)(?:\s+\((\w+))?`) - result := versionMatcher.FindStringSubmatch(str) - if result == nil { - return 0, 0, 0, "", fmt.Errorf("no iptables version found in string: %s", str) - } - - v1, err := strconv.Atoi(result[1]) - if err != nil { - return 0, 0, 0, "", err - } - - v2, err := strconv.Atoi(result[2]) - if err != nil { - return 0, 0, 0, "", err - } - - v3, err := strconv.Atoi(result[3]) - if err != nil { - return 0, 0, 0, "", err - } - - mode := "legacy" - if result[4] != "" { - mode = result[4] - } - return v1, v2, v3, mode, nil -} - -// Runs "iptables --version" to get the version string -func getIptablesVersionString(path string) (string, error) { - cmd := exec.Command(path, "--version") - var out bytes.Buffer - cmd.Stdout = &out - err := cmd.Run() - if err != nil { - return "", err - } - return out.String(), nil -} - -// Checks if an iptables version is after 1.4.11, when --check was added -func iptablesHasCheckCommand(v1 int, v2 int, v3 int) bool { - if v1 > 1 { - return true - } - if v1 == 1 && v2 > 4 { - return true - } - if v1 == 1 && v2 == 4 && v3 >= 11 { - return true - } - return false -} - -// Checks if an iptables version is after 1.4.20, when --wait was added -func iptablesHasWaitCommand(v1 int, v2 int, v3 int) bool { - if v1 > 1 { - return true - } - if v1 == 1 && v2 > 4 { - return true - } - if v1 == 1 && v2 == 4 && v3 >= 20 { - return true - } - return false -} - -// Checks if an iptables version is after 1.6.2, when --random-fully was added -func iptablesHasRandomFully(v1 int, v2 int, v3 int) bool { - if v1 > 1 { - return true - } - if v1 == 1 && v2 > 6 { - return true - } - if v1 == 1 && v2 == 6 && v3 >= 2 { - return true - } - return false -} - -// Checks if a rule specification exists for a table -func (ipt *IPTables) existsForOldIptables(table, chain string, rulespec []string) (bool, error) { - rs := strings.Join(append([]string{"-A", chain}, rulespec...), " ") - args := []string{"-t", table, "-S"} - var stdout bytes.Buffer - err := ipt.runWithOutput(args, &stdout) - if err != nil { - return false, err - } - return strings.Contains(stdout.String(), rs), nil -} - -// counterRegex is the regex used to detect nftables counter format -var counterRegex = regexp.MustCompile(`^\[([0-9]+):([0-9]+)\] `) - -// filterRuleOutput works around some inconsistencies in output. -// For example, when iptables is in legacy vs. nftables mode, it produces -// different results. -func filterRuleOutput(rule string) string { - out := rule - - // work around an output difference in nftables mode where counters - // are output in iptables-save format, rather than iptables -S format - // The string begins with "[0:0]" - // - // Fixes #49 - if groups := counterRegex.FindStringSubmatch(out); groups != nil { - // drop the brackets - out = out[len(groups[0]):] - out = fmt.Sprintf("%s -c %s %s", out, groups[1], groups[2]) - } - - return out -} diff --git a/vendor/github.com/coreos/go-iptables/iptables/lock.go b/vendor/github.com/coreos/go-iptables/iptables/lock.go deleted file mode 100644 index a88e92b4e..000000000 --- a/vendor/github.com/coreos/go-iptables/iptables/lock.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package iptables - -import ( - "os" - "sync" - "syscall" -) - -const ( - // In earlier versions of iptables, the xtables lock was implemented - // via a Unix socket, but now flock is used via this lockfile: - // http://git.netfilter.org/iptables/commit/?id=aa562a660d1555b13cffbac1e744033e91f82707 - // Note the LSB-conforming "/run" directory does not exist on old - // distributions, so assume "/var" is symlinked - xtablesLockFilePath = "/var/run/xtables.lock" - - defaultFilePerm = 0600 -) - -type Unlocker interface { - Unlock() error -} - -type nopUnlocker struct{} - -func (_ nopUnlocker) Unlock() error { return nil } - -type fileLock struct { - // mu is used to protect against concurrent invocations from within this process - mu sync.Mutex - fd int -} - -// tryLock takes an exclusive lock on the xtables lock file without blocking. -// This is best-effort only: if the exclusive lock would block (i.e. because -// another process already holds it), no error is returned. Otherwise, any -// error encountered during the locking operation is returned. -// The returned Unlocker should be used to release the lock when the caller is -// done invoking iptables commands. -func (l *fileLock) tryLock() (Unlocker, error) { - l.mu.Lock() - err := syscall.Flock(l.fd, syscall.LOCK_EX|syscall.LOCK_NB) - switch err { - case syscall.EWOULDBLOCK: - l.mu.Unlock() - return nopUnlocker{}, nil - case nil: - return l, nil - default: - l.mu.Unlock() - return nil, err - } -} - -// Unlock closes the underlying file, which implicitly unlocks it as well. It -// also unlocks the associated mutex. -func (l *fileLock) Unlock() error { - defer l.mu.Unlock() - return syscall.Close(l.fd) -} - -// newXtablesFileLock opens a new lock on the xtables lockfile without -// acquiring the lock -func newXtablesFileLock() (*fileLock, error) { - fd, err := syscall.Open(xtablesLockFilePath, os.O_CREATE, defaultFilePerm) - if err != nil { - return nil, err - } - return &fileLock{fd: fd}, nil -} diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go index 1eb9a6bf2..2730fcf4a 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go @@ -13,11 +13,12 @@ import ( // Valid Label Options var validOptions = map[string]bool{ - "disable": true, - "type": true, - "user": true, - "role": true, - "level": true, + "disable": true, + "type": true, + "filetype": true, + "user": true, + "role": true, + "level": true, } var ErrIncompatibleLabel = fmt.Errorf("Bad SELinux option z and Z can not be used together") @@ -51,13 +52,16 @@ func InitLabels(options []string) (plabel string, mlabel string, Err error) { return "", mountLabel, nil } if i := strings.Index(opt, ":"); i == -1 { - return "", "", fmt.Errorf("Bad label option %q, valid options 'disable' or \n'user, role, level, type' followed by ':' and a value", opt) + return "", "", fmt.Errorf("Bad label option %q, valid options 'disable' or \n'user, role, level, type, filetype' followed by ':' and a value", opt) } con := strings.SplitN(opt, ":", 2) if !validOptions[con[0]] { - return "", "", fmt.Errorf("Bad label option %q, valid options 'disable, user, role, level, type'", con[0]) + return "", "", fmt.Errorf("Bad label option %q, valid options 'disable, user, role, level, type, filetype'", con[0]) } + if con[0] == "filetype" { + mcon["type"] = con[1] + } pcon[con[0]] = con[1] if con[0] == "level" || con[0] == "user" { mcon[con[0]] = con[1] diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go index d7786c33c..2d4e9f890 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go @@ -18,6 +18,7 @@ import ( "strings" "sync" "syscall" + "golang.org/x/sys/unix" ) const ( @@ -392,6 +393,14 @@ func SetExecLabel(label string) error { return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()), label) } +/* +SetTaskLabel sets the SELinux label for the current thread, or an error. +This requires the dyntransition permission. +*/ +func SetTaskLabel(label string) error { + return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/current", syscall.Gettid()), label) +} + // SetSocketLabel takes a process label and tells the kernel to assign the // label to the next socket that gets created func SetSocketLabel(label string) error { @@ -403,6 +412,11 @@ func SocketLabel() (string, error) { return readCon(fmt.Sprintf("/proc/self/task/%d/attr/sockcreate", syscall.Gettid())) } +// PeerLabel retrieves the label of the client on the other side of a socket +func PeerLabel(fd uintptr) (string, error) { + return unix.GetsockoptString(int(fd), syscall.SOL_SOCKET, syscall.SO_PEERSEC) +} + // SetKeyLabel takes a process label and tells the kernel to assign the // label to the next kernel keyring that gets created func SetKeyLabel(label string) error { diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go index 79b005d19..0c2e1cd38 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go @@ -97,6 +97,14 @@ func SetExecLabel(label string) error { } /* +SetTaskLabel sets the SELinux label for the current thread, or an error. +This requires the dyntransition permission. +*/ +func SetTaskLabel(label string) error { + return nil +} + +/* SetSocketLabel sets the SELinux label that the kernel will use for any programs that are executed by the current process thread, or an error. */ @@ -109,6 +117,11 @@ func SocketLabel() (string, error) { return "", nil } +// PeerLabel retrieves the label of the client on the other side of a socket +func PeerLabel(fd uintptr) (string, error) { + return "", nil +} + // SetKeyLabel takes a process label and tells the kernel to assign the // label to the next kernel keyring that gets created func SetKeyLabel(label string) error { diff --git a/vendor/modules.txt b/vendor/modules.txt index efb7d99da..3acff38c9 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -75,10 +75,10 @@ github.com/containers/image/storage github.com/containers/image/copy github.com/containers/image/docker/reference github.com/containers/image/docker/tarfile +github.com/containers/image/oci/layout github.com/containers/image/tarball github.com/containers/image/pkg/sysregistriesv2 github.com/containers/image/image -github.com/containers/image/oci/layout github.com/containers/image/directory/explicitfilepath github.com/containers/image/docker/policyconfiguration github.com/containers/image/pkg/blobinfocache/none @@ -144,8 +144,6 @@ github.com/containers/storage/drivers/quota github.com/containers/storage/pkg/fsutils github.com/containers/storage/pkg/ostree github.com/containers/storage/drivers/copy -# github.com/coreos/go-iptables v0.4.1 -github.com/coreos/go-iptables/iptables # github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a github.com/coreos/go-systemd/activation github.com/coreos/go-systemd/dbus @@ -365,7 +363,7 @@ github.com/opencontainers/runtime-tools/generate/seccomp github.com/opencontainers/runtime-tools/filepath github.com/opencontainers/runtime-tools/specerror github.com/opencontainers/runtime-tools/error -# github.com/opencontainers/selinux v1.2.2 +# github.com/opencontainers/selinux v1.3.0 github.com/opencontainers/selinux/go-selinux/label github.com/opencontainers/selinux/go-selinux # github.com/openshift/imagebuilder v1.1.0 |