diff options
43 files changed, 1167 insertions, 89 deletions
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index 99f389799..6d98aaf0e 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -321,6 +321,7 @@ type KubePlayValues struct { Authfile string CertDir string Creds string + Network string Quiet bool SignaturePolicy string TlsVerify bool diff --git a/cmd/podman/main_local.go b/cmd/podman/main_local.go index 23b3f5ae7..a65e6acf8 100644 --- a/cmd/podman/main_local.go +++ b/cmd/podman/main_local.go @@ -11,7 +11,6 @@ import ( "os" "runtime/pprof" "strconv" - "strings" "syscall" "github.com/containers/common/pkg/config" @@ -192,7 +191,7 @@ func setupRootless(cmd *cobra.Command, args []string) error { } } - if os.Geteuid() == 0 || cmd == _searchCommand || cmd == _versionCommand || cmd == _mountCommand || cmd == _migrateCommand || strings.HasPrefix(cmd.Use, "help") { + if !executeCommandInUserNS(cmd) { return nil } @@ -243,6 +242,25 @@ func setupRootless(cmd *cobra.Command, args []string) error { return nil } +// Most podman commands when run in rootless mode, need to be executed in the +// users usernamespace. This function is updated with a list of commands that +// should NOT be run within the user namespace. +func executeCommandInUserNS(cmd *cobra.Command) bool { + if os.Geteuid() == 0 { + return false + } + switch cmd { + case _migrateCommand, + _mountCommand, + _renumberCommand, + _infoCommand, + _searchCommand, + _versionCommand: + return false + } + return true +} + func setRLimits() error { rlimits := new(syscall.Rlimit) rlimits.Cur = 1048576 diff --git a/cmd/podman/play_kube.go b/cmd/podman/play_kube.go index 2028d2ef4..a5669c595 100644 --- a/cmd/podman/play_kube.go +++ b/cmd/podman/play_kube.go @@ -51,6 +51,7 @@ func init() { flags.StringVar(&playKubeCommand.SeccompProfileRoot, "seccomp-profile-root", defaultSeccompRoot, "Directory path for seccomp profiles") markFlagHidden(flags, "signature-policy") } + flags.StringVar(&playKubeCommand.Network, "network", "", "Connect pod to CNI network(s)") } func playKubeCmd(c *cliconfig.KubePlayValues) error { diff --git a/cmd/podmanV2/common/create.go b/cmd/podmanV2/common/create.go index 724ed2f42..f81d021c8 100644 --- a/cmd/podmanV2/common/create.go +++ b/cmd/podmanV2/common/create.go @@ -138,7 +138,7 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { ) createFlags.StringVar( &cf.DetachKeys, - "detach-keys", getDefaultDetachKeys(), + "detach-keys", GetDefaultDetachKeys(), "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-cf`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`", ) createFlags.StringSliceVar( diff --git a/cmd/podmanV2/common/default.go b/cmd/podmanV2/common/default.go index fea161edf..b71fcb6f0 100644 --- a/cmd/podmanV2/common/default.go +++ b/cmd/podmanV2/common/default.go @@ -116,6 +116,6 @@ func getDefaultPidsDescription() string { return "Tune container pids limit (set 0 for unlimited)" } -func getDefaultDetachKeys() string { +func GetDefaultDetachKeys() string { return defaultContainerConfig.Engine.DetachKeys } diff --git a/cmd/podmanV2/containers/attach.go b/cmd/podmanV2/containers/attach.go new file mode 100644 index 000000000..d62dcff86 --- /dev/null +++ b/cmd/podmanV2/containers/attach.go @@ -0,0 +1,60 @@ +package containers + +import ( + "os" + + "github.com/containers/libpod/cmd/podmanV2/common" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + attachDescription = "The podman attach command allows you to attach to a running container using the container's ID or name, either to view its ongoing output or to control it interactively." + attachCommand = &cobra.Command{ + Use: "attach [flags] CONTAINER", + Short: "Attach to a running container", + Long: attachDescription, + RunE: attach, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) > 1 || (len(args) == 0 && !cmd.Flag("latest").Changed) { + return errors.Errorf("attach requires the name or id of one running container or the latest flag") + } + return nil + }, + PreRunE: preRunE, + Example: `podman attach ctrID + podman attach 1234 + podman attach --no-stdin foobar`, + } +) + +var ( + attachOpts entities.AttachOptions +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode}, + Command: attachCommand, + }) + flags := attachCommand.Flags() + flags.StringVar(&attachOpts.DetachKeys, "detach-keys", common.GetDefaultDetachKeys(), "Select the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") + flags.BoolVar(&attachOpts.NoStdin, "no-stdin", false, "Do not attach STDIN. The default is false") + flags.BoolVar(&attachOpts.SigProxy, "sig-proxy", true, "Proxy received signals to the process") + flags.BoolVarP(&attachOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") + } +} + +func attach(cmd *cobra.Command, args []string) error { + attachOpts.Stdin = os.Stdin + if attachOpts.NoStdin { + attachOpts.Stdin = nil + } + attachOpts.Stdout = os.Stdout + attachOpts.Stderr = os.Stderr + return registry.ContainerEngine().ContainerAttach(registry.GetContext(), args[0], attachOpts) +} diff --git a/cmd/podmanV2/containers/exec.go b/cmd/podmanV2/containers/exec.go new file mode 100644 index 000000000..4bff57dbb --- /dev/null +++ b/cmd/podmanV2/containers/exec.go @@ -0,0 +1,93 @@ +package containers + +import ( + "bufio" + "os" + + "github.com/containers/libpod/cmd/podmanV2/common" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + envLib "github.com/containers/libpod/pkg/env" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + execDescription = `Execute the specified command inside a running container. +` + execCommand = &cobra.Command{ + Use: "exec [flags] CONTAINER [COMMAND [ARG...]]", + Short: "Run a process in a running container", + Long: execDescription, + PreRunE: preRunE, + RunE: exec, + Example: `podman exec -it ctrID ls + podman exec -it -w /tmp myCtr pwd + podman exec --user root ctrID ls`, + } +) + +var ( + envInput, envFile []string + execOpts entities.ExecOptions +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode}, + Command: execCommand, + }) + flags := execCommand.Flags() + flags.SetInterspersed(false) + flags.StringVar(&execOpts.DetachKeys, "detach-keys", common.GetDefaultDetachKeys(), "Select the key sequence for detaching a container. Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _") + flags.StringArrayVarP(&envInput, "env", "e", []string{}, "Set environment variables") + flags.StringSliceVar(&envFile, "env-file", []string{}, "Read in a file of environment variables") + flags.BoolVarP(&execOpts.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached") + flags.BoolVarP(&execOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + flags.BoolVar(&execOpts.Privileged, "privileged", false, "Give the process extended Linux capabilities inside the container. The default is false") + flags.BoolVarP(&execOpts.Tty, "tty", "t", false, "Allocate a pseudo-TTY. The default is false") + flags.StringVarP(&execOpts.User, "user", "u", "", "Sets the username or UID used and optionally the groupname or GID for the specified command") + flags.UintVar(&execOpts.PreserveFDs, "preserve-fds", 0, "Pass N additional file descriptors to the container") + flags.StringVarP(&execOpts.WorkDir, "workdir", "w", "", "Working directory inside the container") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") + _ = flags.MarkHidden("preserve-fds") + } + +} +func exec(cmd *cobra.Command, args []string) error { + var nameOrId string + execOpts.Cmd = args + if !execOpts.Latest { + execOpts.Cmd = args[1:] + nameOrId = args[0] + } + // Validate given environment variables + execOpts.Envs = make(map[string]string) + for _, f := range envFile { + fileEnv, err := envLib.ParseFile(f) + if err != nil { + return err + } + execOpts.Envs = envLib.Join(execOpts.Envs, fileEnv) + } + + cliEnv, err := envLib.ParseSlice(envInput) + if err != nil { + return errors.Wrap(err, "error parsing environment variables") + } + + execOpts.Envs = envLib.Join(execOpts.Envs, cliEnv) + execOpts.Streams.OutputStream = os.Stdout + execOpts.Streams.ErrorStream = os.Stderr + if execOpts.Interactive { + execOpts.Streams.InputStream = bufio.NewReader(os.Stdin) + execOpts.Streams.AttachInput = true + } + execOpts.Streams.AttachOutput = true + execOpts.Streams.AttachError = true + + exitCode, err := registry.ContainerEngine().ContainerExec(registry.GetContext(), nameOrId, execOpts) + registry.SetExitCode(exitCode) + return err +} diff --git a/cmd/podmanV2/containers/start.go b/cmd/podmanV2/containers/start.go new file mode 100644 index 000000000..0ae2f6d50 --- /dev/null +++ b/cmd/podmanV2/containers/start.go @@ -0,0 +1,87 @@ +package containers + +import ( + "fmt" + "os" + + "github.com/containers/libpod/cmd/podmanV2/common" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/cmd/podmanV2/utils" + "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + startDescription = `Starts one or more containers. The container name or ID can be used.` + startCommand = &cobra.Command{ + Use: "start [flags] CONTAINER [CONTAINER...]", + Short: "Start one or more containers", + Long: startDescription, + RunE: start, + PreRunE: preRunE, + Args: cobra.MinimumNArgs(1), + Example: `podman start --latest + podman start 860a4b231279 5421ab43b45 + podman start --interactive --attach imageID`, + } +) + +var ( + startOptions entities.ContainerStartOptions +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode}, + Command: startCommand, + }) + flags := startCommand.Flags() + flags.BoolVarP(&startOptions.Attach, "attach", "a", false, "Attach container's STDOUT and STDERR") + flags.StringVar(&startOptions.DetachKeys, "detach-keys", common.GetDefaultDetachKeys(), "Select the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") + flags.BoolVarP(&startOptions.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached") + flags.BoolVarP(&startOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + flags.BoolVar(&startOptions.SigProxy, "sig-proxy", false, "Proxy received signals to the process (default true if attaching, false otherwise)") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") + _ = flags.MarkHidden("sig-proxy") + } + +} + +func start(cmd *cobra.Command, args []string) error { + var errs utils.OutputErrors + if len(args) > 1 && startOptions.Attach { + return errors.Errorf("you cannot start and attach multiple containers at once") + } + + sigProxy := startOptions.SigProxy || startOptions.Attach + if cmd.Flag("sig-proxy").Changed { + sigProxy = startOptions.SigProxy + } + + if sigProxy && !startOptions.Attach { + return errors.Wrapf(define.ErrInvalidArg, "you cannot use sig-proxy without --attach") + } + if startOptions.Attach { + startOptions.Stdin = os.Stdin + startOptions.Stderr = os.Stderr + startOptions.Stdout = os.Stdout + } + + responses, err := registry.ContainerEngine().ContainerStart(registry.GetContext(), args, startOptions) + if err != nil { + return err + } + + for _, r := range responses { + if r.Err == nil { + fmt.Println(r.Id) + } else { + errs = append(errs, r.Err) + } + } + // TODO need to understand an implement exitcodes + return errs.PrintErrors() +} diff --git a/cmd/podmanV2/main.go b/cmd/podmanV2/main.go index 6781a7f06..fe3cd9f16 100644 --- a/cmd/podmanV2/main.go +++ b/cmd/podmanV2/main.go @@ -12,6 +12,7 @@ import ( _ "github.com/containers/libpod/cmd/podmanV2/networks" _ "github.com/containers/libpod/cmd/podmanV2/pods" "github.com/containers/libpod/cmd/podmanV2/registry" + _ "github.com/containers/libpod/cmd/podmanV2/system" _ "github.com/containers/libpod/cmd/podmanV2/volumes" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/domain/entities" diff --git a/cmd/podmanV2/pods/inspect.go b/cmd/podmanV2/pods/inspect.go new file mode 100644 index 000000000..9aab610f2 --- /dev/null +++ b/cmd/podmanV2/pods/inspect.go @@ -0,0 +1,64 @@ +package pods + +import ( + "context" + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + inspectOptions = entities.PodInspectOptions{} +) + +var ( + inspectDescription = fmt.Sprintf(`Display the configuration for a pod by name or id + + By default, this will render all results in a JSON array.`) + + inspectCmd = &cobra.Command{ + Use: "inspect [flags] POD [POD...]", + Short: "Displays a pod configuration", + Long: inspectDescription, + RunE: inspect, + Example: `podman pod inspect podID`, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: inspectCmd, + Parent: podCmd, + }) + flags := inspectCmd.Flags() + flags.BoolVarP(&inspectOptions.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") + } +} + +func inspect(cmd *cobra.Command, args []string) error { + + if len(args) < 1 && !inspectOptions.Latest { + return errors.Errorf("you must provide the name or id of a running pod") + } + + if !inspectOptions.Latest { + inspectOptions.NameOrID = args[0] + } + responses, err := registry.ContainerEngine().PodInspect(context.Background(), inspectOptions) + if err != nil { + return err + } + b, err := jsoniter.MarshalIndent(responses, "", " ") + if err != nil { + return err + } + fmt.Println(string(b)) + return nil +} diff --git a/cmd/podmanV2/system/version.go b/cmd/podmanV2/system/version.go new file mode 100644 index 000000000..e8002056b --- /dev/null +++ b/cmd/podmanV2/system/version.go @@ -0,0 +1,119 @@ +package system + +import ( + "fmt" + "io" + "os" + "strings" + "text/tabwriter" + "time" + + "github.com/containers/buildah/pkg/formats" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + versionCommand = &cobra.Command{ + Use: "version", + Args: cobra.NoArgs, + Short: "Display the Podman Version Information", + RunE: version, + PersistentPreRunE: preRunE, + } + format string +) + +type versionStruct struct { + Client define.Version + Server define.Version +} + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: versionCommand, + }) + flags := versionCommand.Flags() + flags.StringVarP(&format, "format", "f", "", "Change the output format to JSON or a Go template") +} + +func version(cmd *cobra.Command, args []string) error { + var ( + v versionStruct + err error + ) + v.Client, err = define.GetVersion() + if err != nil { + return errors.Wrapf(err, "unable to determine version") + } + // TODO we need to discuss how to implement + // this more. current endpoints dont have a + // version endpoint. maybe we use info? + //if remote { + // v.Server, err = getRemoteVersion(c) + // if err != nil { + // return err + // } + //} else { + v.Server = v.Client + //} + + versionOutputFormat := format + if versionOutputFormat != "" { + if strings.Join(strings.Fields(versionOutputFormat), "") == "{{json.}}" { + versionOutputFormat = formats.JSONString + } + var out formats.Writer + switch versionOutputFormat { + case formats.JSONString: + out = formats.JSONStruct{Output: v} + return out.Out() + default: + out = formats.StdoutTemplate{Output: v, Template: versionOutputFormat} + err := out.Out() + if err != nil { + // On Failure, assume user is using older version of podman version --format and check client + out = formats.StdoutTemplate{Output: v.Client, Template: versionOutputFormat} + if err1 := out.Out(); err1 != nil { + return err + } + } + } + return nil + } + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + defer w.Flush() + + if registry.IsRemote() { + if _, err := fmt.Fprintf(w, "Client:\n"); err != nil { + return err + } + formatVersion(w, v.Client) + if _, err := fmt.Fprintf(w, "\nServer:\n"); err != nil { + return err + } + formatVersion(w, v.Server) + } else { + formatVersion(w, v.Client) + } + return nil +} + +func formatVersion(writer io.Writer, version define.Version) { + fmt.Fprintf(writer, "Version:\t%s\n", version.Version) + fmt.Fprintf(writer, "RemoteAPI Version:\t%d\n", version.RemoteAPIVersion) + fmt.Fprintf(writer, "Go Version:\t%s\n", version.GoVersion) + if version.GitCommit != "" { + fmt.Fprintf(writer, "Git Commit:\t%s\n", version.GitCommit) + } + // Prints out the build time in readable format + if version.Built != 0 { + fmt.Fprintf(writer, "Built:\t%s\n", time.Unix(version.Built, 0).Format(time.ANSIC)) + } + + fmt.Fprintf(writer, "OS/Arch:\t%s\n", version.OsArch) +} diff --git a/completions/bash/podman b/completions/bash/podman index 77f881d53..6997db3b5 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -2681,6 +2681,7 @@ _podman_play_kube() { --authfile --cert-dir --creds + --network " local boolean_options=" diff --git a/docs/source/markdown/podman-play-kube.1.md b/docs/source/markdown/podman-play-kube.1.md index 2367ff7fe..dd9441800 100644 --- a/docs/source/markdown/podman-play-kube.1.md +++ b/docs/source/markdown/podman-play-kube.1.md @@ -36,6 +36,10 @@ The [username[:password]] to use to authenticate with the registry if required. If one or both values are not supplied, a command line prompt will appear and the value can be entered. The password is entered without echo. +**--network**=*cni networks* + +A comma-separated list of the names of CNI networks the pod should join. + **--quiet**, **-q** Suppress output information when pulling images @@ -62,8 +66,16 @@ $ podman play kube demo.yml 52182811df2b1e73f36476003a66ec872101ea59034ac0d4d3a7b40903b955a6 ``` +CNI network(s) can be specified as comma-separated list using ``--network`` +``` +$ podman play kube demo.yml --network cni1,cni2 +52182811df2b1e73f36476003a66ec872101ea59034ac0d4d3a7b40903b955a6 +``` + +Please take into account that CNI networks must be created first using podman-network-create(1). + ## SEE ALSO -podman(1), podman-container(1), podman-pod(1), podman-generate-kube(1), podman-play(1) +podman(1), podman-container(1), podman-pod(1), podman-generate-kube(1), podman-play(1), podman-network-create(1) ## HISTORY December 2018, Originally compiled by Brent Baude (bbaude at redhat dot com) @@ -10,7 +10,7 @@ require ( github.com/containernetworking/cni v0.7.2-0.20200304161608-4fae32b84921 github.com/containernetworking/plugins v0.8.5 github.com/containers/buildah v1.14.6-0.20200402210551-e9a6703edee2 - github.com/containers/common v0.8.0 + github.com/containers/common v0.8.1 github.com/containers/conmon v2.0.14+incompatible github.com/containers/image/v5 v5.3.1 github.com/containers/psgo v1.4.0 @@ -42,7 +42,7 @@ require ( github.com/opencontainers/runc v1.0.0-rc9 github.com/opencontainers/runtime-spec v0.1.2-0.20190618234442-a950415649c7 github.com/opencontainers/runtime-tools v0.9.0 - github.com/opencontainers/selinux v1.4.0 + github.com/opencontainers/selinux v1.5.0 github.com/opentracing/opentracing-go v1.1.0 github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.0 @@ -71,6 +71,8 @@ github.com/containers/common v0.6.1/go.mod h1:m62kenckrWi5rZx32kaLje2Og0hpf6NsaT github.com/containers/common v0.7.0/go.mod h1:UmhIdvSkhTR0hWR01AnuZGNufm80+A0s8isb05eTmz0= github.com/containers/common v0.8.0 h1:C+wjkcmR4gooeKCXZpyjsHSFARm5AZRegflGz0x0MMw= github.com/containers/common v0.8.0/go.mod h1:QJTx9+SvhHKP6e+p7Nxqc8oNnS5rSf0KVhxudIbDslU= +github.com/containers/common v0.8.1 h1:1IUwAtZ4mC7GYRr4AC23cHf2oXCuoLzTUoSzIkSgnYw= +github.com/containers/common v0.8.1/go.mod h1:VxDJbaA1k6N1TNv9Rt6bQEF4hyKVHNfOfGA5L91ADEs= github.com/containers/common v1.0.0 h1:sZB48LzGP4bP1CmrkQIFUzdUVBysqRv3kWVk4+qbaVA= github.com/containers/common v1.0.0/go.mod h1:m62kenckrWi5rZx32kaLje2Og0hpf6NsaTBn6+b+Oys= github.com/containers/conmon v2.0.14+incompatible h1:knU1O1QxXy5YxtjMQVKEyCajROaehizK9FHaICl+P5Y= @@ -353,6 +355,8 @@ github.com/opencontainers/selinux v1.3.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwy github.com/opencontainers/selinux v1.3.2/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= github.com/opencontainers/selinux v1.4.0 h1:cpiX/2wWIju/6My60T6/z9CxNG7c8xTQyEmA9fChpUo= github.com/opencontainers/selinux v1.4.0/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= +github.com/opencontainers/selinux v1.5.0 h1:giFN+hbiSqvKWPyagmNk9sABaH7VUZ/+XS7tInqDQ6c= +github.com/opencontainers/selinux v1.5.0/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= github.com/openshift/api v0.0.0-20200106203948-7ab22a2c8316 h1:enQG2QUGwug4fR1yM6hL0Fjzx6Km/exZY6RbSPwMu3o= github.com/openshift/api v0.0.0-20200106203948-7ab22a2c8316/go.mod h1:dv+J0b/HWai0QnMVb37/H0v36klkLBi2TNpPeWDxX10= github.com/openshift/imagebuilder v1.1.3 h1:8TiphsD2wboU7tygtGZ5ZBfCP9FH2ZtvEAli67V2PJ4= @@ -587,6 +591,7 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72 h1:bw9doJza/SFBEweII/rHQh338oozWyiFsBRHtrflcws= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= diff --git a/libpod/container_api.go b/libpod/container_api.go index 967180437..55c79fa74 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -3,7 +3,6 @@ package libpod import ( "bufio" "context" - "io" "io/ioutil" "net" "os" @@ -96,7 +95,7 @@ func (c *Container) Start(ctx context.Context, recursive bool) (err error) { // The channel will be closed automatically after the result of attach has been // sent. // If recursive is set, StartAndAttach will also start all containers this container depends on. -func (c *Container) StartAndAttach(ctx context.Context, streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, recursive bool) (attachResChan <-chan error, err error) { +func (c *Container) StartAndAttach(ctx context.Context, streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, recursive bool) (attachResChan <-chan error, err error) { if !c.batched { c.lock.Lock() defer c.lock.Unlock() @@ -213,29 +212,10 @@ func (c *Container) Kill(signal uint) error { return c.save() } -// AttachStreams contains streams that will be attached to the container -type AttachStreams struct { - // OutputStream will be attached to container's STDOUT - OutputStream io.WriteCloser - // ErrorStream will be attached to container's STDERR - ErrorStream io.WriteCloser - // InputStream will be attached to container's STDIN - InputStream *bufio.Reader - // AttachOutput is whether to attach to STDOUT - // If false, stdout will not be attached - AttachOutput bool - // AttachError is whether to attach to STDERR - // If false, stdout will not be attached - AttachError bool - // AttachInput is whether to attach to STDIN - // If false, stdout will not be attached - AttachInput bool -} - // Attach attaches to a container. // This function returns when the attach finishes. It does not hold the lock for // the duration of its runtime, only using it at the beginning to verify state. -func (c *Container) Attach(streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize) error { +func (c *Container) Attach(streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize) error { if !c.batched { c.lock.Lock() if err := c.syncContainer(); err != nil { diff --git a/libpod/container_exec.go b/libpod/container_exec.go index 5469462f8..c1ce8b724 100644 --- a/libpod/container_exec.go +++ b/libpod/container_exec.go @@ -221,7 +221,7 @@ func (c *Container) ExecStart(sessionID string) error { // ExecStartAndAttach starts and attaches to an exec session in a container. // TODO: Should we include detach keys in the signature to allow override? // TODO: How do we handle AttachStdin/AttachStdout/AttachStderr? -func (c *Container) ExecStartAndAttach(sessionID string, streams *AttachStreams) error { +func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachStreams) error { if !c.batched { c.lock.Lock() defer c.lock.Unlock() @@ -544,7 +544,7 @@ func (c *Container) ExecResize(sessionID string, newSize remotecommand.TerminalS // Exec emulates the old Libpod exec API, providing a single call to create, // run, and remove an exec session. Returns exit code and error. Exit code is // not guaranteed to be set sanely if error is not nil. -func (c *Container) Exec(config *ExecConfig, streams *AttachStreams, resize <-chan remotecommand.TerminalSize) (int, error) { +func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resize <-chan remotecommand.TerminalSize) (int, error) { sessionID, err := c.ExecCreate(config) if err != nil { return -1, err diff --git a/libpod/container_top_linux.go b/libpod/container_top_linux.go index 2a35a2ae9..98a69966a 100644 --- a/libpod/container_top_linux.go +++ b/libpod/container_top_linux.go @@ -112,7 +112,7 @@ func (c *Container) execPS(args []string) ([]string, error) { defer wErrPipe.Close() defer rErrPipe.Close() - streams := new(AttachStreams) + streams := new(define.AttachStreams) streams.OutputStream = wPipe streams.ErrorStream = wErrPipe streams.AttachOutput = true diff --git a/libpod/define/config.go b/libpod/define/config.go index 7b967f17d..10e00062a 100644 --- a/libpod/define/config.go +++ b/libpod/define/config.go @@ -1,5 +1,10 @@ package define +import ( + "bufio" + "io" +) + var ( // DefaultInfraImage to use for infra container DefaultInfraImage = "k8s.gcr.io/pause:3.2" @@ -33,3 +38,22 @@ const ( V2s2ManifestDir = "docker-dir" V2s2Archive = "docker-archive" ) + +// AttachStreams contains streams that will be attached to the container +type AttachStreams struct { + // OutputStream will be attached to container's STDOUT + OutputStream io.WriteCloser + // ErrorStream will be attached to container's STDERR + ErrorStream io.WriteCloser + // InputStream will be attached to container's STDIN + InputStream *bufio.Reader + // AttachOutput is whether to attach to STDOUT + // If false, stdout will not be attached + AttachOutput bool + // AttachError is whether to attach to STDERR + // If false, stdout will not be attached + AttachError bool + // AttachInput is whether to attach to STDIN + // If false, stdout will not be attached + AttachInput bool +} diff --git a/libpod/healthcheck.go b/libpod/healthcheck.go index 08a613dfe..daddb6561 100644 --- a/libpod/healthcheck.go +++ b/libpod/healthcheck.go @@ -108,7 +108,7 @@ func (c *Container) runHealthCheck() (HealthCheckStatus, error) { hcw := hcWriteCloser{ captureBuffer, } - streams := new(AttachStreams) + streams := new(define.AttachStreams) streams.OutputStream = hcw streams.ErrorStream = hcw diff --git a/libpod/oci.go b/libpod/oci.go index ef46cf5c3..e4fbcb62e 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -4,6 +4,8 @@ import ( "bufio" "net" + "github.com/containers/libpod/libpod/define" + "k8s.io/client-go/tools/remotecommand" ) @@ -141,7 +143,7 @@ type ExecOptions struct { // the container was run as will be used. User string // Streams are the streams that will be attached to the container. - Streams *AttachStreams + Streams *define.AttachStreams // PreserveFDs is a number of additional file descriptors (in addition // to 0, 1, 2) that will be passed to the executed process. The total FDs // passed will be 3 + PreserveFDs. diff --git a/libpod/oci_attach_linux.go b/libpod/oci_attach_linux.go index fb0a54bff..ff158c2d1 100644 --- a/libpod/oci_attach_linux.go +++ b/libpod/oci_attach_linux.go @@ -31,7 +31,7 @@ const ( // Attach to the given container // Does not check if state is appropriate // started is only required if startContainer is true -func (c *Container) attach(streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, startContainer bool, started chan bool) error { +func (c *Container) attach(streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, startContainer bool, started chan bool) error { if !streams.AttachOutput && !streams.AttachError && !streams.AttachInput { return errors.Wrapf(define.ErrInvalidArg, "must provide at least one stream to attach to") } @@ -94,7 +94,7 @@ func (c *Container) attach(streams *AttachStreams, keys string, resize <-chan re // 4. attachToExec sends on startFd, signalling it has attached to the socket and child is ready to go // 5. child receives on startFd, runs the runtime exec command // attachToExec is responsible for closing startFd and attachFd -func (c *Container) attachToExec(streams *AttachStreams, keys *string, sessionID string, startFd, attachFd *os.File) error { +func (c *Container) attachToExec(streams *define.AttachStreams, keys *string, sessionID string, startFd, attachFd *os.File) error { if !streams.AttachOutput && !streams.AttachError && !streams.AttachInput { return errors.Wrapf(define.ErrInvalidArg, "must provide at least one stream to attach to") } @@ -189,7 +189,7 @@ func buildSocketPath(socketPath string) string { return socketPath } -func setupStdioChannels(streams *AttachStreams, conn *net.UnixConn, detachKeys []byte) (chan error, chan error) { +func setupStdioChannels(streams *define.AttachStreams, conn *net.UnixConn, detachKeys []byte) (chan error, chan error) { receiveStdoutError := make(chan error) go func() { receiveStdoutError <- redirectResponseToOutputStreams(streams.OutputStream, streams.ErrorStream, streams.AttachOutput, streams.AttachError, conn) @@ -257,7 +257,7 @@ func redirectResponseToOutputStreams(outputStream, errorStream io.Writer, writeO return err } -func readStdio(streams *AttachStreams, receiveStdoutError, stdinDone chan error) error { +func readStdio(streams *define.AttachStreams, receiveStdoutError, stdinDone chan error) error { var err error select { case err = <-receiveStdoutError: diff --git a/libpod/oci_attach_unsupported.go b/libpod/oci_attach_unsupported.go index 987d2c973..3b0216e5d 100644 --- a/libpod/oci_attach_unsupported.go +++ b/libpod/oci_attach_unsupported.go @@ -9,10 +9,10 @@ import ( "k8s.io/client-go/tools/remotecommand" ) -func (c *Container) attach(streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, startContainer bool, started chan bool) error { +func (c *Container) attach(streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, startContainer bool, started chan bool) error { return define.ErrNotImplemented } -func (c *Container) attachToExec(streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, sessionID string, startFd *os.File, attachFd *os.File) error { +func (c *Container) attachToExec(streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, sessionID string, startFd *os.File, attachFd *os.File) error { return define.ErrNotImplemented } diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index b4ebeb944..ecadbd2f9 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -1004,7 +1004,7 @@ func (r *LocalRuntime) ExecContainer(ctx context.Context, cli *cliconfig.ExecVal } env = envLib.Join(env, cliEnv) - streams := new(libpod.AttachStreams) + streams := new(define.AttachStreams) streams.OutputStream = os.Stdout streams.ErrorStream = os.Stderr if cli.Interactive { diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go index 102eabd8b..7c2a84cc7 100644 --- a/pkg/adapter/pods.go +++ b/pkg/adapter/pods.go @@ -343,7 +343,7 @@ func (r *LocalRuntime) CreatePod(ctx context.Context, cli *cliconfig.PodCreateVa logrus.Debugf("Pod will use host networking") options = append(options, libpod.WithPodHostNetwork()) case "": - return "", errors.Errorf("invalid value passed to --net: must provide a comma-separated list of CNI networks or host") + return "", errors.Errorf("invalid value passed to --network: must provide a comma-separated list of CNI networks or host") default: // We'll assume this is a comma-separated list of CNI // networks. @@ -595,6 +595,22 @@ func (r *LocalRuntime) PlayKubeYAML(ctx context.Context, c *cliconfig.KubePlayVa podPorts := getPodPorts(podYAML.Spec.Containers) podOptions = append(podOptions, libpod.WithInfraContainerPorts(podPorts)) + if c.Flag("network").Changed { + netValue := c.String("network") + switch strings.ToLower(netValue) { + case "bridge", "host": + return nil, errors.Errorf("invalid value passed to --network: bridge or host networking must be configured in YAML") + case "": + return nil, errors.Errorf("invalid value passed to --network: must provide a comma-separated list of CNI networks") + default: + // We'll assume this is a comma-separated list of CNI + // networks. + networks := strings.Split(netValue, ",") + logrus.Debugf("Pod joining CNI networks: %v", networks) + podOptions = append(podOptions, libpod.WithPodNetworks(networks)) + } + } + // Create the Pod pod, err = r.NewPod(ctx, podOptions...) if err != nil { diff --git a/pkg/adapter/terminal_linux.go b/pkg/adapter/terminal_linux.go index ef5a6f926..a56704be6 100644 --- a/pkg/adapter/terminal_linux.go +++ b/pkg/adapter/terminal_linux.go @@ -7,6 +7,7 @@ import ( "os" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh/terminal" @@ -14,7 +15,7 @@ import ( ) // ExecAttachCtr execs and attaches to a container -func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *libpod.AttachStreams, preserveFDs uint, detachKeys string) (int, error) { +func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *define.AttachStreams, preserveFDs uint, detachKeys string) (int, error) { resize := make(chan remotecommand.TerminalSize) haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd())) @@ -69,7 +70,7 @@ func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, defer cancel() } - streams := new(libpod.AttachStreams) + streams := new(define.AttachStreams) streams.OutputStream = stdout streams.ErrorStream = stderr streams.InputStream = bufio.NewReader(stdin) diff --git a/pkg/adapter/terminal_unsupported.go b/pkg/adapter/terminal_unsupported.go index 3009f0a38..9067757a1 100644 --- a/pkg/adapter/terminal_unsupported.go +++ b/pkg/adapter/terminal_unsupported.go @@ -11,7 +11,7 @@ import ( ) // ExecAttachCtr execs and attaches to a container -func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *libpod.AttachStreams, preserveFDs uint, detachKeys string) (int, error) { +func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *define.AttachStreams, preserveFDs uint, detachKeys string) (int, error) { return -1, define.ErrNotImplemented } diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index e834029b2..a890169a1 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -73,7 +73,10 @@ func PodInspect(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } - utils.WriteResponse(w, http.StatusOK, podData) + report := entities.PodInspectReport{ + PodInspect: podData, + } + utils.WriteResponse(w, http.StatusOK, report) } func PodStop(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/bindings/pods/pods.go b/pkg/bindings/pods/pods.go index ae87c00e9..83847614a 100644 --- a/pkg/bindings/pods/pods.go +++ b/pkg/bindings/pods/pods.go @@ -7,7 +7,6 @@ import ( "strconv" "strings" - "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/bindings" "github.com/containers/libpod/pkg/domain/entities" @@ -49,17 +48,19 @@ func Exists(ctx context.Context, nameOrID string) (bool, error) { } // Inspect returns low-level information about the given pod. -func Inspect(ctx context.Context, nameOrID string) (*libpod.PodInspect, error) { +func Inspect(ctx context.Context, nameOrID string) (*entities.PodInspectReport, error) { + var ( + report entities.PodInspectReport + ) conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - inspect := libpod.PodInspect{} response, err := conn.DoRequest(nil, http.MethodGet, "/pods/%s/json", nil, nameOrID) if err != nil { - return &inspect, err + return nil, err } - return &inspect, response.Process(&inspect) + return &report, response.Process(&report) } // Kill sends a SIGTERM to all the containers in a pod. The optional signal parameter diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 74b23cd71..6b4bb9ba2 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -2,6 +2,7 @@ package entities import ( "io" + "os" "time" "github.com/containers/libpod/libpod/define" @@ -157,3 +158,52 @@ type RestoreReport struct { type ContainerCreateReport struct { Id string } + +// AttachOptions describes the cli and other values +// needed to perform an attach +type AttachOptions struct { + DetachKeys string + Latest bool + NoStdin bool + SigProxy bool + Stdin *os.File + Stdout *os.File + Stderr *os.File +} + +// ExecOptions describes the cli values to exec into +// a container +type ExecOptions struct { + Cmd []string + DetachKeys string + Envs map[string]string + Interactive bool + Latest bool + PreserveFDs uint + Privileged bool + Streams define.AttachStreams + Tty bool + User string + WorkDir string +} + +// ContainerStartOptions describes the val from the +// CLI needed to start a container +type ContainerStartOptions struct { + Attach bool + DetachKeys string + Interactive bool + Latest bool + SigProxy bool + Stdout *os.File + Stderr *os.File + Stdin *os.File +} + +// ContainerStartReport describes the response from starting +// containers from the cli +type ContainerStartReport struct { + Id string + Err error + ExitCode int +} diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 025da50f3..264780771 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -8,10 +8,12 @@ import ( ) type ContainerEngine interface { + ContainerAttach(ctx context.Context, nameOrId string, options AttachOptions) error ContainerCommit(ctx context.Context, nameOrId string, options CommitOptions) (*CommitReport, error) ContainerCheckpoint(ctx context.Context, namesOrIds []string, options CheckpointOptions) ([]*CheckpointReport, error) ContainerRestore(ctx context.Context, namesOrIds []string, options RestoreOptions) ([]*RestoreReport, error) ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error) + ContainerExec(ctx context.Context, nameOrId string, options ExecOptions) (int, error) ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, error) ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, error) ContainerExport(ctx context.Context, nameOrId string, options ContainerExportOptions) error @@ -19,11 +21,13 @@ type ContainerEngine interface { ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error) ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*RmReport, error) + ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error) ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error) ContainerTop(ctx context.Context, options TopOptions) (*StringSliceReport, error) ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error) HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error) + PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error) PodExists(ctx context.Context, nameOrId string) (*BoolReport, error) PodKill(ctx context.Context, namesOrIds []string, options PodKillOptions) ([]*PodKillReport, error) @@ -35,6 +39,8 @@ type ContainerEngine interface { PodStop(ctx context.Context, namesOrIds []string, options PodStopOptions) ([]*PodStopReport, error) PodTop(ctx context.Context, options PodTopOptions) (*StringSliceReport, error) PodUnpause(ctx context.Context, namesOrIds []string, options PodunpauseOptions) ([]*PodUnpauseReport, error) + PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error) + VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IdOrNameResponse, error) VolumeInspect(ctx context.Context, namesOrIds []string, opts VolumeInspectOptions) ([]*VolumeInspectReport, error) VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error) diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index a0b2c6cec..cd2e79961 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -3,6 +3,7 @@ package entities import ( "time" + "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/specgen" ) @@ -164,3 +165,14 @@ type PodPSOptions struct { Quiet bool Sort string } + +type PodInspectOptions struct { + Latest bool + + // Options for the API. + NameOrID string +} + +type PodInspectReport struct { + *libpod.PodInspect +} diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index d3d51db82..929c3f335 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -5,16 +5,18 @@ package abi import ( "context" "io/ioutil" + "strconv" "strings" "github.com/containers/buildah" "github.com/containers/image/v5/manifest" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/libpod/events" "github.com/containers/libpod/libpod/image" - "github.com/containers/libpod/pkg/adapter/shortcuts" "github.com/containers/libpod/pkg/checkpoint" "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/domain/infra/abi/terminal" "github.com/containers/libpod/pkg/signal" "github.com/containers/libpod/pkg/specgen" "github.com/containers/libpod/pkg/specgen/generate" @@ -64,7 +66,7 @@ func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []strin var ( responses []entities.WaitReport ) - ctrs, err := shortcuts.GetContainersByContext(false, options.Latest, namesOrIds, ic.Libpod) + ctrs, err := getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod) if err != nil { return nil, err } @@ -90,7 +92,7 @@ func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []stri if options.All { ctrs, err = ic.Libpod.GetAllContainers() } else { - ctrs, err = shortcuts.GetContainersByContext(false, false, namesOrIds, ic.Libpod) + ctrs, err = getContainersByContext(false, false, namesOrIds, ic.Libpod) } if err != nil { return nil, err @@ -111,7 +113,7 @@ func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []st if options.All { ctrs, err = ic.Libpod.GetAllContainers() } else { - ctrs, err = shortcuts.GetContainersByContext(false, false, namesOrIds, ic.Libpod) + ctrs, err = getContainersByContext(false, false, namesOrIds, ic.Libpod) } if err != nil { return nil, err @@ -135,7 +137,7 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin id := strings.Split(string(content), "\n")[0] names = append(names, id) } - ctrs, err := shortcuts.GetContainersByContext(options.All, options.Latest, names, ic.Libpod) + ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod) if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) { return nil, err } @@ -171,7 +173,7 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin if err != nil { return nil, err } - ctrs, err := shortcuts.GetContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) + ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) if err != nil { return nil, err } @@ -187,7 +189,7 @@ func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []st var ( reports []*entities.RestartReport ) - ctrs, err := shortcuts.GetContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) + ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) if err != nil { return nil, err } @@ -229,7 +231,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, names = append(names, id) } - ctrs, err := shortcuts.GetContainersByContext(options.All, options.Latest, names, ic.Libpod) + ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod) if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) { // Failed to get containers. If force is specified, get the containers ID // and evict them @@ -277,7 +279,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, error) { var reports []*entities.ContainerInspectReport - ctrs, err := shortcuts.GetContainersByContext(false, options.Latest, namesOrIds, ic.Libpod) + ctrs, err := getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod) if err != nil { return nil, err } @@ -455,3 +457,163 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG } return &entities.ContainerCreateReport{Id: ctr.ID()}, nil } + +func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string, options entities.AttachOptions) error { + ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrId}, ic.Libpod) + if err != nil { + return err + } + ctr := ctrs[0] + conState, err := ctr.State() + if err != nil { + return errors.Wrapf(err, "unable to determine state of %s", ctr.ID()) + } + if conState != define.ContainerStateRunning { + return errors.Errorf("you can only attach to running containers") + } + + // If the container is in a pod, also set to recursively start dependencies + if err := terminal.StartAttachCtr(ctx, ctr, options.Stdin, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false, ctr.PodID() != ""); err != nil && errors.Cause(err) != define.ErrDetach { + return errors.Wrapf(err, "error attaching to container %s", ctr.ID()) + } + return nil +} + +func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions) (int, error) { + ec := define.ExecErrorCodeGeneric + if options.PreserveFDs > 0 { + entries, err := ioutil.ReadDir("/proc/self/fd") + if err != nil { + return ec, errors.Wrapf(err, "unable to read /proc/self/fd") + } + + m := make(map[int]bool) + for _, e := range entries { + i, err := strconv.Atoi(e.Name()) + if err != nil { + return ec, errors.Wrapf(err, "cannot parse %s in /proc/self/fd", e.Name()) + } + m[i] = true + } + + for i := 3; i < 3+int(options.PreserveFDs); i++ { + if _, found := m[i]; !found { + return ec, errors.New("invalid --preserve-fds=N specified. Not enough FDs available") + } + } + } + ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrId}, ic.Libpod) + if err != nil { + return ec, err + } + ctr := ctrs[0] + ec, err = terminal.ExecAttachCtr(ctx, ctr, options.Tty, options.Privileged, options.Envs, options.Cmd, options.User, options.WorkDir, &options.Streams, options.PreserveFDs, options.DetachKeys) + return define.TranslateExecErrorToExitCode(ec, err), err +} + +func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) { + var reports []*entities.ContainerStartReport + var exitCode = define.ExecErrorCodeGeneric + ctrs, err := getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod) + if err != nil { + return nil, err + } + // There can only be one container if attach was used + for _, ctr := range ctrs { + ctrState, err := ctr.State() + if err != nil { + return nil, err + } + ctrRunning := ctrState == define.ContainerStateRunning + + if options.Attach { + err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, !ctrRunning, ctr.PodID() != "") + if errors.Cause(err) == define.ErrDetach { + // User manually detached + // Exit cleanly immediately + reports = append(reports, &entities.ContainerStartReport{ + Id: ctr.ID(), + Err: nil, + ExitCode: 0, + }) + return reports, nil + } + + if errors.Cause(err) == define.ErrWillDeadlock { + logrus.Debugf("Deadlock error: %v", err) + reports = append(reports, &entities.ContainerStartReport{ + Id: ctr.ID(), + Err: err, + ExitCode: define.ExitCode(err), + }) + return reports, errors.Errorf("attempting to start container %s would cause a deadlock; please run 'podman system renumber' to resolve", ctr.ID()) + } + + if ctrRunning { + reports = append(reports, &entities.ContainerStartReport{ + Id: ctr.ID(), + Err: nil, + ExitCode: 0, + }) + return reports, err + } + + if err != nil { + reports = append(reports, &entities.ContainerStartReport{ + Id: ctr.ID(), + Err: err, + ExitCode: exitCode, + }) + return reports, errors.Wrapf(err, "unable to start container %s", ctr.ID()) + } + + if ecode, err := ctr.Wait(); err != nil { + if errors.Cause(err) == define.ErrNoSuchCtr { + // Check events + event, err := ic.Libpod.GetLastContainerEvent(ctr.ID(), events.Exited) + if err != nil { + logrus.Errorf("Cannot get exit code: %v", err) + exitCode = define.ExecErrorCodeNotFound + } else { + exitCode = event.ContainerExitCode + } + } + } else { + exitCode = int(ecode) + } + reports = append(reports, &entities.ContainerStartReport{ + Id: ctr.ID(), + Err: err, + ExitCode: exitCode, + }) + return reports, nil + } // end attach + + // Start the container if it's not running already. + if !ctrRunning { + // Handle non-attach start + // If the container is in a pod, also set to recursively start dependencies + report := &entities.ContainerStartReport{ + Id: ctr.ID(), + ExitCode: 125, + } + if err := ctr.Start(ctx, ctr.PodID() != ""); err != nil { + //if lastError != nil { + // fmt.Fprintln(os.Stderr, lastError) + //} + report.Err = err + if errors.Cause(err) == define.ErrWillDeadlock { + report.Err = errors.Wrapf(err, "please run 'podman system renumber' to resolve deadlocks") + reports = append(reports, report) + continue + } + report.Err = errors.Wrapf(err, "unable to start container %q", ctr.ID()) + reports = append(reports, report) + continue + } + report.ExitCode = 0 + reports = append(reports, report) + } + } + return reports, nil +} diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go index 494a048ec..073cd8d5c 100644 --- a/pkg/domain/infra/abi/pods.go +++ b/pkg/domain/infra/abi/pods.go @@ -331,3 +331,24 @@ func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOpti } return reports, nil } + +func (ic *ContainerEngine) PodInspect(ctx context.Context, options entities.PodInspectOptions) (*entities.PodInspectReport, error) { + var ( + pod *libpod.Pod + err error + ) + // Look up the pod. + if options.Latest { + pod, err = ic.Libpod.GetLatestPod() + } else { + pod, err = ic.Libpod.LookupPod(options.NameOrID) + } + if err != nil { + return nil, errors.Wrap(err, "unable to lookup requested container") + } + inspect, err := pod.Inspect() + if err != nil { + return nil, err + } + return &entities.PodInspectReport{PodInspect: inspect}, nil +} diff --git a/pkg/domain/infra/abi/terminal/sigproxy_linux.go b/pkg/domain/infra/abi/terminal/sigproxy_linux.go new file mode 100644 index 000000000..d7f5853d8 --- /dev/null +++ b/pkg/domain/infra/abi/terminal/sigproxy_linux.go @@ -0,0 +1,47 @@ +// +build ABISupport + +package terminal + +import ( + "os" + "syscall" + + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/signal" + "github.com/sirupsen/logrus" +) + +// ProxySignals ... +func ProxySignals(ctr *libpod.Container) { + sigBuffer := make(chan os.Signal, 128) + signal.CatchAll(sigBuffer) + + logrus.Debugf("Enabling signal proxying") + + go func() { + for s := range sigBuffer { + // Ignore SIGCHLD and SIGPIPE - these are mostly likely + // intended for the podman command itself. + // SIGURG was added because of golang 1.14 and its preemptive changes + // causing more signals to "show up". + // https://github.com/containers/libpod/issues/5483 + if s == syscall.SIGCHLD || s == syscall.SIGPIPE || s == syscall.SIGURG { + continue + } + + if err := ctr.Kill(uint(s.(syscall.Signal))); err != nil { + // If the container dies, and we find out here, + // we need to forward that one signal to + // ourselves so that it is not lost, and then + // we terminate the proxy and let the defaults + // play out. + logrus.Errorf("Error forwarding signal %d to container %s: %v", s, ctr.ID(), err) + signal.StopCatch(sigBuffer) + if err := syscall.Kill(syscall.Getpid(), s.(syscall.Signal)); err != nil { + logrus.Errorf("failed to kill pid %d", syscall.Getpid()) + } + return + } + } + }() +} diff --git a/pkg/domain/infra/abi/terminal/terminal.go b/pkg/domain/infra/abi/terminal/terminal.go new file mode 100644 index 000000000..f187bdd6b --- /dev/null +++ b/pkg/domain/infra/abi/terminal/terminal.go @@ -0,0 +1,103 @@ +// +build ABISupport + +package terminal + +import ( + "context" + "os" + "os/signal" + + lsignal "github.com/containers/libpod/pkg/signal" + "github.com/docker/docker/pkg/term" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "k8s.io/client-go/tools/remotecommand" +) + +// RawTtyFormatter ... +type RawTtyFormatter struct { +} + +// getResize returns a TerminalSize command matching stdin's current +// size on success, and nil on errors. +func getResize() *remotecommand.TerminalSize { + winsize, err := term.GetWinsize(os.Stdin.Fd()) + if err != nil { + logrus.Warnf("Could not get terminal size %v", err) + return nil + } + return &remotecommand.TerminalSize{ + Width: winsize.Width, + Height: winsize.Height, + } +} + +// Helper for prepareAttach - set up a goroutine to generate terminal resize events +func resizeTty(ctx context.Context, resize chan remotecommand.TerminalSize) { + sigchan := make(chan os.Signal, 1) + signal.Notify(sigchan, lsignal.SIGWINCH) + go func() { + defer close(resize) + // Update the terminal size immediately without waiting + // for a SIGWINCH to get the correct initial size. + resizeEvent := getResize() + for { + if resizeEvent == nil { + select { + case <-ctx.Done(): + return + case <-sigchan: + resizeEvent = getResize() + } + } else { + select { + case <-ctx.Done(): + return + case <-sigchan: + resizeEvent = getResize() + case resize <- *resizeEvent: + resizeEvent = nil + } + } + } + }() +} + +func restoreTerminal(state *term.State) error { + logrus.SetFormatter(&logrus.TextFormatter{}) + return term.RestoreTerminal(os.Stdin.Fd(), state) +} + +// Format ... +func (f *RawTtyFormatter) Format(entry *logrus.Entry) ([]byte, error) { + textFormatter := logrus.TextFormatter{} + bytes, err := textFormatter.Format(entry) + + if err == nil { + bytes = append(bytes, '\r') + } + + return bytes, err +} + +func handleTerminalAttach(ctx context.Context, resize chan remotecommand.TerminalSize) (context.CancelFunc, *term.State, error) { + logrus.Debugf("Handling terminal attach") + + subCtx, cancel := context.WithCancel(ctx) + + resizeTty(subCtx, resize) + + oldTermState, err := term.SaveState(os.Stdin.Fd()) + if err != nil { + // allow caller to not have to do any cleaning up if we error here + cancel() + return nil, nil, errors.Wrapf(err, "unable to save terminal state") + } + + logrus.SetFormatter(&RawTtyFormatter{}) + if _, err := term.SetRawTerminal(os.Stdin.Fd()); err != nil { + return cancel, nil, err + } + + return cancel, oldTermState, nil +} diff --git a/pkg/domain/infra/abi/terminal/terminal_linux.go b/pkg/domain/infra/abi/terminal/terminal_linux.go new file mode 100644 index 000000000..664205df1 --- /dev/null +++ b/pkg/domain/infra/abi/terminal/terminal_linux.go @@ -0,0 +1,123 @@ +// +build ABISupport + +package terminal + +import ( + "bufio" + "context" + "fmt" + "os" + + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/crypto/ssh/terminal" + "k8s.io/client-go/tools/remotecommand" +) + +// ExecAttachCtr execs and attaches to a container +func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *define.AttachStreams, preserveFDs uint, detachKeys string) (int, error) { + resize := make(chan remotecommand.TerminalSize) + haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd())) + + // Check if we are attached to a terminal. If we are, generate resize + // events, and set the terminal to raw mode + if haveTerminal && tty { + cancel, oldTermState, err := handleTerminalAttach(ctx, resize) + if err != nil { + return -1, err + } + defer cancel() + defer func() { + if err := restoreTerminal(oldTermState); err != nil { + logrus.Errorf("unable to restore terminal: %q", err) + } + }() + } + + execConfig := new(libpod.ExecConfig) + execConfig.Command = cmd + execConfig.Terminal = tty + execConfig.Privileged = privileged + execConfig.Environment = env + execConfig.User = user + execConfig.WorkDir = workDir + execConfig.DetachKeys = &detachKeys + execConfig.PreserveFDs = preserveFDs + + return ctr.Exec(execConfig, streams, resize) +} + +// StartAttachCtr starts and (if required) attaches to a container +// if you change the signature of this function from os.File to io.Writer, it will trigger a downstream +// error. we may need to just lint disable this one. +func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, stdin *os.File, detachKeys string, sigProxy bool, startContainer bool, recursive bool) error { //nolint-interfacer + resize := make(chan remotecommand.TerminalSize) + + haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd())) + + // Check if we are attached to a terminal. If we are, generate resize + // events, and set the terminal to raw mode + if haveTerminal && ctr.Spec().Process.Terminal { + cancel, oldTermState, err := handleTerminalAttach(ctx, resize) + if err != nil { + return err + } + defer func() { + if err := restoreTerminal(oldTermState); err != nil { + logrus.Errorf("unable to restore terminal: %q", err) + } + }() + defer cancel() + } + + streams := new(define.AttachStreams) + streams.OutputStream = stdout + streams.ErrorStream = stderr + streams.InputStream = bufio.NewReader(stdin) + streams.AttachOutput = true + streams.AttachError = true + streams.AttachInput = true + + if stdout == nil { + logrus.Debugf("Not attaching to stdout") + streams.AttachOutput = false + } + if stderr == nil { + logrus.Debugf("Not attaching to stderr") + streams.AttachError = false + } + if stdin == nil { + logrus.Debugf("Not attaching to stdin") + streams.AttachInput = false + } + + if !startContainer { + if sigProxy { + ProxySignals(ctr) + } + + return ctr.Attach(streams, detachKeys, resize) + } + + attachChan, err := ctr.StartAndAttach(ctx, streams, detachKeys, resize, recursive) + if err != nil { + return err + } + + if sigProxy { + ProxySignals(ctr) + } + + if stdout == nil && stderr == nil { + fmt.Printf("%s\n", ctr.ID()) + } + + err = <-attachChan + if err != nil { + return errors.Wrapf(err, "error attaching to container %s", ctr.ID()) + } + + return nil +} diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index ae8994cba..4101068ba 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -305,3 +305,15 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG } return &entities.ContainerCreateReport{Id: response.ID}, nil } + +func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string, options entities.AttachOptions) error { + return errors.New("not implemented") +} + +func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions) (int, error) { + return 125, errors.New("not implemented") +} + +func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) { + return nil, errors.New("not implemented") +} diff --git a/pkg/domain/infra/tunnel/pods.go b/pkg/domain/infra/tunnel/pods.go index ad87a0a29..dad77284f 100644 --- a/pkg/domain/infra/tunnel/pods.go +++ b/pkg/domain/infra/tunnel/pods.go @@ -197,3 +197,13 @@ func (ic *ContainerEngine) PodTop(ctx context.Context, options entities.PodTopOp func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOptions) ([]*entities.ListPodsReport, error) { return pods.List(ic.ClientCxt, options.Filters) } + +func (ic *ContainerEngine) PodInspect(ctx context.Context, options entities.PodInspectOptions) (*entities.PodInspectReport, error) { + switch { + case options.Latest: + return nil, errors.New("latest is not supported") + case options.NameOrID == "": + return nil, errors.New("NameOrID must be specified") + } + return pods.Inspect(ic.ClientCxt, options.NameOrID) +} diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go new file mode 100644 index 000000000..5bafef1fe --- /dev/null +++ b/pkg/domain/infra/tunnel/system.go @@ -0,0 +1 @@ +package tunnel diff --git a/pkg/varlinkapi/attach.go b/pkg/varlinkapi/attach.go index 94f4d653e..34f351669 100644 --- a/pkg/varlinkapi/attach.go +++ b/pkg/varlinkapi/attach.go @@ -16,7 +16,7 @@ import ( "k8s.io/client-go/tools/remotecommand" ) -func setupStreams(call iopodman.VarlinkCall) (*bufio.Reader, *bufio.Writer, *io.PipeReader, *io.PipeWriter, *libpod.AttachStreams) { +func setupStreams(call iopodman.VarlinkCall) (*bufio.Reader, *bufio.Writer, *io.PipeReader, *io.PipeWriter, *define.AttachStreams) { // These are the varlink sockets reader := call.Call.Reader @@ -30,7 +30,7 @@ func setupStreams(call iopodman.VarlinkCall) (*bufio.Reader, *bufio.Writer, *io. // TODO if runc ever starts passing stderr, we can too // stderrWriter := NewVirtWriteCloser(writer, ToStderr) - streams := libpod.AttachStreams{ + streams := define.AttachStreams{ OutputStream: stdoutWriter, InputStream: bufio.NewReader(pr), // Runc eats the error stream @@ -117,7 +117,7 @@ func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys st return call.Writer.Flush() } -func attach(ctr *libpod.Container, streams *libpod.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error { +func attach(ctr *libpod.Container, streams *define.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error { go func() { if err := ctr.Attach(streams, detachKeys, resize); err != nil { errChan <- err @@ -127,7 +127,7 @@ func attach(ctr *libpod.Container, streams *libpod.AttachStreams, detachKeys str return attachError } -func startAndAttach(ctr *libpod.Container, streams *libpod.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error { +func startAndAttach(ctr *libpod.Container, streams *define.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error { var finalErr error attachChan, err := ctr.StartAndAttach(getContext(), streams, detachKeys, resize, false) if err != nil { 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 599bdb6e2..9c979e5e2 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go @@ -31,6 +31,7 @@ const ( // Disabled constant to indicate SELinux is disabled Disabled = -1 + contextFile = "/usr/share/containers/selinux/contexts" selinuxDir = "/etc/selinux/" selinuxConfig = selinuxDir + "config" selinuxfsMount = "/sys/fs/selinux" @@ -684,23 +685,26 @@ func ROFileLabel() string { return roFileLabel } -/* -ContainerLabels returns an allocated processLabel and fileLabel to be used for -container labeling by the calling process. -*/ -func ContainerLabels() (processLabel string, fileLabel string) { +func openContextFile() (*os.File, error) { + if f, err := os.Open(contextFile); err == nil { + return f, nil + } + lxcPath := filepath.Join(getSELinuxPolicyRoot(), "/contexts/lxc_contexts") + return os.Open(lxcPath) +} + +var labels = loadLabels() + +func loadLabels() map[string]string { var ( val, key string bufin *bufio.Reader ) - if !GetEnabled() { - return "", "" - } - lxcPath := fmt.Sprintf("%s/contexts/lxc_contexts", getSELinuxPolicyRoot()) - in, err := os.Open(lxcPath) + labels := make(map[string]string) + in, err := openContextFile() if err != nil { - return "", "" + return labels } defer in.Close() @@ -712,7 +716,7 @@ func ContainerLabels() (processLabel string, fileLabel string) { if err == io.EOF { done = true } else { - goto exit + break } } line = strings.TrimSpace(line) @@ -726,26 +730,64 @@ func ContainerLabels() (processLabel string, fileLabel string) { } if groups := assignRegex.FindStringSubmatch(line); groups != nil { key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2]) - if key == "process" { - processLabel = strings.Trim(val, "\"") - } - if key == "file" { - fileLabel = strings.Trim(val, "\"") - } - if key == "ro_file" { - roFileLabel = strings.Trim(val, "\"") - } + labels[key] = strings.Trim(val, "\"") } } - if processLabel == "" || fileLabel == "" { + return labels +} + +/* +KVMContainerLabels returns the default processLabel and mountLabel to be used +for kvm containers by the calling process. +*/ +func KVMContainerLabels() (string, string) { + processLabel := labels["kvm_process"] + if processLabel == "" { + processLabel = labels["process"] + } + + return addMcs(processLabel, labels["file"]) +} + +/* +InitContainerLabels returns the default processLabel and file labels to be +used for containers running an init system like systemd by the calling process. +*/ +func InitContainerLabels() (string, string) { + processLabel := labels["init_process"] + if processLabel == "" { + processLabel = labels["process"] + } + + return addMcs(processLabel, labels["file"]) +} + +/* +ContainerLabels returns an allocated processLabel and fileLabel to be used for +container labeling by the calling process. +*/ +func ContainerLabels() (processLabel string, fileLabel string) { + if !GetEnabled() { return "", "" } + processLabel = labels["process"] + fileLabel = labels["file"] + roFileLabel = labels["ro_file"] + + if processLabel == "" || fileLabel == "" { + return "", fileLabel + } + if roFileLabel == "" { roFileLabel = fileLabel } -exit: + + return addMcs(processLabel, fileLabel) +} + +func addMcs(processLabel, fileLabel string) (string, string) { scon, _ := NewContext(processLabel) if scon["level"] != "" { mcs := uniqMcs(1024) diff --git a/vendor/modules.txt b/vendor/modules.txt index 2ec974c02..f4663b04d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -82,7 +82,7 @@ github.com/containers/buildah/pkg/secrets github.com/containers/buildah/pkg/supplemented github.com/containers/buildah/pkg/umask github.com/containers/buildah/util -# github.com/containers/common v0.8.0 +# github.com/containers/common v0.8.1 github.com/containers/common/pkg/apparmor github.com/containers/common/pkg/capabilities github.com/containers/common/pkg/cgroupv2 @@ -411,7 +411,7 @@ github.com/opencontainers/runtime-tools/generate github.com/opencontainers/runtime-tools/generate/seccomp github.com/opencontainers/runtime-tools/specerror github.com/opencontainers/runtime-tools/validate -# github.com/opencontainers/selinux v1.4.0 +# github.com/opencontainers/selinux v1.5.0 github.com/opencontainers/selinux/go-selinux github.com/opencontainers/selinux/go-selinux/label github.com/opencontainers/selinux/pkg/pwalk |