diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/adapter/client.go | 3 | ||||
-rw-r--r-- | pkg/adapter/containers.go | 46 | ||||
-rw-r--r-- | pkg/adapter/pods.go | 14 | ||||
-rw-r--r-- | pkg/adapter/runtime.go | 26 | ||||
-rw-r--r-- | pkg/adapter/runtime_remote.go | 31 | ||||
-rw-r--r-- | pkg/namespaces/namespaces.go | 87 | ||||
-rw-r--r-- | pkg/spec/createconfig.go | 12 | ||||
-rw-r--r-- | pkg/spec/spec.go | 37 | ||||
-rw-r--r-- | pkg/spec/storage.go | 137 | ||||
-rw-r--r-- | pkg/util/utils.go | 6 | ||||
-rw-r--r-- | pkg/util/utils_supported.go | 36 | ||||
-rw-r--r-- | pkg/util/utils_windows.go | 15 | ||||
-rw-r--r-- | pkg/varlinkapi/images.go | 9 | ||||
-rw-r--r-- | pkg/varlinkapi/system.go | 1 |
14 files changed, 254 insertions, 206 deletions
diff --git a/pkg/adapter/client.go b/pkg/adapter/client.go index 694d9f961..da6ff5fd0 100644 --- a/pkg/adapter/client.go +++ b/pkg/adapter/client.go @@ -16,7 +16,7 @@ var remoteEndpoint *Endpoint func (r RemoteRuntime) RemoteEndpoint() (remoteEndpoint *Endpoint, err error) { remoteConfigConnections, err := remoteclientconfig.ReadRemoteConfig(r.config) - if errors.Cause(err) != remoteclientconfig.ErrNoConfigationFile { + if err != nil && errors.Cause(err) != remoteclientconfig.ErrNoConfigationFile { return nil, err } // If the user defines an env variable for podman_varlink_bridge @@ -68,7 +68,6 @@ func (r RemoteRuntime) Connect() (*varlink.Connection, error) { if err != nil { return nil, err } - switch ep.Type { case DirectConnection: return varlink.NewConnection(ep.Connection) diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index faaef3e60..b712bd9aa 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -9,7 +9,6 @@ import ( "io" "io/ioutil" "os" - "path/filepath" "strconv" "strings" "sync" @@ -23,6 +22,7 @@ import ( "github.com/containers/libpod/cmd/podman/shared/parse" "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/libpod/logs" "github.com/containers/libpod/pkg/adapter/shortcuts" @@ -418,14 +418,13 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode if ecode, err := ctr.Wait(); err != nil { if errors.Cause(err) == define.ErrNoSuchCtr { - // The container may have been removed - // Go looking for an exit file - ctrExitCode, err := ReadExitFile(config.TmpDir, ctr.ID()) + // Check events + event, err := r.Runtime.GetLastContainerEvent(ctr.ID(), events.Exited) if err != nil { logrus.Errorf("Cannot get exit code: %v", err) exitCode = 127 } else { - exitCode = ctrExitCode + exitCode = event.ContainerExitCode } } } else { @@ -441,31 +440,6 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode return exitCode, nil } -// ReadExitFile reads a container's exit file -func ReadExitFile(runtimeTmp, ctrID string) (int, error) { - exitFile := filepath.Join(runtimeTmp, "exits", fmt.Sprintf("%s-old", ctrID)) - - logrus.Debugf("Attempting to read container %s exit code from file %s", ctrID, exitFile) - - // Check if it exists - if _, err := os.Stat(exitFile); err != nil { - return 0, errors.Wrapf(err, "error getting exit file for container %s", ctrID) - } - - // File exists, read it in and convert to int - statusStr, err := ioutil.ReadFile(exitFile) - if err != nil { - return 0, errors.Wrapf(err, "error reading exit file for container %s", ctrID) - } - - exitCode, err := strconv.Atoi(string(statusStr)) - if err != nil { - return 0, errors.Wrapf(err, "error parsing exit code for container %s", ctrID) - } - - return exitCode, nil -} - // Ps ... func (r *LocalRuntime) Ps(c *cliconfig.PsValues, opts shared.PsOptions) ([]shared.PsContainerOutput, error) { maxWorkers := shared.Parallelize("ps") @@ -565,6 +539,7 @@ func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues) TargetFile: c.Import, Name: c.Name, IgnoreRootfs: c.IgnoreRootfs, + IgnoreStaticIP: c.IgnoreStaticIP, } filterFuncs = append(filterFuncs, func(c *libpod.Container) bool { @@ -655,18 +630,13 @@ func (r *LocalRuntime) Start(ctx context.Context, c *cliconfig.StartValues, sigP if ecode, err := ctr.Wait(); err != nil { if errors.Cause(err) == define.ErrNoSuchCtr { - // The container may have been removed - // Go looking for an exit file - rtc, err := r.GetConfig() - if err != nil { - return 0, err - } - ctrExitCode, err := ReadExitFile(rtc.TmpDir, ctr.ID()) + // Check events + event, err := r.Runtime.GetLastContainerEvent(ctr.ID(), events.Exited) if err != nil { logrus.Errorf("Cannot get exit code: %v", err) exitCode = 127 } else { - exitCode = ctrExitCode + exitCode = event.ContainerExitCode } } } else { diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go index b9d7fcd9b..e25238956 100644 --- a/pkg/adapter/pods.go +++ b/pkg/adapter/pods.go @@ -10,6 +10,7 @@ import ( "os" "strings" + "github.com/containers/buildah/pkg/parse" "github.com/containers/image/types" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" @@ -570,7 +571,7 @@ func (r *LocalRuntime) PlayKubeYAML(ctx context.Context, c *cliconfig.KubePlayVa } } - if err := createconfig.ValidateVolumeHostDir(hostPath.Path); err != nil { + if err := parse.ValidateVolumeHostDir(hostPath.Path); err != nil { return nil, errors.Wrapf(err, "Error in parsing HostPath in YAML") } volumes[volume.Name] = hostPath.Path @@ -682,6 +683,15 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container if containerYAML.SecurityContext.AllowPrivilegeEscalation != nil { containerConfig.NoNewPrivs = !*containerYAML.SecurityContext.AllowPrivilegeEscalation } + + } + if caps := containerYAML.SecurityContext.Capabilities; caps != nil { + for _, capability := range caps.Add { + containerConfig.CapAdd = append(containerConfig.CapAdd, string(capability)) + } + for _, capability := range caps.Drop { + containerConfig.CapDrop = append(containerConfig.CapDrop, string(capability)) + } } containerConfig.Command = []string{} @@ -728,7 +738,7 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container if !exists { return nil, errors.Errorf("Volume mount %s specified for container but not configured in volumes", volume.Name) } - if err := createconfig.ValidateVolumeCtrDir(volume.MountPath); err != nil { + if err := parse.ValidateVolumeCtrDir(volume.MountPath); err != nil { return nil, errors.Wrapf(err, "error in parsing MountPath") } containerConfig.Volumes = append(containerConfig.Volumes, fmt.Sprintf("%s:%s", hostPath, volume.MountPath)) diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go index ee6913cc0..4a3b41297 100644 --- a/pkg/adapter/runtime.go +++ b/pkg/adapter/runtime.go @@ -5,22 +5,22 @@ package adapter import ( "bufio" "context" - "github.com/containers/libpod/libpod/define" "io" "io/ioutil" "os" "text/template" - "github.com/containers/libpod/cmd/podman/shared" - "github.com/containers/buildah" "github.com/containers/buildah/imagebuildah" + "github.com/containers/buildah/pkg/formats" "github.com/containers/buildah/pkg/parse" "github.com/containers/image/docker/reference" "github.com/containers/image/types" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/cmd/podman/shared" "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/rootless" @@ -351,9 +351,13 @@ func (r *LocalRuntime) Events(c *cliconfig.EventValues) error { fromStart bool eventsError error ) - tmpl, err := template.New("events").Parse(c.Format) - if err != nil { - return err + var tmpl *template.Template + if c.Format != formats.JSONString { + template, err := template.New("events").Parse(c.Format) + if err != nil { + return err + } + tmpl = template } if len(c.Since) > 0 || len(c.Until) > 0 { fromStart = true @@ -369,7 +373,15 @@ func (r *LocalRuntime) Events(c *cliconfig.EventValues) error { } w := bufio.NewWriter(os.Stdout) for event := range eventChannel { - if len(c.Format) > 0 { + if c.Format == formats.JSONString { + jsonStr, err := event.ToJSONString() + if err != nil { + return errors.Wrapf(err, "unable to format json") + } + if _, err := w.Write([]byte(jsonStr)); err != nil { + return err + } + } else if len(c.Format) > 0 { if err := tmpl.Execute(w, event); err != nil { return err } diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go index 9fae39df0..0cafbb2aa 100644 --- a/pkg/adapter/runtime_remote.go +++ b/pkg/adapter/runtime_remote.go @@ -10,13 +10,13 @@ import ( "io" "io/ioutil" "os" + "path/filepath" "strings" "text/template" "time" - v1 "k8s.io/api/core/v1" - "github.com/containers/buildah/imagebuildah" + "github.com/containers/buildah/pkg/formats" "github.com/containers/image/docker/reference" "github.com/containers/image/types" "github.com/containers/libpod/cmd/podman/cliconfig" @@ -32,6 +32,7 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/varlink/go/varlink" + v1 "k8s.io/api/core/v1" ) // ImageRuntime is wrapper for image runtime @@ -68,6 +69,12 @@ func GetRuntime(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime, cmd: c.GlobalFlags, } configPath := remoteclientconfig.GetConfigFilePath() + // Check if the basedir for configPath exists and if not, create it. + if _, err := os.Stat(filepath.Dir(configPath)); os.IsNotExist(err) { + if mkdirErr := os.MkdirAll(filepath.Dir(configPath), 0750); mkdirErr != nil { + return nil, mkdirErr + } + } if len(c.GlobalFlags.RemoteConfigFilePath) > 0 { configPath = c.GlobalFlags.RemoteConfigFilePath customConfig = true @@ -820,9 +827,13 @@ func (r *LocalRuntime) Events(c *cliconfig.EventValues) error { } w := bufio.NewWriter(os.Stdout) - tmpl, err := template.New("events").Parse(c.Format) - if err != nil { - return err + var tmpl *template.Template + if c.Format != formats.JSONString { + template, err := template.New("events").Parse(c.Format) + if err != nil { + return err + } + tmpl = template } for { @@ -856,7 +867,15 @@ func (r *LocalRuntime) Events(c *cliconfig.EventValues) error { Time: eTime, Type: eType, } - if len(c.Format) > 0 { + if c.Format == formats.JSONString { + jsonStr, err := event.ToJSONString() + if err != nil { + return errors.Wrapf(err, "unable to format json") + } + if _, err := w.Write([]byte(jsonStr)); err != nil { + return err + } + } else if len(c.Format) > 0 { if err := tmpl.Execute(w, event); err != nil { return err } diff --git a/pkg/namespaces/namespaces.go b/pkg/namespaces/namespaces.go index 35298796f..9d1033b93 100644 --- a/pkg/namespaces/namespaces.go +++ b/pkg/namespaces/namespaces.go @@ -4,17 +4,30 @@ import ( "strings" ) +const ( + bridgeType = "bridge" + containerType = "container" + defaultType = "default" + hostType = "host" + noneType = "none" + nsType = "ns" + podType = "pod" + privateType = "private" + shareableType = "shareable" + slirpType = "slirp4netns" +) + // CgroupMode represents cgroup mode in the container. type CgroupMode string // IsHost indicates whether the container uses the host's cgroup. func (n CgroupMode) IsHost() bool { - return n == "host" + return n == hostType } // IsNS indicates a cgroup namespace passed in by path (ns:<path>) func (n CgroupMode) IsNS() bool { - return strings.HasPrefix(string(n), "ns:") + return strings.HasPrefix(string(n), nsType) } // NS gets the path associated with a ns:<path> cgroup ns @@ -29,13 +42,13 @@ func (n CgroupMode) NS() string { // IsContainer indicates whether the container uses a new cgroup namespace. func (n CgroupMode) IsContainer() bool { parts := strings.SplitN(string(n), ":", 2) - return len(parts) > 1 && parts[0] == "container" + return len(parts) > 1 && parts[0] == containerType } // Container returns the name of the container whose cgroup namespace is going to be used. func (n CgroupMode) Container() string { parts := strings.SplitN(string(n), ":", 2) - if len(parts) > 1 { + if len(parts) > 1 && parts[0] == containerType { return parts[1] } return "" @@ -43,15 +56,15 @@ func (n CgroupMode) Container() string { // IsPrivate indicates whether the container uses the a private cgroup. func (n CgroupMode) IsPrivate() bool { - return n == "private" + return n == privateType } // Valid indicates whether the Cgroup namespace is valid. func (n CgroupMode) Valid() bool { parts := strings.Split(string(n), ":") switch mode := parts[0]; mode { - case "", "host", "private", "ns": - case "container": + case "", hostType, privateType, nsType: + case containerType: if len(parts) != 2 || parts[1] == "" { return false } @@ -66,7 +79,7 @@ type UsernsMode string // IsHost indicates whether the container uses the host's userns. func (n UsernsMode) IsHost() bool { - return n == "host" + return n == hostType } // IsKeepID indicates whether container uses a mapping where the (uid, gid) on the host is lept inside of the namespace. @@ -83,8 +96,8 @@ func (n UsernsMode) IsPrivate() bool { func (n UsernsMode) Valid() bool { parts := strings.Split(string(n), ":") switch mode := parts[0]; mode { - case "", "host", "keep-id", "ns": - case "container": + case "", hostType, "keep-id", nsType: + case containerType: if len(parts) != 2 || parts[1] == "" { return false } @@ -111,13 +124,13 @@ func (n UsernsMode) NS() string { // IsContainer indicates whether container uses a container userns. func (n UsernsMode) IsContainer() bool { parts := strings.SplitN(string(n), ":", 2) - return len(parts) > 1 && parts[0] == "container" + return len(parts) > 1 && parts[0] == containerType } // Container is the id of the container which network this container is connected to. func (n UsernsMode) Container() string { parts := strings.SplitN(string(n), ":", 2) - if len(parts) > 1 { + if len(parts) > 1 && parts[0] == containerType { return parts[1] } return "" @@ -133,19 +146,19 @@ func (n UTSMode) IsPrivate() bool { // IsHost indicates whether the container uses the host's UTS namespace. func (n UTSMode) IsHost() bool { - return n == "host" + return n == hostType } // IsContainer indicates whether the container uses a container's UTS namespace. func (n UTSMode) IsContainer() bool { parts := strings.SplitN(string(n), ":", 2) - return len(parts) > 1 && parts[0] == "container" + return len(parts) > 1 && parts[0] == containerType } // Container returns the name of the container whose uts namespace is going to be used. func (n UTSMode) Container() string { parts := strings.SplitN(string(n), ":", 2) - if len(parts) > 1 { + if len(parts) > 1 && parts[0] == containerType { return parts[1] } return "" @@ -155,8 +168,8 @@ func (n UTSMode) Container() string { func (n UTSMode) Valid() bool { parts := strings.Split(string(n), ":") switch mode := parts[0]; mode { - case "", "host": - case "container": + case "", hostType: + case containerType: if len(parts) != 2 || parts[1] == "" { return false } @@ -171,28 +184,28 @@ type IpcMode string // IsPrivate indicates whether the container uses its own private ipc namespace which cannot be shared. func (n IpcMode) IsPrivate() bool { - return n == "private" + return n == privateType } // IsHost indicates whether the container shares the host's ipc namespace. func (n IpcMode) IsHost() bool { - return n == "host" + return n == hostType } // IsShareable indicates whether the container's ipc namespace can be shared with another container. func (n IpcMode) IsShareable() bool { - return n == "shareable" + return n == shareableType } // IsContainer indicates whether the container uses another container's ipc namespace. func (n IpcMode) IsContainer() bool { parts := strings.SplitN(string(n), ":", 2) - return len(parts) > 1 && parts[0] == "container" + return len(parts) > 1 && parts[0] == containerType } // IsNone indicates whether container IpcMode is set to "none". func (n IpcMode) IsNone() bool { - return n == "none" + return n == noneType } // IsEmpty indicates whether container IpcMode is empty @@ -208,7 +221,7 @@ func (n IpcMode) Valid() bool { // Container returns the name of the container ipc stack is going to be used. func (n IpcMode) Container() string { parts := strings.SplitN(string(n), ":", 2) - if len(parts) > 1 && parts[0] == "container" { + if len(parts) > 1 && parts[0] == containerType { return parts[1] } return "" @@ -224,21 +237,21 @@ func (n PidMode) IsPrivate() bool { // IsHost indicates whether the container uses the host's pid namespace. func (n PidMode) IsHost() bool { - return n == "host" + return n == hostType } // IsContainer indicates whether the container uses a container's pid namespace. func (n PidMode) IsContainer() bool { parts := strings.SplitN(string(n), ":", 2) - return len(parts) > 1 && parts[0] == "container" + return len(parts) > 1 && parts[0] == containerType } // Valid indicates whether the pid namespace is valid. func (n PidMode) Valid() bool { parts := strings.Split(string(n), ":") switch mode := parts[0]; mode { - case "", "host": - case "container": + case "", hostType: + case containerType: if len(parts) != 2 || parts[1] == "" { return false } @@ -251,7 +264,7 @@ func (n PidMode) Valid() bool { // Container returns the name of the container whose pid namespace is going to be used. func (n PidMode) Container() string { parts := strings.SplitN(string(n), ":", 2) - if len(parts) > 1 { + if len(parts) > 1 && parts[0] == containerType { return parts[1] } return "" @@ -262,17 +275,17 @@ type NetworkMode string // IsNone indicates whether container isn't using a network stack. func (n NetworkMode) IsNone() bool { - return n == "none" + return n == noneType } // IsHost indicates whether the container uses the host's network stack. func (n NetworkMode) IsHost() bool { - return n == "host" + return n == hostType } // IsDefault indicates whether container uses the default network stack. func (n NetworkMode) IsDefault() bool { - return n == "default" + return n == defaultType } // IsPrivate indicates whether container uses its private network stack. @@ -283,13 +296,13 @@ func (n NetworkMode) IsPrivate() bool { // IsContainer indicates whether container uses a container network stack. func (n NetworkMode) IsContainer() bool { parts := strings.SplitN(string(n), ":", 2) - return len(parts) > 1 && parts[0] == "container" + return len(parts) > 1 && parts[0] == containerType } // Container is the id of the container which network this container is connected to. func (n NetworkMode) Container() string { parts := strings.SplitN(string(n), ":", 2) - if len(parts) > 1 { + if len(parts) > 1 && parts[0] == containerType { return parts[1] } return "" @@ -305,17 +318,17 @@ func (n NetworkMode) UserDefined() string { // IsBridge indicates whether container uses the bridge network stack func (n NetworkMode) IsBridge() bool { - return n == "bridge" + return n == bridgeType } // IsSlirp4netns indicates if we are running a rootless network stack func (n NetworkMode) IsSlirp4netns() bool { - return n == "slirp4netns" + return n == slirpType } // IsNS indicates a network namespace passed in by path (ns:<path>) func (n NetworkMode) IsNS() bool { - return strings.HasPrefix(string(n), "ns:") + return strings.HasPrefix(string(n), nsType) } // NS gets the path associated with a ns:<path> network ns @@ -329,7 +342,7 @@ func (n NetworkMode) NS() string { // IsPod returns whether the network refers to pod networking func (n NetworkMode) IsPod() bool { - return n == "pod" + return n == podType } // IsUserDefined indicates user-created network diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go index 214a3c5ed..3f70e5935 100644 --- a/pkg/spec/createconfig.go +++ b/pkg/spec/createconfig.go @@ -64,8 +64,9 @@ type CreateConfig struct { CidFile string ConmonPidFile string Cgroupns string - CgroupParent string // cgroup-parent - Command []string + CgroupParent string // cgroup-parent + Command []string // Full command that will be used + UserCommand []string // User-entered command (or image CMD) Detach bool // detach Devices []string // device DNSOpt []string //dns-opt @@ -168,6 +169,9 @@ func (c *CreateConfig) createExitCommand(runtime *libpod.Runtime) ([]string, err for _, opt := range config.StorageConfig.GraphDriverOptions { command = append(command, []string{"--storage-opt", opt}...) } + if config.EventsLogger != "" { + command = append(command, []string{"--events-backend", config.EventsLogger}...) + } if c.Syslog { command = append(command, "--syslog", "true") @@ -227,8 +231,8 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l options = append(options, libpod.WithNamedVolumes(namedVolumes)) } - if len(c.Command) != 0 { - options = append(options, libpod.WithCommand(c.Command)) + if len(c.UserCommand) != 0 { + options = append(options, libpod.WithCommand(c.UserCommand)) } // Add entrypoint unconditionally diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go index c94746767..156d6849d 100644 --- a/pkg/spec/spec.go +++ b/pkg/spec/spec.go @@ -174,10 +174,20 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM } hostname := config.Hostname - if hostname == "" && (config.NetMode.IsHost() || config.UtsMode.IsHost()) { - hostname, err = os.Hostname() - if err != nil { - return nil, errors.Wrap(err, "unable to retrieve hostname") + if hostname == "" { + if utsCtrID := config.UtsMode.Container(); utsCtrID != "" { + utsCtr, err := runtime.GetContainer(utsCtrID) + if err != nil { + return nil, errors.Wrapf(err, "unable to retrieve hostname from dependency container %s", utsCtrID) + } + hostname = utsCtr.Hostname() + } else if config.NetMode.IsHost() || config.UtsMode.IsHost() { + hostname, err = os.Hostname() + if err != nil { + return nil, errors.Wrap(err, "unable to retrieve hostname of the host") + } + } else { + logrus.Debug("No hostname set; container's hostname will default to runtime default") } } g.RemoveHostname() @@ -541,8 +551,8 @@ func addPidNS(config *CreateConfig, g *generate.Generator) error { if pidMode.IsHost() { return g.RemoveLinuxNamespace(string(spec.PIDNamespace)) } - if pidMode.IsContainer() { - logrus.Debug("using container pidmode") + if pidCtr := pidMode.Container(); pidCtr != "" { + logrus.Debugf("using container %s pidmode", pidCtr) } if IsPod(string(pidMode)) { logrus.Debug("using pod pidmode") @@ -579,8 +589,8 @@ func addNetNS(config *CreateConfig, g *generate.Generator) error { } else if netMode.IsBridge() { logrus.Debug("Using bridge netmode") return nil - } else if netMode.IsContainer() { - logrus.Debug("Using container netmode") + } else if netCtr := netMode.Container(); netCtr != "" { + logrus.Debugf("using container %s netmode", netCtr) return nil } else if IsNS(string(netMode)) { logrus.Debug("Using ns netmode") @@ -606,6 +616,9 @@ func addUTSNS(config *CreateConfig, g *generate.Generator) error { if utsMode.IsHost() { return g.RemoveLinuxNamespace(string(spec.UTSNamespace)) } + if utsCtr := utsMode.Container(); utsCtr != "" { + logrus.Debugf("using container %s utsmode", utsCtr) + } return nil } @@ -617,8 +630,8 @@ func addIpcNS(config *CreateConfig, g *generate.Generator) error { if ipcMode.IsHost() { return g.RemoveLinuxNamespace(string(spec.IPCNamespace)) } - if ipcMode.IsContainer() { - logrus.Debug("Using container ipcmode") + if ipcCtr := ipcMode.Container(); ipcCtr != "" { + logrus.Debugf("Using container %s ipcmode", ipcCtr) } return nil @@ -635,8 +648,8 @@ func addCgroupNS(config *CreateConfig, g *generate.Generator) error { if cgroupMode.IsPrivate() { return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), "") } - if cgroupMode.IsContainer() { - logrus.Debug("Using container cgroup mode") + if cgCtr := cgroupMode.Container(); cgCtr != "" { + logrus.Debugf("Using container %s cgroup mode", cgCtr) } return nil } diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go index 88f1f6dc1..e0bb48a9c 100644 --- a/pkg/spec/storage.go +++ b/pkg/spec/storage.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" + "github.com/containers/buildah/pkg/parse" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/util" "github.com/containers/storage/pkg/stringid" @@ -248,8 +249,11 @@ func (config *CreateConfig) getVolumesFrom(runtime *libpod.Runtime) (map[string] finalNamedVolumes := make(map[string]*libpod.ContainerNamedVolume) for _, vol := range config.VolumesFrom { - options := []string{} - splitVol := strings.SplitN(vol, ":", 2) + var ( + options = []string{} + err error + splitVol = strings.SplitN(vol, ":", 2) + ) if len(splitVol) == 2 { if strings.Contains(splitVol[1], "Z") || strings.Contains(splitVol[1], "private") || @@ -257,12 +261,10 @@ func (config *CreateConfig) getVolumesFrom(runtime *libpod.Runtime) (map[string] strings.Contains(splitVol[1], "shared") { return nil, nil, errors.Errorf("invalid options %q, can only specify 'ro', 'rw', and 'z", splitVol[1]) } - options = strings.Split(splitVol[1], ",") - opts, err := ValidateVolumeOpts(options) - if err != nil { + + if options, err = parse.ValidateVolumeOpts(strings.Split(splitVol[1], ",")); err != nil { return nil, nil, err } - options = opts } ctr, err := runtime.LookupContainer(splitVol[0]) if err != nil { @@ -408,13 +410,42 @@ func getBindMount(args []string) (spec.Mount, error) { setSource := false setDest := false + setRORW := false for _, val := range args { kv := strings.Split(val, "=") switch kv[0] { case "bind-nonrecursive": newMount.Options = append(newMount.Options, "bind") - case "ro", "nosuid", "nodev", "noexec": + case "ro", "rw": + if setRORW { + return newMount, errors.Wrapf(optionArgError, "cannot pass 'ro' or 'rw' options more than once") + } + setRORW = true + // Can be formatted as one of: + // ro + // ro=[true|false] + // rw + // rw=[true|false] + if len(kv) == 1 { + newMount.Options = append(newMount.Options, kv[0]) + } else if len(kv) == 2 { + switch strings.ToLower(kv[1]) { + case "true": + newMount.Options = append(newMount.Options, kv[0]) + case "false": + // Set the opposite only for rw + // ro's opposite is the default + if kv[0] == "rw" { + newMount.Options = append(newMount.Options, "ro") + } + default: + return newMount, errors.Wrapf(optionArgError, "%s must be set to true or false, instead received %q", kv[0], kv[1]) + } + } else { + return newMount, errors.Wrapf(optionArgError, "badly formatted option %q", val) + } + case "nosuid", "nodev", "noexec": // TODO: detect duplication of these options. // (Is this necessary?) newMount.Options = append(newMount.Options, kv[0]) @@ -429,7 +460,7 @@ func getBindMount(args []string) (spec.Mount, error) { if len(kv) == 1 { return newMount, errors.Wrapf(optionArgError, kv[0]) } - if err := ValidateVolumeHostDir(kv[1]); err != nil { + if err := parse.ValidateVolumeHostDir(kv[1]); err != nil { return newMount, err } newMount.Source = kv[1] @@ -438,7 +469,7 @@ func getBindMount(args []string) (spec.Mount, error) { if len(kv) == 1 { return newMount, errors.Wrapf(optionArgError, kv[0]) } - if err := ValidateVolumeCtrDir(kv[1]); err != nil { + if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { return newMount, err } newMount.Destination = kv[1] @@ -456,12 +487,11 @@ func getBindMount(args []string) (spec.Mount, error) { newMount.Source = newMount.Destination } - opts, err := ValidateVolumeOpts(newMount.Options) + options, err := parse.ValidateVolumeOpts(newMount.Options) if err != nil { return newMount, err } - newMount.Options = opts - + newMount.Options = options return newMount, nil } @@ -495,7 +525,7 @@ func getTmpfsMount(args []string) (spec.Mount, error) { if len(kv) == 1 { return newMount, errors.Wrapf(optionArgError, kv[0]) } - if err := ValidateVolumeCtrDir(kv[1]); err != nil { + if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { return newMount, err } newMount.Destination = kv[1] @@ -539,7 +569,7 @@ func getNamedVolume(args []string) (*libpod.ContainerNamedVolume, error) { if len(kv) == 1 { return nil, errors.Wrapf(optionArgError, kv[0]) } - if err := ValidateVolumeCtrDir(kv[1]); err != nil { + if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { return nil, err } newVolume.Dest = kv[1] @@ -559,75 +589,6 @@ func getNamedVolume(args []string) (*libpod.ContainerNamedVolume, error) { return newVolume, nil } -// ValidateVolumeHostDir validates a volume mount's source directory -func ValidateVolumeHostDir(hostDir string) error { - if len(hostDir) == 0 { - return errors.Errorf("host directory cannot be empty") - } - if filepath.IsAbs(hostDir) { - if _, err := os.Stat(hostDir); err != nil { - return errors.Wrapf(err, "error checking path %q", hostDir) - } - } - // If hostDir is not an absolute path, that means the user wants to create a - // named volume. This will be done later on in the code. - return nil -} - -// ValidateVolumeCtrDir validates a volume mount's destination directory. -func ValidateVolumeCtrDir(ctrDir string) error { - if len(ctrDir) == 0 { - return errors.Errorf("container directory cannot be empty") - } - if !filepath.IsAbs(ctrDir) { - return errors.Errorf("invalid container path %q, must be an absolute path", ctrDir) - } - return nil -} - -// ValidateVolumeOpts validates a volume's options -func ValidateVolumeOpts(options []string) ([]string, error) { - var foundRootPropagation, foundRWRO, foundLabelChange, bindType int - finalOpts := make([]string, 0, len(options)) - for _, opt := range options { - switch opt { - case "rw", "ro": - foundRWRO++ - if foundRWRO > 1 { - return nil, errors.Errorf("invalid options %q, can only specify 1 'rw' or 'ro' option", strings.Join(options, ", ")) - } - case "z", "Z": - foundLabelChange++ - if foundLabelChange > 1 { - return nil, errors.Errorf("invalid options %q, can only specify 1 'z' or 'Z' option", strings.Join(options, ", ")) - } - case "private", "rprivate", "shared", "rshared", "slave", "rslave": - foundRootPropagation++ - if foundRootPropagation > 1 { - return nil, errors.Errorf("invalid options %q, can only specify 1 '[r]shared', '[r]private' or '[r]slave' option", strings.Join(options, ", ")) - } - case "bind", "rbind": - bindType++ - if bindType > 1 { - return nil, errors.Errorf("invalid options %q, can only specify 1 '[r]bind' option", strings.Join(options, ", ")) - } - case "cached", "delegated": - // The discarded ops are OS X specific volume options - // introduced in a recent Docker version. - // They have no meaning on Linux, so here we silently - // drop them. This matches Docker's behavior (the options - // are intended to be always safe to use, even not on OS - // X). - continue - default: - return nil, errors.Errorf("invalid mount option %q", opt) - } - finalOpts = append(finalOpts, opt) - } - return finalOpts, nil -} - -// GetVolumeMounts takes user provided input for bind mounts and creates Mount structs func (config *CreateConfig) getVolumeMounts() (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) { mounts := make(map[string]spec.Mount) volumes := make(map[string]*libpod.ContainerNamedVolume) @@ -639,6 +600,7 @@ func (config *CreateConfig) getVolumeMounts() (map[string]spec.Mount, map[string options []string src string dest string + err error ) splitVol := strings.Split(vol, ":") @@ -653,18 +615,15 @@ func (config *CreateConfig) getVolumeMounts() (map[string]spec.Mount, map[string dest = splitVol[1] } if len(splitVol) > 2 { - options = strings.Split(splitVol[2], ",") - opts, err := ValidateVolumeOpts(options) - if err != nil { + if options, err = parse.ValidateVolumeOpts(strings.Split(splitVol[2], ",")); err != nil { return nil, nil, err } - options = opts } - if err := ValidateVolumeHostDir(src); err != nil { + if err := parse.ValidateVolumeHostDir(src); err != nil { return nil, nil, err } - if err := ValidateVolumeCtrDir(dest); err != nil { + if err := parse.ValidateVolumeCtrDir(dest); err != nil { return nil, nil, err } diff --git a/pkg/util/utils.go b/pkg/util/utils.go index fba34a337..520e41438 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -239,8 +239,10 @@ func ParseIDMapping(mode namespaces.UsernsMode, UIDMapSlice, GIDMapSlice []strin } var ( - rootlessRuntimeDirOnce sync.Once - rootlessRuntimeDir string + rootlessConfigHomeDirOnce sync.Once + rootlessConfigHomeDir string + rootlessRuntimeDirOnce sync.Once + rootlessRuntimeDir string ) type tomlOptionsConfig struct { diff --git a/pkg/util/utils_supported.go b/pkg/util/utils_supported.go index af55689a6..c7c8787a0 100644 --- a/pkg/util/utils_supported.go +++ b/pkg/util/utils_supported.go @@ -26,7 +26,7 @@ func GetRootlessRuntimeDir() (string, error) { if runtimeDir == "" { tmpDir := filepath.Join("/run", "user", uid) if err := os.MkdirAll(tmpDir, 0700); err != nil { - logrus.Errorf("unable to make temp dir %s", tmpDir) + logrus.Debugf("unable to make temp dir %s", tmpDir) } st, err := os.Stat(tmpDir) if err == nil && int(st.Sys().(*syscall.Stat_t).Uid) == os.Geteuid() && st.Mode().Perm() == 0700 { @@ -36,7 +36,7 @@ func GetRootlessRuntimeDir() (string, error) { if runtimeDir == "" { tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("run-%s", uid)) if err := os.MkdirAll(tmpDir, 0700); err != nil { - logrus.Errorf("unable to make temp dir %s", tmpDir) + logrus.Debugf("unable to make temp dir %s", tmpDir) } st, err := os.Stat(tmpDir) if err == nil && int(st.Sys().(*syscall.Stat_t).Uid) == os.Geteuid() && st.Mode().Perm() == 0700 { @@ -65,6 +65,38 @@ func GetRootlessRuntimeDir() (string, error) { return rootlessRuntimeDir, nil } +// GetRootlessConfigHomeDir returns the config home directory when running as non root +func GetRootlessConfigHomeDir() (string, error) { + var rootlessConfigHomeDirError error + + rootlessConfigHomeDirOnce.Do(func() { + cfgHomeDir := os.Getenv("XDG_CONFIG_HOME") + if cfgHomeDir == "" { + home := os.Getenv("HOME") + resolvedHome, err := filepath.EvalSymlinks(home) + if err != nil { + rootlessConfigHomeDirError = errors.Wrapf(err, "cannot resolve %s", home) + return + } + tmpDir := filepath.Join(resolvedHome, ".config") + if err := os.MkdirAll(tmpDir, 0755); err != nil { + logrus.Errorf("unable to make temp dir %s", tmpDir) + } + st, err := os.Stat(tmpDir) + if err == nil && int(st.Sys().(*syscall.Stat_t).Uid) == os.Geteuid() && st.Mode().Perm() == 0755 { + cfgHomeDir = tmpDir + } + } + rootlessConfigHomeDir = cfgHomeDir + }) + + if rootlessConfigHomeDirError != nil { + return "", rootlessConfigHomeDirError + } + + return rootlessConfigHomeDir, nil +} + // GetRootlessPauseProcessPidPath returns the path to the file that holds the pid for // the pause process func GetRootlessPauseProcessPidPath() (string, error) { diff --git a/pkg/util/utils_windows.go b/pkg/util/utils_windows.go index 635558bf7..e781e6717 100644 --- a/pkg/util/utils_windows.go +++ b/pkg/util/utils_windows.go @@ -6,24 +6,31 @@ import ( "github.com/pkg/errors" ) +var errNotImplemented = errors.New("not yet implemented") + // IsCgroup2UnifiedMode returns whether we are running in cgroup 2 unified mode. func IsCgroup2UnifiedMode() (bool, error) { - return false, errors.New("this function is not implemented for windows") + return false, errors.Wrap(errNotImplemented, "IsCgroup2Unified") } // GetContainerPidInformationDescriptors returns a string slice of all supported // format descriptors of GetContainerPidInformation. func GetContainerPidInformationDescriptors() ([]string, error) { - return nil, errors.New("this function is not implemented for windows") + return nil, errors.Wrap(errNotImplemented, "GetContainerPidInformationDescriptors") } // GetRootlessPauseProcessPidPath returns the path to the file that holds the pid for // the pause process func GetRootlessPauseProcessPidPath() (string, error) { - return "", errors.New("this function is not implemented for windows") + return "", errors.Wrap(errNotImplemented, "GetRootlessPauseProcessPidPath") } // GetRootlessRuntimeDir returns the runtime directory when running as non root func GetRootlessRuntimeDir() (string, error) { - return "", errors.New("this function is not implemented for windows") + return "", errors.Wrap(errNotImplemented, "GetRootlessRuntimeDir") +} + +// GetRootlessConfigHomeDir returns the config home directory when running as non root +func GetRootlessConfigHomeDir() (string, error) { + return "", errors.Wrap(errNotImplemented, "GetRootlessConfigHomeDir") } diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go index 338499bd4..b5a711dfd 100644 --- a/pkg/varlinkapi/images.go +++ b/pkg/varlinkapi/images.go @@ -198,6 +198,8 @@ func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildI if call.WantsMore() { call.Continues = true + } else { + return call.ReplyErrorOccurred("endpoint requires a more connection") } var newPathDockerFiles []string @@ -642,6 +644,7 @@ func (i *LibpodAPI) PullImage(call iopodman.VarlinkCall, name string) error { defer close(c) go func() { + var foundError bool if strings.HasPrefix(name, dockerarchive.Transport.Name()+":") { srcRef, err := alltransports.ParseImageName(name) if err != nil { @@ -649,6 +652,7 @@ func (i *LibpodAPI) PullImage(call iopodman.VarlinkCall, name string) error { } newImage, err := i.Runtime.ImageRuntime().LoadFromArchiveReference(getContext(), srcRef, "", output) if err != nil { + foundError = true c <- errors.Wrapf(err, "error pulling image from %q", name) } else { imageID = newImage[0].ID() @@ -656,12 +660,15 @@ func (i *LibpodAPI) PullImage(call iopodman.VarlinkCall, name string) error { } else { newImage, err := i.Runtime.ImageRuntime().New(getContext(), name, "", "", output, &dockerRegistryOptions, so, false, nil) if err != nil { + foundError = true c <- errors.Wrapf(err, "unable to pull %s", name) } else { imageID = newImage.ID() } } - c <- nil + if !foundError { + c <- nil + } }() var log []string diff --git a/pkg/varlinkapi/system.go b/pkg/varlinkapi/system.go index 9b5b3a5b1..2de785b79 100644 --- a/pkg/varlinkapi/system.go +++ b/pkg/varlinkapi/system.go @@ -61,6 +61,7 @@ func (i *LibpodAPI) GetInfo(call iopodman.VarlinkCall) error { Kernel: host["kernel"].(string), Os: host["os"].(string), Uptime: host["uptime"].(string), + Eventlogger: host["eventlogger"].(string), } podmanInfo.Host = infoHost store := info[1].Data |