diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container_api.go | 57 | ||||
-rw-r--r-- | libpod/container_internal.go | 22 | ||||
-rw-r--r-- | libpod/container_validate.go | 99 | ||||
-rw-r--r-- | libpod/define/exec_codes.go | 6 | ||||
-rw-r--r-- | libpod/oci.go | 9 | ||||
-rw-r--r-- | libpod/oci_attach_linux.go | 4 | ||||
-rw-r--r-- | libpod/oci_conmon_linux.go | 84 | ||||
-rw-r--r-- | libpod/oci_missing.go | 4 | ||||
-rw-r--r-- | libpod/options.go | 87 | ||||
-rw-r--r-- | libpod/pod.go | 1 | ||||
-rw-r--r-- | libpod/pod_api.go | 12 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 28 |
12 files changed, 240 insertions, 173 deletions
diff --git a/libpod/container_api.go b/libpod/container_api.go index dabbe27dc..dc7470f1a 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -9,9 +9,9 @@ import ( "os" "time" + "github.com/containers/common/pkg/capabilities" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/events" - "github.com/containers/libpod/pkg/capabilities" "github.com/containers/storage/pkg/stringid" "github.com/opentracing/opentracing-go" "github.com/pkg/errors" @@ -282,13 +282,24 @@ func (c *Container) Exec(tty, privileged bool, env map[string]string, cmd []stri opts.Resize = resize opts.DetachKeys = detachKeys - pid, attachChan, err := c.ociRuntime.ExecContainer(c, sessionID, opts) + pid := 0 + pipeDataChan, attachChan, err := c.ociRuntime.ExecContainer(c, sessionID, opts) + // if pipeDataChan isn't nil, we should set the err + if pipeDataChan != nil { + pidData := <-pipeDataChan + if pidData.err != nil { + err = pidData.err + } + pid = pidData.data + } if err != nil { ec := define.ExecErrorCodeGeneric // Conmon will pass a non-zero exit code from the runtime as a pid here. // we differentiate a pid with an exit code by sending it as negative, so reverse // that change and return the exit code the runtime failed with. - if pid < 0 { + // Make sure the value is not ErrorConmonRead, as that is a podman set bogus value + // and not sent by conmon (and thus has no special meaning) + if pid < 0 && pid != define.ErrorConmonRead { ec = -1 * pid } return ec, err @@ -318,18 +329,24 @@ func (c *Container) Exec(tty, privileged bool, env map[string]string, cmd []stri lastErr := <-attachChan - exitCode, err := c.readExecExitCode(sessionID) - if err != nil { + exitCodeData := <-pipeDataChan + if exitCodeData.err != nil { if lastErr != nil { logrus.Errorf(lastErr.Error()) } - lastErr = err + lastErr = exitCodeData.err } - if exitCode != 0 { + if exitCodeData.data != 0 { if lastErr != nil { logrus.Errorf(lastErr.Error()) } - lastErr = errors.Wrapf(define.ErrOCIRuntime, "non zero exit code: %d", exitCode) + // ErrorConmonRead is a bogus value set by podman to indicate reading a value from + // conmon failed. Since it is specifically not a valid exit code, we should set + // a generic error here + if exitCodeData.data == define.ErrorConmonRead { + exitCodeData.data = define.ExecErrorCodeGeneric + } + lastErr = errors.Wrapf(define.ErrOCIRuntime, "non zero exit code: %d", exitCodeData.data) } // Lock again @@ -340,7 +357,7 @@ func (c *Container) Exec(tty, privileged bool, env map[string]string, cmd []stri // Sync the container again to pick up changes in state if err := c.syncContainer(); err != nil { logrus.Errorf("error syncing container %s state to remove exec session %s", c.ID(), sessionID) - return exitCode, lastErr + return exitCodeData.data, lastErr } // Remove the exec session from state @@ -348,7 +365,7 @@ func (c *Container) Exec(tty, privileged bool, env map[string]string, cmd []stri if err := c.save(); err != nil { logrus.Errorf("Error removing exec session %s from container %s state: %v", sessionID, c.ID(), err) } - return exitCode, lastErr + return exitCodeData.data, lastErr } // AttachStreams contains streams that will be attached to the container @@ -624,6 +641,26 @@ func (c *Container) WaitWithInterval(waitTimeout time.Duration) (int32, error) { } } +func (c *Container) WaitForConditionWithInterval(waitTimeout time.Duration, condition define.ContainerStatus) (int32, error) { + if !c.valid { + return -1, define.ErrCtrRemoved + } + if condition == define.ContainerStateStopped || condition == define.ContainerStateExited { + return c.WaitWithInterval(waitTimeout) + } + for { + state, err := c.State() + if err != nil { + return -1, err + } + if state == condition { + break + } + time.Sleep(waitTimeout) + } + return -1, nil +} + // Cleanup unmounts all mount points in container and cleans up container storage // It also cleans up the network stack func (c *Container) Cleanup(ctx context.Context) error { diff --git a/libpod/container_internal.go b/libpod/container_internal.go index ff43bfc8f..67e02cc31 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -206,28 +206,6 @@ func (c *Container) execOCILog(sessionID string) string { return filepath.Join(c.execBundlePath(sessionID), "oci-log") } -// readExecExitCode reads the exit file for an exec session and returns -// the exit code -func (c *Container) readExecExitCode(sessionID string) (int, error) { - exitFile := filepath.Join(c.execExitFileDir(sessionID), c.ID()) - chWait := make(chan error) - defer close(chWait) - - _, err := WaitForFile(exitFile, chWait, time.Second*5) - if err != nil { - return -1, err - } - ec, err := ioutil.ReadFile(exitFile) - if err != nil { - return -1, err - } - ecInt, err := strconv.Atoi(string(ec)) - if err != nil { - return -1, err - } - return ecInt, nil -} - // Wait for the container's exit file to appear. // When it does, update our state based on it. func (c *Container) waitForExitFileAndSync() error { diff --git a/libpod/container_validate.go b/libpod/container_validate.go new file mode 100644 index 000000000..b7f0aadff --- /dev/null +++ b/libpod/container_validate.go @@ -0,0 +1,99 @@ +package libpod + +import ( + "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/pkg/rootless" + spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" +) + +// Validate that the configuration of a container is valid. +func (c *Container) validate() error { + imageIDSet := c.config.RootfsImageID != "" + imageNameSet := c.config.RootfsImageName != "" + rootfsSet := c.config.Rootfs != "" + + // If one of RootfsImageIDor RootfsImageName are set, both must be set. + if (imageIDSet || imageNameSet) && !(imageIDSet && imageNameSet) { + return errors.Wrapf(define.ErrInvalidArg, "both RootfsImageName and RootfsImageID must be set if either is set") + } + + // Cannot set RootfsImageID and Rootfs at the same time + if imageIDSet && rootfsSet { + return errors.Wrapf(define.ErrInvalidArg, "cannot set both an image ID and rootfs for a container") + } + + // Must set at least one of RootfsImageID or Rootfs + if !(imageIDSet || rootfsSet) { + return errors.Wrapf(define.ErrInvalidArg, "must set root filesystem source to either image or rootfs") + } + + // Cannot make a network namespace if we are joining another container's + // network namespace + if c.config.CreateNetNS && c.config.NetNsCtr != "" { + return errors.Wrapf(define.ErrInvalidArg, "cannot both create a network namespace and join another container's network namespace") + } + + // Not creating cgroups has a number of requirements, mostly related to + // the PID namespace. + if c.config.NoCgroups || c.config.CgroupsMode == "disabled" { + if c.config.PIDNsCtr != "" { + return errors.Wrapf(define.ErrInvalidArg, "cannot join another container's PID namespace if not creating cgroups") + } + + if c.config.CgroupParent != "" { + return errors.Wrapf(define.ErrInvalidArg, "cannot set cgroup parent if not creating cgroups") + } + + // Ensure we have a PID namespace + if c.config.Spec.Linux == nil { + return errors.Wrapf(define.ErrInvalidArg, "must provide Linux namespace configuration in OCI spec when using NoCgroups") + } + foundPid := false + for _, ns := range c.config.Spec.Linux.Namespaces { + if ns.Type == spec.PIDNamespace { + foundPid = true + if ns.Path != "" { + return errors.Wrapf(define.ErrInvalidArg, "containers not creating CGroups must create a private PID namespace - cannot use another") + } + break + } + } + if !foundPid { + return errors.Wrapf(define.ErrInvalidArg, "containers not creating CGroups must create a private PID namespace") + } + } + + // Rootless has some requirements, compared to networks. + if rootless.IsRootless() { + if len(c.config.Networks) > 0 { + return errors.Wrapf(define.ErrInvalidArg, "cannot join CNI networks if running rootless") + } + + // TODO: Should we make sure network mode is set to Slirp if set + // at all? + } + + // Can only set static IP or MAC is creating a network namespace. + if !c.config.CreateNetNS && (c.config.StaticIP != nil || c.config.StaticMAC != nil) { + return errors.Wrapf(define.ErrInvalidArg, "cannot set static IP or MAC address if not creating a network namespace") + } + + // Cannot set static IP or MAC if joining >1 CNI network. + if len(c.config.Networks) > 1 && (c.config.StaticIP != nil || c.config.StaticMAC != nil) { + return errors.Wrapf(define.ErrInvalidArg, "cannot set static IP or MAC address if joining more than one CNI network") + } + + // Using image resolv.conf conflicts with various DNS settings. + if c.config.UseImageResolvConf && + (len(c.config.DNSSearch) > 0 || len(c.config.DNSServer) > 0 || + len(c.config.DNSOption) > 0) { + return errors.Wrapf(define.ErrInvalidArg, "cannot configure DNS options if using image's resolv.conf") + } + + if c.config.UseImageHosts && len(c.config.HostAdd) > 0 { + return errors.Wrapf(define.ErrInvalidArg, "cannot add to /etc/hosts if using image's /etc/hosts") + } + + return nil +} diff --git a/libpod/define/exec_codes.go b/libpod/define/exec_codes.go index f94616b33..c2ec08666 100644 --- a/libpod/define/exec_codes.go +++ b/libpod/define/exec_codes.go @@ -1,6 +1,7 @@ package define import ( + "math" "strings" "github.com/pkg/errors" @@ -17,6 +18,11 @@ const ( ExecErrorCodeCannotInvoke = 126 // ExecErrorCodeNotFound is the error code to return when a command cannot be found ExecErrorCodeNotFound = 127 + // ErrorConmonRead is a bogus value that can neither be a valid PID or exit code. It is + // used because conmon will send a negative value when sending a PID back over a pipe FD + // to signify something went wrong in the runtime. We need to differentiate between that + // value and a failure on the podman side of reading that value. Thus, we use ErrorConmonRead + ErrorConmonRead = math.MinInt32 - 1 ) // TranslateExecErrorToExitCode takes an error and checks whether it diff --git a/libpod/oci.go b/libpod/oci.go index 2ea61851f..e5f9b2135 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -70,7 +70,7 @@ type OCIRuntime interface { // ExecContainer executes a command in a running container. // Returns an int (exit code), error channel (errors from attach), and // error (errors that occurred attempting to start the exec session). - ExecContainer(ctr *Container, sessionID string, options *ExecOptions) (int, chan error, error) + ExecContainer(ctr *Container, sessionID string, options *ExecOptions) (chan DataAndErr, chan error, error) // ExecStopContainer stops a given exec session in a running container. // SIGTERM with be sent initially, then SIGKILL after the given timeout. // If timeout is 0, SIGKILL will be sent immediately, and SIGTERM will @@ -159,3 +159,10 @@ type HTTPAttachStreams struct { Stdout bool Stderr bool } + +// DataAndErr is a generic structure for passing around an int and an error +// it is especially useful for getting information from conmon +type DataAndErr struct { + data int + err error +} diff --git a/libpod/oci_attach_linux.go b/libpod/oci_attach_linux.go index 46c70e7eb..5a8198d05 100644 --- a/libpod/oci_attach_linux.go +++ b/libpod/oci_attach_linux.go @@ -119,8 +119,8 @@ func (c *Container) attachToExec(streams *AttachStreams, keys string, resize <-c socketPath := buildSocketPath(sockPath) // 2: read from attachFd that the parent process has set up the console socket - if _, err := readConmonPipeData(attachFd, ""); err != nil { - return err + if pipeData := readConmonPipeData(attachFd, ""); pipeData.err != nil { + return pipeData.err } // 2: then attach diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index 800f89603..f260e3a39 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -595,31 +595,29 @@ func (r *ConmonOCIRuntime) AttachResize(ctr *Container, newSize remotecommand.Te // ExecContainer executes a command in a running container // TODO: Split into Create/Start/Attach/Wait -func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options *ExecOptions) (int, chan error, error) { +func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options *ExecOptions) (chan DataAndErr, chan error, error) { if options == nil { - return -1, nil, errors.Wrapf(define.ErrInvalidArg, "must provide an ExecOptions struct to ExecContainer") + return nil, nil, errors.Wrapf(define.ErrInvalidArg, "must provide an ExecOptions struct to ExecContainer") } if len(options.Cmd) == 0 { - return -1, nil, errors.Wrapf(define.ErrInvalidArg, "must provide a command to execute") + return nil, nil, errors.Wrapf(define.ErrInvalidArg, "must provide a command to execute") } if sessionID == "" { - return -1, nil, errors.Wrapf(define.ErrEmptyID, "must provide a session ID for exec") + return nil, nil, errors.Wrapf(define.ErrEmptyID, "must provide a session ID for exec") } // create sync pipe to receive the pid parentSyncPipe, childSyncPipe, err := newPipe() if err != nil { - return -1, nil, errors.Wrapf(err, "error creating socket pair") + return nil, nil, errors.Wrapf(err, "error creating socket pair") } - defer errorhandling.CloseQuiet(parentSyncPipe) - // create start pipe to set the cgroup before running // attachToExec is responsible for closing parentStartPipe childStartPipe, parentStartPipe, err := newPipe() if err != nil { - return -1, nil, errors.Wrapf(err, "error creating socket pair") + return nil, nil, errors.Wrapf(err, "error creating socket pair") } // We want to make sure we close the parent{Start,Attach}Pipes if we fail @@ -638,7 +636,7 @@ func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options // attachToExec is responsible for closing parentAttachPipe parentAttachPipe, childAttachPipe, err := newPipe() if err != nil { - return -1, nil, errors.Wrapf(err, "error creating socket pair") + return nil, nil, errors.Wrapf(err, "error creating socket pair") } defer func() { @@ -658,7 +656,7 @@ func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options runtimeDir, err := util.GetRuntimeDir() if err != nil { - return -1, nil, err + return nil, nil, err } finalEnv := make([]string, 0, len(options.Env)) @@ -668,7 +666,7 @@ func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options processFile, err := prepareProcessExec(c, options.Cmd, finalEnv, options.Terminal, options.Cwd, options.User, sessionID) if err != nil { - return -1, nil, err + return nil, nil, err } var ociLog string @@ -717,7 +715,7 @@ func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options conmonEnv, extraFiles, err := r.configureConmonEnv(runtimeDir) if err != nil { - return -1, nil, err + return nil, nil, err } if options.PreserveFDs > 0 { @@ -748,10 +746,10 @@ func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options childrenClosed = true if err != nil { - return -1, nil, errors.Wrapf(err, "cannot start container %s", c.ID()) + return nil, nil, errors.Wrapf(err, "cannot start container %s", c.ID()) } if err := r.moveConmonToCgroupAndSignal(c, execCmd, parentStartPipe); err != nil { - return -1, nil, err + return nil, nil, err } if options.PreserveFDs > 0 { @@ -774,9 +772,16 @@ func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options }() attachToExecCalled = true - pid, err := readConmonPipeData(parentSyncPipe, ociLog) + dataChan := make(chan DataAndErr) + go func() { + // read the exec pid + dataChan <- readConmonPipeData(parentSyncPipe, ociLog) + // read the exec exit code + dataChan <- readConmonPipeData(parentSyncPipe, ociLog) + errorhandling.CloseQuiet(parentSyncPipe) + }() - return pid, attachChan, err + return dataChan, attachChan, err } // ExecStopContainer stops a given exec session in a running container. @@ -1206,14 +1211,14 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co return err } - pid, err := readConmonPipeData(parentSyncPipe, ociLog) - if err != nil { + pipeData := readConmonPipeData(parentSyncPipe, ociLog) + if pipeData.err != nil { if err2 := r.DeleteContainer(ctr); err2 != nil { logrus.Errorf("Error removing container %s from runtime after creation failed", ctr.ID()) } - return err + return pipeData.err } - ctr.state.PID = pid + ctr.state.PID = pipeData.data conmonPID, err := readConmonPidFile(ctr.config.ConmonPidFile) if err != nil { @@ -1525,7 +1530,7 @@ func readConmonPidFile(pidFile string) (int, error) { } // readConmonPipeData attempts to read a syncInfo struct from the pipe -func readConmonPipeData(pipe *os.File, ociLog string) (int, error) { +func readConmonPipeData(pipe *os.File, ociLog string) DataAndErr { // syncInfo is used to return data from monitor process to daemon type syncInfo struct { Data int `json:"data"` @@ -1552,7 +1557,7 @@ func readConmonPipeData(pipe *os.File, ociLog string) (int, error) { ch <- syncStruct{si: si} }() - data := -1 + data := define.ErrorConmonRead select { case ss := <-ch: if ss.err != nil { @@ -1561,11 +1566,17 @@ func readConmonPipeData(pipe *os.File, ociLog string) (int, error) { if err == nil { var ociErr ociError if err := json.Unmarshal(ociLogData, &ociErr); err == nil { - return -1, getOCIRuntimeError(ociErr.Msg) + return DataAndErr{ + data: data, + err: getOCIRuntimeError(ociErr.Msg), + } } } } - return -1, errors.Wrapf(ss.err, "container create failed (no logs from conmon)") + return DataAndErr{ + data: data, + err: errors.Wrapf(ss.err, "container create failed (no logs from conmon)"), + } } logrus.Debugf("Received: %d", ss.si.Data) if ss.si.Data < 0 { @@ -1574,21 +1585,36 @@ func readConmonPipeData(pipe *os.File, ociLog string) (int, error) { if err == nil { var ociErr ociError if err := json.Unmarshal(ociLogData, &ociErr); err == nil { - return ss.si.Data, getOCIRuntimeError(ociErr.Msg) + return DataAndErr{ + data: ss.si.Data, + err: getOCIRuntimeError(ociErr.Msg), + } } } } // If we failed to parse the JSON errors, then print the output as it is if ss.si.Message != "" { - return ss.si.Data, getOCIRuntimeError(ss.si.Message) + return DataAndErr{ + data: ss.si.Data, + err: getOCIRuntimeError(ss.si.Message), + } + } + return DataAndErr{ + data: ss.si.Data, + err: errors.Wrapf(define.ErrInternal, "container create failed"), } - return ss.si.Data, errors.Wrapf(define.ErrInternal, "container create failed") } data = ss.si.Data case <-time.After(define.ContainerCreateTimeout): - return -1, errors.Wrapf(define.ErrInternal, "container creation timeout") + return DataAndErr{ + data: data, + err: errors.Wrapf(define.ErrInternal, "container creation timeout"), + } + } + return DataAndErr{ + data: data, + err: nil, } - return data, nil } // writeConmonPipeData writes nonse data to a pipe diff --git a/libpod/oci_missing.go b/libpod/oci_missing.go index ff7eea625..1b7c1979d 100644 --- a/libpod/oci_missing.go +++ b/libpod/oci_missing.go @@ -121,8 +121,8 @@ func (r *MissingRuntime) AttachResize(ctr *Container, newSize remotecommand.Term } // ExecContainer is not available as the runtime is missing -func (r *MissingRuntime) ExecContainer(ctr *Container, sessionID string, options *ExecOptions) (int, chan error, error) { - return -1, nil, r.printError() +func (r *MissingRuntime) ExecContainer(ctr *Container, sessionID string, options *ExecOptions) (chan DataAndErr, chan error, error) { + return nil, nil, r.printError() } // ExecStopContainer is not available as the runtime is missing. diff --git a/libpod/options.go b/libpod/options.go index d01e8a85f..98de71af2 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -599,13 +599,6 @@ func WithRootFSFromImage(imageID string, imageName string) CtrCreateOption { return define.ErrCtrFinalized } - if ctr.config.RootfsImageID != "" || ctr.config.RootfsImageName != "" { - return errors.Wrapf(define.ErrInvalidArg, "container already configured with root filesystem") - } - if ctr.config.Rootfs != "" { - return errors.Wrapf(define.ErrInvalidArg, "cannot set both an image ID and a rootfs for a container") - } - ctr.config.RootfsImageID = imageID ctr.config.RootfsImageName = imageName @@ -815,10 +808,6 @@ func WithNetNSFrom(nsCtr *Container) CtrCreateOption { return err } - if ctr.config.CreateNetNS { - return errors.Wrapf(define.ErrInvalidArg, "cannot join another container's net ns as we are making a new net ns") - } - ctr.config.NetNsCtr = nsCtr.ID() return nil @@ -839,10 +828,6 @@ func WithPIDNSFrom(nsCtr *Container) CtrCreateOption { return err } - if ctr.config.NoCgroups { - return errors.Wrapf(define.ErrInvalidArg, "container has disabled creation of CGroups, which is incompatible with sharing a PID namespace") - } - ctr.config.PIDNsCtr = nsCtr.ID() return nil @@ -921,16 +906,8 @@ func WithDependencyCtrs(ctrs []*Container) CtrCreateOption { deps := make([]string, 0, len(ctrs)) for _, dep := range ctrs { - if !dep.valid { - return errors.Wrapf(define.ErrCtrRemoved, "container %s is not valid", dep.ID()) - } - - if dep.ID() == ctr.ID() { - return errors.Wrapf(define.ErrInvalidArg, "must specify another container") - } - - if ctr.config.Pod != "" && dep.config.Pod != ctr.config.Pod { - return errors.Wrapf(define.ErrInvalidArg, "container has joined pod %s and dependency container %s is not a member of the pod", ctr.config.Pod, dep.ID()) + if err := checkDependencyContainer(dep, ctr); err != nil { + return err } deps = append(deps, dep.ID()) @@ -952,20 +929,6 @@ func WithNetNS(portMappings []ocicni.PortMapping, postConfigureNetNS bool, netmo return define.ErrCtrFinalized } - if rootless.IsRootless() { - if len(networks) > 0 { - return errors.Wrapf(define.ErrInvalidArg, "cannot use CNI networks with rootless containers") - } - } - - if len(networks) > 1 && (ctr.config.StaticIP != nil || ctr.config.StaticMAC != nil) { - return errors.Wrapf(define.ErrInvalidArg, "cannot join more than one CNI network if configuring a static IP or MAC address") - } - - if ctr.config.NetNsCtr != "" { - return errors.Wrapf(define.ErrInvalidArg, "container is already set to join another container's net ns, cannot create a new net ns") - } - ctr.config.PostConfigureNetNS = postConfigureNetNS ctr.config.NetMode = namespaces.NetworkMode(netmode) ctr.config.CreateNetNS = true @@ -988,14 +951,6 @@ func WithStaticIP(ip net.IP) CtrCreateOption { return define.ErrCtrFinalized } - if !ctr.config.CreateNetNS { - return errors.Wrapf(define.ErrInvalidArg, "cannot set a static IP if the container is not creating a network namespace") - } - - if len(ctr.config.Networks) > 1 { - return errors.Wrapf(define.ErrInvalidArg, "cannot set a static IP if joining more than 1 CNI network") - } - ctr.config.StaticIP = ip return nil @@ -1013,14 +968,6 @@ func WithStaticMAC(mac net.HardwareAddr) CtrCreateOption { return define.ErrCtrFinalized } - if !ctr.config.CreateNetNS { - return errors.Wrapf(define.ErrInvalidArg, "cannot set a static MAC if the container is not creating a network namespace") - } - - if len(ctr.config.Networks) > 1 { - return errors.Wrapf(define.ErrInvalidArg, "cannot set a static MAC if joining more than 1 CNI network") - } - ctr.config.StaticMAC = mac return nil @@ -1114,10 +1061,6 @@ func WithCgroupParent(parent string) CtrCreateOption { return errors.Wrapf(define.ErrInvalidArg, "cgroup parent cannot be empty") } - if ctr.config.NoCgroups { - return errors.Wrapf(define.ErrInvalidArg, "CgroupParent conflicts with NoCgroups") - } - ctr.config.CgroupParent = parent return nil @@ -1130,9 +1073,6 @@ func WithDNSSearch(searchDomains []string) CtrCreateOption { if ctr.valid { return define.ErrCtrFinalized } - if ctr.config.UseImageResolvConf { - return errors.Wrapf(define.ErrInvalidArg, "cannot add DNS search domains if container will not create /etc/resolv.conf") - } ctr.config.DNSSearch = searchDomains return nil } @@ -1144,9 +1084,6 @@ func WithDNS(dnsServers []string) CtrCreateOption { if ctr.valid { return define.ErrCtrFinalized } - if ctr.config.UseImageResolvConf { - return errors.Wrapf(define.ErrInvalidArg, "cannot add DNS servers if container will not create /etc/resolv.conf") - } var dns []net.IP for _, i := range dnsServers { result := net.ParseIP(i) @@ -1166,9 +1103,6 @@ func WithDNSOption(dnsOptions []string) CtrCreateOption { if ctr.valid { return define.ErrCtrFinalized } - if ctr.config.UseImageResolvConf { - return errors.Wrapf(define.ErrInvalidArg, "cannot add DNS options if container will not create /etc/resolv.conf") - } ctr.config.DNSOption = dnsOptions return nil } @@ -1181,10 +1115,6 @@ func WithHosts(hosts []string) CtrCreateOption { return define.ErrCtrFinalized } - if ctr.config.UseImageHosts { - return errors.Wrapf(define.ErrInvalidArg, "cannot add hosts if container will not create /etc/hosts") - } - ctr.config.HostAdd = hosts return nil } @@ -1282,9 +1212,6 @@ func WithRootFS(rootfs string) CtrCreateOption { if _, err := os.Stat(rootfs); err != nil { return errors.Wrapf(err, "error checking path %q", rootfs) } - if ctr.config.RootfsImageID != "" { - return errors.Wrapf(define.ErrInvalidArg, "cannot set both an image ID and a rootfs for a container") - } ctr.config.Rootfs = rootfs return nil } @@ -1314,12 +1241,6 @@ func WithUseImageResolvConf() CtrCreateOption { return define.ErrCtrFinalized } - if len(ctr.config.DNSServer) != 0 || - len(ctr.config.DNSSearch) != 0 || - len(ctr.config.DNSOption) != 0 { - return errors.Wrapf(define.ErrInvalidArg, "not creating resolv.conf conflicts with DNS options") - } - ctr.config.UseImageResolvConf = true return nil @@ -1334,10 +1255,6 @@ func WithUseImageHosts() CtrCreateOption { return define.ErrCtrFinalized } - if len(ctr.config.HostAdd) != 0 { - return errors.Wrapf(define.ErrInvalidArg, "not creating /etc/hosts conflicts with adding to the hosts file") - } - ctr.config.UseImageHosts = true return nil diff --git a/libpod/pod.go b/libpod/pod.go index 1b4c06c9d..4cdeb1033 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -88,6 +88,7 @@ type PodInspect struct { type PodInspectState struct { CgroupPath string `json:"cgroupPath"` InfraContainerID string `json:"infraContainerID"` + Status string `json:"status"` } // PodContainerInfo keeps information on a container in a pod diff --git a/libpod/pod_api.go b/libpod/pod_api.go index cb04f7411..200732652 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -407,7 +407,10 @@ func (p *Pod) Status() (map[string]define.ContainerStatus, error) { if err != nil { return nil, err } + return containerStatusFromContainers(allCtrs) +} +func containerStatusFromContainers(allCtrs []*Container) (map[string]define.ContainerStatus, error) { // We need to lock all the containers for _, ctr := range allCtrs { ctr.lock.Lock() @@ -443,6 +446,14 @@ func (p *Pod) Inspect() (*PodInspect, error) { if err != nil { return &PodInspect{}, err } + ctrStatuses, err := containerStatusFromContainers(containers) + if err != nil { + return nil, err + } + status, err := CreatePodStatusResults(ctrStatuses) + if err != nil { + return nil, err + } for _, c := range containers { containerStatus := "unknown" // Ignoring possible errors here because we don't want this to be @@ -468,6 +479,7 @@ func (p *Pod) Inspect() (*PodInspect, error) { State: &PodInspectState{ CgroupPath: p.state.CgroupPath, InfraContainerID: infraContainerID, + Status: status, }, Containers: podContainers, } diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 39284026c..de93fdce7 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -133,7 +133,12 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options .. return r.setupContainer(ctx, ctr) } -func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (c *Container, err error) { +func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Container, err error) { + // Validate the container + if err := ctr.validate(); err != nil { + return nil, err + } + // Allocate a lock for the container lock, err := r.lockManager.AllocateLock() if err != nil { @@ -190,27 +195,6 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (c *Contai ctr.config.Name = name } - // If CGroups are disabled, we MUST create a PID namespace. - // Otherwise, the OCI runtime won't be able to stop our container. - if ctr.config.NoCgroups { - if ctr.config.Spec.Linux == nil { - return nil, errors.Wrapf(define.ErrInvalidArg, "must provide Linux namespace configuration in OCI spec when using NoCgroups") - } - foundPid := false - for _, ns := range ctr.config.Spec.Linux.Namespaces { - if ns.Type == spec.PIDNamespace { - foundPid = true - if ns.Path != "" { - return nil, errors.Wrapf(define.ErrInvalidArg, "containers not creating CGroups must create a private PID namespace - cannot use another") - } - break - } - } - if !foundPid { - return nil, errors.Wrapf(define.ErrInvalidArg, "containers not creating CGroups must create a private PID namespace") - } - } - // Check CGroup parent sanity, and set it if it was not set. // Only if we're actually configuring CGroups. if !ctr.config.NoCgroups { |