diff options
Diffstat (limited to 'libpod')
28 files changed, 387 insertions, 373 deletions
diff --git a/libpod/common_test.go b/libpod/common_test.go index df730098e..ae3cb1c87 100644 --- a/libpod/common_test.go +++ b/libpod/common_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/lock" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/opencontainers/runtime-tools/generate" @@ -49,7 +50,7 @@ func getTestContainer(id, name string, manager lock.Manager) (*Container, error) }, }, state: &ContainerState{ - State: ContainerStateRunning, + State: define.ContainerStateRunning, ConfigPath: "/does/not/exist/specs/" + id, RunDir: "/does/not/exist/tmp/", Mounted: true, diff --git a/libpod/container.go b/libpod/container.go index d05baa7e0..713386477 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -21,31 +21,6 @@ import ( "github.com/pkg/errors" ) -// ContainerStatus represents the current state of a container -type ContainerStatus int - -const ( - // ContainerStateUnknown indicates that the container is in an error - // state where information about it cannot be retrieved - ContainerStateUnknown ContainerStatus = iota - // ContainerStateConfigured indicates that the container has had its - // storage configured but it has not been created in the OCI runtime - ContainerStateConfigured ContainerStatus = iota - // ContainerStateCreated indicates the container has been created in - // the OCI runtime but not started - ContainerStateCreated ContainerStatus = iota - // ContainerStateRunning indicates the container is currently executing - ContainerStateRunning ContainerStatus = iota - // ContainerStateStopped indicates that the container was running but has - // exited - ContainerStateStopped ContainerStatus = iota - // ContainerStatePaused indicates that the container has been paused - ContainerStatePaused ContainerStatus = iota - // ContainerStateExited indicates the the container has stopped and been - // cleaned up - ContainerStateExited ContainerStatus = iota -) - // CgroupfsDefaultCgroupParent is the cgroup parent for CGroupFS in libpod const CgroupfsDefaultCgroupParent = "/libpod_parent" @@ -169,7 +144,7 @@ type Container struct { // It is stored on disk in a tmpfs and recreated on reboot type ContainerState struct { // The current state of the running container - State ContainerStatus `json:"state"` + State define.ContainerStatus `json:"state"` // The path to the JSON OCI runtime spec for this container ConfigPath string `json:"configPath,omitempty"` // RunDir is a per-boot directory for container content @@ -428,51 +403,6 @@ type ContainerNamedVolume struct { Options []string `json:"options,omitempty"` } -// ContainerStatus returns a string representation for users -// of a container state -func (t ContainerStatus) String() string { - switch t { - case ContainerStateUnknown: - return "unknown" - case ContainerStateConfigured: - return "configured" - case ContainerStateCreated: - return "created" - case ContainerStateRunning: - return "running" - case ContainerStateStopped: - return "stopped" - case ContainerStatePaused: - return "paused" - case ContainerStateExited: - return "exited" - } - return "bad state" -} - -// StringToContainerStatus converts a string representation of a containers -// status into an actual container status type -func StringToContainerStatus(status string) (ContainerStatus, error) { - switch status { - case ContainerStateUnknown.String(): - return ContainerStateUnknown, nil - case ContainerStateConfigured.String(): - return ContainerStateConfigured, nil - case ContainerStateCreated.String(): - return ContainerStateCreated, nil - case ContainerStateRunning.String(): - return ContainerStateRunning, nil - case ContainerStateStopped.String(): - return ContainerStateStopped, nil - case ContainerStatePaused.String(): - return ContainerStatePaused, nil - case ContainerStateExited.String(): - return ContainerStateExited, nil - default: - return ContainerStateUnknown, errors.Wrapf(define.ErrInvalidArg, "unknown container state: %s", status) - } -} - // Config accessors // Unlocked @@ -823,13 +753,13 @@ func (c *Container) WorkingDir() string { // Require locking // State returns the current state of the container -func (c *Container) State() (ContainerStatus, error) { +func (c *Container) State() (define.ContainerStatus, error) { if !c.batched { c.lock.Lock() defer c.lock.Unlock() if err := c.syncContainer(); err != nil { - return ContainerStateUnknown, err + return define.ContainerStateUnknown, err } } return c.state.State, nil @@ -1097,7 +1027,7 @@ func (c *Container) NamespacePath(ns LinuxNS) (string, error) { } } - if c.state.State != ContainerStateRunning && c.state.State != ContainerStatePaused { + if c.state.State != define.ContainerStateRunning && c.state.State != define.ContainerStatePaused { return "", errors.Wrapf(define.ErrCtrStopped, "cannot get namespace path unless container %s is running", c.ID()) } diff --git a/libpod/container.log.go b/libpod/container.log.go new file mode 100644 index 000000000..7d0cd5bfb --- /dev/null +++ b/libpod/container.log.go @@ -0,0 +1,73 @@ +package libpod + +import ( + "os" + + "github.com/containers/libpod/libpod/logs" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// Log is a runtime function that can read one or more container logs. +func (r *Runtime) Log(containers []*Container, options *logs.LogOptions, logChannel chan *logs.LogLine) error { + for _, ctr := range containers { + if err := ctr.ReadLog(options, logChannel); err != nil { + return err + } + } + return nil +} + +// ReadLog reads a containers log based on the input options and returns loglines over a channel +func (c *Container) ReadLog(options *logs.LogOptions, logChannel chan *logs.LogLine) error { + // TODO Skip sending logs until journald logs can be read + // TODO make this not a magic string + if c.LogDriver() == JournaldLogging { + return c.readFromJournal(options, logChannel) + } + return c.readFromLogFile(options, logChannel) +} + +func (c *Container) readFromLogFile(options *logs.LogOptions, logChannel chan *logs.LogLine) error { + t, tailLog, err := logs.GetLogFile(c.LogPath(), options) + if err != nil { + // If the log file does not exist, this is not fatal. + if os.IsNotExist(errors.Cause(err)) { + return nil + } + return errors.Wrapf(err, "unable to read log file %s for %s ", c.ID(), c.LogPath()) + } + options.WaitGroup.Add(1) + if len(tailLog) > 0 { + for _, nll := range tailLog { + nll.CID = c.ID() + if nll.Since(options.Since) { + logChannel <- nll + } + } + } + + go func() { + var partial string + for line := range t.Lines { + nll, err := logs.NewLogLine(line.Text) + if err != nil { + logrus.Error(err) + continue + } + if nll.Partial() { + partial = partial + nll.Msg + continue + } else if !nll.Partial() && len(partial) > 1 { + nll.Msg = partial + partial = "" + } + nll.CID = c.ID() + if nll.Since(options.Since) { + logChannel <- nll + } + } + options.WaitGroup.Done() + }() + return nil +} diff --git a/libpod/container_api.go b/libpod/container_api.go index b8c339a39..8d1e5751b 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -37,9 +37,9 @@ func (c *Container) Init(ctx context.Context) (err error) { } } - if !(c.state.State == ContainerStateConfigured || - c.state.State == ContainerStateStopped || - c.state.State == ContainerStateExited) { + if !(c.state.State == define.ContainerStateConfigured || + c.state.State == define.ContainerStateStopped || + c.state.State == define.ContainerStateExited) { return errors.Wrapf(define.ErrCtrStateInvalid, "container %s has already been created in runtime", c.ID()) } @@ -55,7 +55,7 @@ func (c *Container) Init(ctx context.Context) (err error) { return err } - if c.state.State == ContainerStateStopped { + if c.state.State == define.ContainerStateStopped { // Reinitialize the container return c.reinit(ctx, false) } @@ -178,14 +178,14 @@ func (c *Container) StopWithTimeout(timeout uint) error { } } - if c.state.State == ContainerStateConfigured || - c.state.State == ContainerStateUnknown || - c.state.State == ContainerStatePaused { + if c.state.State == define.ContainerStateConfigured || + c.state.State == define.ContainerStateUnknown || + c.state.State == define.ContainerStatePaused { return errors.Wrapf(define.ErrCtrStateInvalid, "can only stop created, running, or stopped containers. %s is in state %s", c.ID(), c.state.State.String()) } - if c.state.State == ContainerStateStopped || - c.state.State == ContainerStateExited { + if c.state.State == define.ContainerStateStopped || + c.state.State == define.ContainerStateExited { return define.ErrCtrStopped } defer c.newContainerEvent(events.Stop) @@ -203,7 +203,7 @@ func (c *Container) Kill(signal uint) error { } } - if c.state.State != ContainerStateRunning { + if c.state.State != define.ContainerStateRunning { return errors.Wrapf(define.ErrCtrStateInvalid, "can only kill running containers. %s is in state %s", c.ID(), c.state.State.String()) } @@ -241,7 +241,7 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir conState := c.state.State // TODO can probably relax this once we track exec sessions - if conState != ContainerStateRunning { + if conState != define.ContainerStateRunning { return errors.Wrapf(define.ErrCtrStateInvalid, "cannot exec into container that is not running") } if privileged || c.config.Privileged { @@ -399,9 +399,9 @@ func (c *Container) Attach(streams *AttachStreams, keys string, resize <-chan re c.lock.Unlock() } - if c.state.State != ContainerStateCreated && - c.state.State != ContainerStateRunning && - c.state.State != ContainerStateExited { + if c.state.State != define.ContainerStateCreated && + c.state.State != define.ContainerStateRunning && + c.state.State != define.ContainerStateExited { return errors.Wrapf(define.ErrCtrStateInvalid, "can only attach to created or running containers") } defer c.newContainerEvent(events.Attach) @@ -440,7 +440,7 @@ func (c *Container) Unmount(force bool) error { return errors.Wrapf(err, "can't determine how many times %s is mounted, refusing to unmount", c.ID()) } if mounted == 1 { - if c.state.State == ContainerStateRunning || c.state.State == ContainerStatePaused { + if c.state.State == define.ContainerStateRunning || c.state.State == define.ContainerStatePaused { return errors.Wrapf(define.ErrCtrStateInvalid, "cannot unmount storage for container %s as it is running or paused", c.ID()) } if len(c.state.ExecSessions) != 0 { @@ -464,10 +464,10 @@ func (c *Container) Pause() error { } } - if c.state.State == ContainerStatePaused { + if c.state.State == define.ContainerStatePaused { return errors.Wrapf(define.ErrCtrStateInvalid, "%q is already paused", c.ID()) } - if c.state.State != ContainerStateRunning { + if c.state.State != define.ContainerStateRunning { return errors.Wrapf(define.ErrCtrStateInvalid, "%q is not running, can't pause", c.state.State) } defer c.newContainerEvent(events.Pause) @@ -485,7 +485,7 @@ func (c *Container) Unpause() error { } } - if c.state.State != ContainerStatePaused { + if c.state.State != define.ContainerStatePaused { return errors.Wrapf(define.ErrCtrStateInvalid, "%q is not paused, can't unpause", c.ID()) } defer c.newContainerEvent(events.Unpause) @@ -578,7 +578,7 @@ func (c *Container) Cleanup(ctx context.Context) error { } // Check if state is good - if c.state.State == ContainerStateRunning || c.state.State == ContainerStatePaused { + if c.state.State == define.ContainerStateRunning || c.state.State == define.ContainerStatePaused { return errors.Wrapf(define.ErrCtrStateInvalid, "container %s is running or paused, refusing to clean up", c.ID()) } @@ -656,9 +656,9 @@ func (c *Container) Sync() error { // If runtime knows about the container, update its status in runtime // And then save back to disk - if (c.state.State != ContainerStateUnknown) && - (c.state.State != ContainerStateConfigured) && - (c.state.State != ContainerStateExited) { + if (c.state.State != define.ContainerStateUnknown) && + (c.state.State != define.ContainerStateConfigured) && + (c.state.State != define.ContainerStateExited) { oldState := c.state.State if err := c.ociRuntime.updateContainerStatus(c, true); err != nil { return err @@ -687,27 +687,27 @@ func (c *Container) Refresh(ctx context.Context) error { } wasCreated := false - if c.state.State == ContainerStateCreated { + if c.state.State == define.ContainerStateCreated { wasCreated = true } wasRunning := false - if c.state.State == ContainerStateRunning { + if c.state.State == define.ContainerStateRunning { wasRunning = true } wasPaused := false - if c.state.State == ContainerStatePaused { + if c.state.State == define.ContainerStatePaused { wasPaused = true } // First, unpause the container if it's paused - if c.state.State == ContainerStatePaused { + if c.state.State == define.ContainerStatePaused { if err := c.unpause(); err != nil { return err } } // Next, if the container is running, stop it - if c.state.State == ContainerStateRunning { + if c.state.State == define.ContainerStateRunning { if err := c.stop(c.config.StopTimeout); err != nil { return err } @@ -724,7 +724,7 @@ func (c *Container) Refresh(ctx context.Context) error { // If the container is in ContainerStateStopped, we need to delete it // from the runtime and clear conmon state - if c.state.State == ContainerStateStopped { + if c.state.State == define.ContainerStateStopped { if err := c.delete(ctx); err != nil { return err } diff --git a/libpod/container_commit.go b/libpod/container_commit.go index 82115455a..17586bfad 100644 --- a/libpod/container_commit.go +++ b/libpod/container_commit.go @@ -9,6 +9,7 @@ import ( "github.com/containers/buildah" "github.com/containers/buildah/util" is "github.com/containers/image/storage" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/events" "github.com/containers/libpod/libpod/image" "github.com/pkg/errors" @@ -48,7 +49,7 @@ func (c *Container) Commit(ctx context.Context, destImage string, options Contai } } - if c.state.State == ContainerStateRunning && options.Pause { + if c.state.State == define.ContainerStateRunning && options.Pause { if err := c.ociRuntime.pauseContainer(c); err != nil { return nil, errors.Wrapf(err, "error pausing container %q", c.ID()) } diff --git a/libpod/container_graph.go b/libpod/container_graph.go index c266c5227..50dbdfbe4 100644 --- a/libpod/container_graph.go +++ b/libpod/container_graph.go @@ -244,13 +244,13 @@ func startNode(ctx context.Context, node *containerNode, setError bool, ctrError // Start the container (only if it is not running) if !ctrErrored { - if !restart && node.container.state.State != ContainerStateRunning { + if !restart && node.container.state.State != define.ContainerStateRunning { if err := node.container.initAndStart(ctx); err != nil { ctrErrored = true ctrErrors[node.id] = err } } - if restart && node.container.state.State != ContainerStatePaused && node.container.state.State != ContainerStateUnknown { + if restart && node.container.state.State != define.ContainerStatePaused && node.container.state.State != define.ContainerStateUnknown { if err := node.container.restartWithTimeout(ctx, node.container.config.StopTimeout); err != nil { ctrErrored = true ctrErrors[node.id] = err diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index 3ac774060..6085f1210 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -5,6 +5,7 @@ import ( "time" "github.com/containers/image/manifest" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/driver" "github.com/cri-o/ocicni/pkg/ocicni" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -255,8 +256,8 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) State: &InspectContainerState{ OciVersion: spec.Version, Status: runtimeInfo.State.String(), - Running: runtimeInfo.State == ContainerStateRunning, - Paused: runtimeInfo.State == ContainerStatePaused, + Running: runtimeInfo.State == define.ContainerStateRunning, + Paused: runtimeInfo.State == define.ContainerStatePaused, OOMKilled: runtimeInfo.OOMKilled, Dead: runtimeInfo.State.String() == "bad state", Pid: runtimeInfo.PID, diff --git a/libpod/container_internal.go b/libpod/container_internal.go index fcd6a990a..43d2b6e61 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -156,7 +156,7 @@ func (c *Container) waitForExitFileAndSync() error { // Reset our state c.state.ExitCode = -1 c.state.FinishedTime = time.Now() - c.state.State = ContainerStateStopped + c.state.State = define.ContainerStateStopped if err2 := c.save(); err2 != nil { logrus.Errorf("Error saving container %s state: %v", c.ID(), err2) @@ -241,9 +241,9 @@ func (c *Container) handleRestartPolicy(ctx context.Context) (restarted bool, er // Is the container running again? // If so, we don't have to do anything - if c.state.State == ContainerStateRunning || c.state.State == ContainerStatePaused { + if c.state.State == define.ContainerStateRunning || c.state.State == define.ContainerStatePaused { return false, nil - } else if c.state.State == ContainerStateUnknown { + } else if c.state.State == define.ContainerStateUnknown { return false, errors.Wrapf(define.ErrInternal, "invalid container state encountered in restart attempt!") } @@ -267,13 +267,13 @@ func (c *Container) handleRestartPolicy(ctx context.Context) (restarted bool, er return false, err } - if c.state.State == ContainerStateStopped { + if c.state.State == define.ContainerStateStopped { // Reinitialize the container if we need to if err := c.reinit(ctx, true); err != nil { return false, err } - } else if c.state.State == ContainerStateConfigured || - c.state.State == ContainerStateExited { + } else if c.state.State == define.ContainerStateConfigured || + c.state.State == define.ContainerStateExited { // Initialize the container if err := c.init(ctx, true); err != nil { return false, err @@ -295,9 +295,9 @@ func (c *Container) syncContainer() error { } // If runtime knows about the container, update its status in runtime // And then save back to disk - if (c.state.State != ContainerStateUnknown) && - (c.state.State != ContainerStateConfigured) && - (c.state.State != ContainerStateExited) { + if (c.state.State != define.ContainerStateUnknown) && + (c.state.State != define.ContainerStateConfigured) && + (c.state.State != define.ContainerStateExited) { oldState := c.state.State // TODO: optionally replace this with a stat for the exit file if err := c.ociRuntime.updateContainerStatus(c, false); err != nil { @@ -307,8 +307,8 @@ func (c *Container) syncContainer() error { if c.state.State != oldState { // Check for a restart policy match if c.config.RestartPolicy != RestartPolicyNone && c.config.RestartPolicy != RestartPolicyNo && - (oldState == ContainerStateRunning || oldState == ContainerStatePaused) && - (c.state.State == ContainerStateStopped || c.state.State == ContainerStateExited) && + (oldState == define.ContainerStateRunning || oldState == define.ContainerStatePaused) && + (c.state.State == define.ContainerStateStopped || c.state.State == define.ContainerStateExited) && !c.state.StoppedByUser { c.state.RestartPolicyMatch = true } @@ -336,7 +336,7 @@ func (c *Container) setupStorage(ctx context.Context) error { return errors.Wrapf(define.ErrCtrRemoved, "container %s is not valid", c.ID()) } - if c.state.State != ContainerStateConfigured { + if c.state.State != define.ContainerStateConfigured { return errors.Wrapf(define.ErrCtrStateInvalid, "container %s must be in Configured state to have storage set up", c.ID()) } @@ -418,7 +418,7 @@ func (c *Container) setupStorage(ctx context.Context) error { // Tear down a container's storage prior to removal func (c *Container) teardownStorage() error { - if c.state.State == ContainerStateRunning || c.state.State == ContainerStatePaused { + if c.state.State == define.ContainerStateRunning || c.state.State == define.ContainerStatePaused { return errors.Wrapf(define.ErrCtrStateInvalid, "cannot remove storage for container %s as it is running or paused", c.ID()) } @@ -454,8 +454,8 @@ func resetState(state *ContainerState) error { state.PID = 0 state.Mountpoint = "" state.Mounted = false - if state.State != ContainerStateExited { - state.State = ContainerStateConfigured + if state.State != define.ContainerStateExited { + state.State = define.ContainerStateConfigured } state.ExecSessions = make(map[string]*ExecSession) state.NetworkStatus = nil @@ -609,7 +609,7 @@ func (c *Container) isStopped() (bool, error) { if err != nil { return true, err } - return (c.state.State != ContainerStateRunning && c.state.State != ContainerStatePaused), nil + return (c.state.State != define.ContainerStateRunning && c.state.State != define.ContainerStatePaused), nil } // save container state to the database @@ -625,10 +625,10 @@ func (c *Container) save() error { // Otherwise, this function will return with error if there are dependencies of this container that aren't running. func (c *Container) prepareToStart(ctx context.Context, recursive bool) (err error) { // Container must be created or stopped to be started - if !(c.state.State == ContainerStateConfigured || - c.state.State == ContainerStateCreated || - c.state.State == ContainerStateStopped || - c.state.State == ContainerStateExited) { + if !(c.state.State == define.ContainerStateConfigured || + c.state.State == define.ContainerStateCreated || + c.state.State == define.ContainerStateStopped || + c.state.State == define.ContainerStateExited) { return errors.Wrapf(define.ErrCtrStateInvalid, "container %s must be in Created or Stopped state to be started", c.ID()) } @@ -654,13 +654,13 @@ func (c *Container) prepareToStart(ctx context.Context, recursive bool) (err err return err } - if c.state.State == ContainerStateStopped { + if c.state.State == define.ContainerStateStopped { // Reinitialize the container if we need to if err := c.reinit(ctx, false); err != nil { return err } - } else if c.state.State == ContainerStateConfigured || - c.state.State == ContainerStateExited { + } else if c.state.State == define.ContainerStateConfigured || + c.state.State == define.ContainerStateExited { // Or initialize it if necessary if err := c.init(ctx, false); err != nil { return err @@ -763,7 +763,7 @@ func (c *Container) getAllDependencies(visited map[string]*Container) error { } // if the dependency is already running, we can assume its dependencies are also running // so no need to add them to those we need to start - if status != ContainerStateRunning { + if status != define.ContainerStateRunning { visited[depID] = dep if err := dep.getAllDependencies(visited); err != nil { return err @@ -795,7 +795,7 @@ func (c *Container) checkDependenciesRunning() ([]string, error) { if err != nil { return nil, errors.Wrapf(err, "error retrieving state of dependency %s of container %s", dep, c.ID()) } - if state != ContainerStateRunning { + if state != define.ContainerStateRunning { notRunning = append(notRunning, dep) } depCtrs[dep] = depCtr @@ -824,7 +824,7 @@ func (c *Container) checkDependenciesRunningLocked(depCtrs map[string]*Container return nil, err } - if depCtr.state.State != ContainerStateRunning { + if depCtr.state.State != define.ContainerStateRunning { notRunning = append(notRunning, dep) } } @@ -875,7 +875,7 @@ func (c *Container) init(ctx context.Context, retainRetries bool) error { c.state.ExitCode = 0 c.state.Exited = false - c.state.State = ContainerStateCreated + c.state.State = define.ContainerStateCreated c.state.StoppedByUser = false c.state.RestartPolicyMatch = false @@ -906,7 +906,7 @@ func (c *Container) cleanupRuntime(ctx context.Context) error { // If the container is not ContainerStateStopped or // ContainerStateCreated, do nothing. - if c.state.State != ContainerStateStopped && c.state.State != ContainerStateCreated { + if c.state.State != define.ContainerStateStopped && c.state.State != define.ContainerStateCreated { return nil } @@ -922,10 +922,10 @@ func (c *Container) cleanupRuntime(ctx context.Context) error { // If we were Stopped, we are now Exited, as we've removed ourself // from the runtime. // If we were Created, we are now Configured. - if c.state.State == ContainerStateStopped { - c.state.State = ContainerStateExited - } else if c.state.State == ContainerStateCreated { - c.state.State = ContainerStateConfigured + if c.state.State == define.ContainerStateStopped { + c.state.State = define.ContainerStateExited + } else if c.state.State == define.ContainerStateCreated { + c.state.State = define.ContainerStateConfigured } if c.valid { @@ -964,16 +964,16 @@ func (c *Container) reinit(ctx context.Context, retainRetries bool) error { // Does not lock or check validity func (c *Container) initAndStart(ctx context.Context) (err error) { // If we are ContainerStateUnknown, throw an error - if c.state.State == ContainerStateUnknown { + if c.state.State == define.ContainerStateUnknown { return errors.Wrapf(define.ErrCtrStateInvalid, "container %s is in an unknown state", c.ID()) } // If we are running, do nothing - if c.state.State == ContainerStateRunning { + if c.state.State == define.ContainerStateRunning { return nil } // If we are paused, throw an error - if c.state.State == ContainerStatePaused { + if c.state.State == define.ContainerStatePaused { return errors.Wrapf(define.ErrCtrStateInvalid, "cannot start paused container %s", c.ID()) } @@ -991,14 +991,14 @@ func (c *Container) initAndStart(ctx context.Context) (err error) { // If we are ContainerStateStopped we need to remove from runtime // And reset to ContainerStateConfigured - if c.state.State == ContainerStateStopped { + if c.state.State == define.ContainerStateStopped { logrus.Debugf("Recreating container %s in OCI runtime", c.ID()) if err := c.reinit(ctx, false); err != nil { return err } - } else if c.state.State == ContainerStateConfigured || - c.state.State == ContainerStateExited { + } else if c.state.State == define.ContainerStateConfigured || + c.state.State == define.ContainerStateExited { if err := c.init(ctx, false); err != nil { return err } @@ -1019,7 +1019,7 @@ func (c *Container) start() error { } logrus.Debugf("Started container %s", c.ID()) - c.state.State = ContainerStateRunning + c.state.State = define.ContainerStateRunning if c.config.HealthCheckConfig != nil { if err := c.updateHealthStatus(HealthCheckStarting); err != nil { @@ -1060,7 +1060,7 @@ func (c *Container) pause() error { logrus.Debugf("Paused container %s", c.ID()) - c.state.State = ContainerStatePaused + c.state.State = define.ContainerStatePaused return c.save() } @@ -1073,20 +1073,20 @@ func (c *Container) unpause() error { logrus.Debugf("Unpaused container %s", c.ID()) - c.state.State = ContainerStateRunning + c.state.State = define.ContainerStateRunning return c.save() } // Internal, non-locking function to restart a container func (c *Container) restartWithTimeout(ctx context.Context, timeout uint) (err error) { - if c.state.State == ContainerStateUnknown || c.state.State == ContainerStatePaused { + if c.state.State == define.ContainerStateUnknown || c.state.State == define.ContainerStatePaused { return errors.Wrapf(define.ErrCtrStateInvalid, "unable to restart a container in a paused or unknown state") } c.newContainerEvent(events.Restart) - if c.state.State == ContainerStateRunning { + if c.state.State == define.ContainerStateRunning { if err := c.stop(timeout); err != nil { return err } @@ -1102,13 +1102,13 @@ func (c *Container) restartWithTimeout(ctx context.Context, timeout uint) (err e return err } - if c.state.State == ContainerStateStopped { + if c.state.State == define.ContainerStateStopped { // Reinitialize the container if we need to if err := c.reinit(ctx, false); err != nil { return err } - } else if c.state.State == ContainerStateConfigured || - c.state.State == ContainerStateExited { + } else if c.state.State == define.ContainerStateConfigured || + c.state.State == define.ContainerStateExited { // Initialize the container if err := c.init(ctx, false); err != nil { return err @@ -1482,12 +1482,12 @@ func (c *Container) copyWithTarFromImage(src, dest string) error { // If it is, we'll remove the container anyways. // Returns nil if safe to remove, or an error describing why it's unsafe if not. func (c *Container) checkReadyForRemoval() error { - if c.state.State == ContainerStateUnknown { + if c.state.State == define.ContainerStateUnknown { return errors.Wrapf(define.ErrCtrStateInvalid, "container %s is in invalid state", c.ID()) } - if c.state.State == ContainerStateRunning || - c.state.State == ContainerStatePaused { + if c.state.State == define.ContainerStateRunning || + c.state.State == define.ContainerStatePaused { return errors.Wrapf(define.ErrCtrStateInvalid, "cannot remove container %s as it is %s - running or paused containers cannot be removed", c.ID(), c.state.State.String()) } diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index e93e0cad8..fad45233a 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -573,7 +573,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO return err } - if c.state.State != ContainerStateRunning { + if c.state.State != define.ContainerStateRunning { return errors.Wrapf(define.ErrCtrStateInvalid, "%q is not running, cannot checkpoint", c.state.State) } @@ -605,7 +605,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO logrus.Debugf("Checkpointed container %s", c.ID()) if !options.KeepRunning { - c.state.State = ContainerStateStopped + c.state.State = define.ContainerStateStopped // Cleanup Storage and Network if err := c.cleanup(ctx); err != nil { @@ -664,7 +664,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti return err } - if (c.state.State != ContainerStateConfigured) && (c.state.State != ContainerStateExited) { + if (c.state.State != define.ContainerStateConfigured) && (c.state.State != define.ContainerStateExited) { return errors.Wrapf(define.ErrCtrStateInvalid, "container %s is running or paused, cannot restore", c.ID()) } @@ -781,7 +781,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti logrus.Debugf("Restored container %s", c.ID()) - c.state.State = ContainerStateRunning + c.state.State = define.ContainerStateRunning if !options.Keep { // Delete all checkpoint related files. At this point, in theory, all files diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go index e549673a6..8a87a8796 100644 --- a/libpod/container_log_linux.go +++ b/libpod/container_log_linux.go @@ -9,6 +9,7 @@ import ( "strings" "time" + "github.com/containers/libpod/libpod/logs" journal "github.com/coreos/go-systemd/sdjournal" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -27,7 +28,7 @@ const ( bufLen = 16384 ) -func (c *Container) readFromJournal(options *LogOptions, logChannel chan *LogLine) error { +func (c *Container) readFromJournal(options *logs.LogOptions, logChannel chan *logs.LogLine) error { var config journal.JournalReaderConfig config.NumFromTail = options.Tail config.Formatter = journalFormatter @@ -79,7 +80,7 @@ func (c *Container) readFromJournal(options *LogOptions, logChannel chan *LogLin // because we are reusing bytes, we need to make // sure the old data doesn't get into the new line bytestr := string(bytes[:ec]) - logLine, err2 := newLogLine(bytestr) + logLine, err2 := logs.NewLogLine(bytestr) if err2 != nil { logrus.Error(err2) continue @@ -98,7 +99,7 @@ func (c *Container) readFromJournal(options *LogOptions, logChannel chan *LogLin func journalFormatter(entry *journal.JournalEntry) (string, error) { usec := entry.RealtimeTimestamp - tsString := time.Unix(0, int64(usec)*int64(time.Microsecond)).Format(logTimeFormat) + tsString := time.Unix(0, int64(usec)*int64(time.Microsecond)).Format(logs.LogTimeFormat) output := fmt.Sprintf("%s ", tsString) priority, ok := entry.Fields["PRIORITY"] if !ok { @@ -114,9 +115,9 @@ func journalFormatter(entry *journal.JournalEntry) (string, error) { // if CONTAINER_PARTIAL_MESSAGE is defined, the log type is "P" if _, ok := entry.Fields["CONTAINER_PARTIAL_MESSAGE"]; ok { - output += fmt.Sprintf("%s ", partialLogType) + output += fmt.Sprintf("%s ", logs.PartialLogType) } else { - output += fmt.Sprintf("%s ", fullLogType) + output += fmt.Sprintf("%s ", logs.FullLogType) } // Finally, append the message @@ -129,12 +130,12 @@ func journalFormatter(entry *journal.JournalEntry) (string, error) { } type FollowBuffer struct { - logChannel chan *LogLine + logChannel chan *logs.LogLine } func (f FollowBuffer) Write(p []byte) (int, error) { bytestr := string(p) - logLine, err := newLogLine(bytestr) + logLine, err := logs.NewLogLine(bytestr) if err != nil { return -1, err } diff --git a/libpod/container_log_unsupported.go b/libpod/container_log_unsupported.go index 380d317b5..2c4492b10 100644 --- a/libpod/container_log_unsupported.go +++ b/libpod/container_log_unsupported.go @@ -4,9 +4,10 @@ package libpod import ( "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/libpod/logs" "github.com/pkg/errors" ) -func (c *Container) readFromJournal(options *LogOptions, logChannel chan *LogLine) error { +func (c *Container) readFromJournal(options *logs.LogOptions, logChannel chan *logs.LogLine) error { return errors.Wrapf(define.ErrOSNotSupported, "Journald logging only enabled with systemd on linux") } diff --git a/libpod/container_top_linux.go b/libpod/container_top_linux.go index 2e0e83c05..ce471838d 100644 --- a/libpod/container_top_linux.go +++ b/libpod/container_top_linux.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/rootless" "github.com/containers/psgo" "github.com/pkg/errors" @@ -18,7 +19,7 @@ func (c *Container) Top(descriptors []string) ([]string, error) { if err != nil { return nil, errors.Wrapf(err, "unable to look up state for %s", c.ID()) } - if conStat != ContainerStateRunning { + if conStat != define.ContainerStateRunning { return nil, errors.Errorf("top can only be used on running containers") } @@ -60,9 +61,3 @@ func (c *Container) GetContainerPidInformation(descriptors []string) ([]string, } return res, nil } - -// GetContainerPidInformationDescriptors returns a string slice of all supported -// format descriptors of GetContainerPidInformation. -func GetContainerPidInformationDescriptors() ([]string, error) { - return psgo.ListDescriptors(), nil -} diff --git a/libpod/container_top_unsupported.go b/libpod/container_top_unsupported.go index 2117f913d..382c98b54 100644 --- a/libpod/container_top_unsupported.go +++ b/libpod/container_top_unsupported.go @@ -15,9 +15,3 @@ import "github.com/containers/libpod/libpod/define" func (c *Container) GetContainerPidInformation(descriptors []string) ([]string, error) { return nil, define.ErrNotImplemented } - -// GetContainerPidInformationDescriptors returns a string slice of all supported -// format descriptors of GetContainerPidInformation. -func GetContainerPidInformationDescriptors() ([]string, error) { - return nil, define.ErrNotImplemented -} diff --git a/libpod/define/config.go b/libpod/define/config.go index 256a4b21f..d8d6ccf55 100644 --- a/libpod/define/config.go +++ b/libpod/define/config.go @@ -3,8 +3,18 @@ package define var ( // DefaultInitPath is the default path to the container-init binary DefaultInitPath = "/usr/libexec/podman/catatonit" + // DefaultInfraImage to use for infra container + DefaultInfraImage = "k8s.gcr.io/pause:3.1" + // DefaultInfraCommand to be run in an infra container + DefaultInfraCommand = "/pause" ) // CtrRemoveTimeout is the default number of seconds to wait after stopping a container // before sending the kill signal const CtrRemoveTimeout = 10 + +// InfoData holds the info type, i.e store, host etc and the data for each type +type InfoData struct { + Type string + Data map[string]interface{} +} diff --git a/libpod/define/containerstate.go b/libpod/define/containerstate.go new file mode 100644 index 000000000..ab2527b3e --- /dev/null +++ b/libpod/define/containerstate.go @@ -0,0 +1,73 @@ +package define + +import "github.com/pkg/errors" + +// ContainerStatus represents the current state of a container +type ContainerStatus int + +const ( + // ContainerStateUnknown indicates that the container is in an error + // state where information about it cannot be retrieved + ContainerStateUnknown ContainerStatus = iota + // ContainerStateConfigured indicates that the container has had its + // storage configured but it has not been created in the OCI runtime + ContainerStateConfigured ContainerStatus = iota + // ContainerStateCreated indicates the container has been created in + // the OCI runtime but not started + ContainerStateCreated ContainerStatus = iota + // ContainerStateRunning indicates the container is currently executing + ContainerStateRunning ContainerStatus = iota + // ContainerStateStopped indicates that the container was running but has + // exited + ContainerStateStopped ContainerStatus = iota + // ContainerStatePaused indicates that the container has been paused + ContainerStatePaused ContainerStatus = iota + // ContainerStateExited indicates the the container has stopped and been + // cleaned up + ContainerStateExited ContainerStatus = iota +) + +// ContainerStatus returns a string representation for users +// of a container state +func (t ContainerStatus) String() string { + switch t { + case ContainerStateUnknown: + return "unknown" + case ContainerStateConfigured: + return "configured" + case ContainerStateCreated: + return "created" + case ContainerStateRunning: + return "running" + case ContainerStateStopped: + return "stopped" + case ContainerStatePaused: + return "paused" + case ContainerStateExited: + return "exited" + } + return "bad state" +} + +// StringToContainerStatus converts a string representation of a containers +// status into an actual container status type +func StringToContainerStatus(status string) (ContainerStatus, error) { + switch status { + case ContainerStateUnknown.String(): + return ContainerStateUnknown, nil + case ContainerStateConfigured.String(): + return ContainerStateConfigured, nil + case ContainerStateCreated.String(): + return ContainerStateCreated, nil + case ContainerStateRunning.String(): + return ContainerStateRunning, nil + case ContainerStateStopped.String(): + return ContainerStateStopped, nil + case ContainerStatePaused.String(): + return ContainerStatePaused, nil + case ContainerStateExited.String(): + return ContainerStateExited, nil + default: + return ContainerStateUnknown, errors.Wrapf(ErrInvalidArg, "unknown container state: %s", status) + } +} diff --git a/libpod/version.go b/libpod/define/version.go index d2b99a275..0f9f49050 100644 --- a/libpod/version.go +++ b/libpod/define/version.go @@ -1,4 +1,4 @@ -package libpod +package define import ( "runtime" diff --git a/libpod/healthcheck.go b/libpod/healthcheck.go index 3e36a2c95..f4ea6c694 100644 --- a/libpod/healthcheck.go +++ b/libpod/healthcheck.go @@ -9,6 +9,7 @@ import ( "strings" "time" + "github.com/containers/libpod/libpod/define" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -169,7 +170,7 @@ func checkHealthCheckCanBeRun(c *Container) (HealthCheckStatus, error) { if err != nil { return HealthCheckInternalError, err } - if cstate != ContainerStateRunning { + if cstate != define.ContainerStateRunning { return HealthCheckContainerStopped, errors.Errorf("container %s is not running", c.ID()) } if !c.HasHealthCheck() { diff --git a/libpod/info.go b/libpod/info.go index c96293e3d..4a89fa648 100644 --- a/libpod/info.go +++ b/libpod/info.go @@ -19,12 +19,6 @@ import ( "github.com/pkg/errors" ) -// InfoData holds the info type, i.e store, host etc and the data for each type -type InfoData struct { - Type string - Data map[string]interface{} -} - // top-level "host" info func (r *Runtime) hostInfo() (map[string]interface{}, error) { // lets say OS, arch, number of cpus, amount of memory, maybe os distribution/version, hostname, kernel version, uptime diff --git a/libpod/container_log.go b/libpod/logs/log.go index 374e5a1fc..488291cfe 100644 --- a/libpod/container_log.go +++ b/libpod/logs/log.go @@ -1,31 +1,29 @@ -package libpod +package logs import ( "fmt" "io/ioutil" - "os" "strings" "sync" "time" "github.com/hpcloud/tail" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) const ( - // logTimeFormat is the time format used in the log. + // LogTimeFormat is the time format used in the log. // It is a modified version of RFC3339Nano that guarantees trailing // zeroes are not trimmed, taken from // https://github.com/golang/go/issues/19635 - logTimeFormat = "2006-01-02T15:04:05.000000000Z07:00" + LogTimeFormat = "2006-01-02T15:04:05.000000000Z07:00" - // partialLogType signifies a log line that exceeded the buffer + // PartialLogType signifies a log line that exceeded the buffer // length and needed to spill into a new line - partialLogType = "P" + PartialLogType = "P" - // fullLogType signifies a log line is full - fullLogType = "F" + // FullLogType signifies a log line is full + FullLogType = "F" ) // LogOptions is the options you can use for logs @@ -48,72 +46,8 @@ type LogLine struct { CID string } -// Log is a runtime function that can read one or more container logs. -func (r *Runtime) Log(containers []*Container, options *LogOptions, logChannel chan *LogLine) error { - for _, ctr := range containers { - if err := ctr.ReadLog(options, logChannel); err != nil { - return err - } - } - return nil -} - -// ReadLog reads a containers log based on the input options and returns loglines over a channel -func (c *Container) ReadLog(options *LogOptions, logChannel chan *LogLine) error { - // TODO Skip sending logs until journald logs can be read - // TODO make this not a magic string - if c.LogDriver() == JournaldLogging { - return c.readFromJournal(options, logChannel) - } - return c.readFromLogFile(options, logChannel) -} - -func (c *Container) readFromLogFile(options *LogOptions, logChannel chan *LogLine) error { - t, tailLog, err := getLogFile(c.LogPath(), options) - if err != nil { - // If the log file does not exist, this is not fatal. - if os.IsNotExist(errors.Cause(err)) { - return nil - } - return errors.Wrapf(err, "unable to read log file %s for %s ", c.ID(), c.LogPath()) - } - options.WaitGroup.Add(1) - if len(tailLog) > 0 { - for _, nll := range tailLog { - nll.CID = c.ID() - if nll.Since(options.Since) { - logChannel <- nll - } - } - } - - go func() { - var partial string - for line := range t.Lines { - nll, err := newLogLine(line.Text) - if err != nil { - logrus.Error(err) - continue - } - if nll.Partial() { - partial = partial + nll.Msg - continue - } else if !nll.Partial() && len(partial) > 1 { - nll.Msg = partial - partial = "" - } - nll.CID = c.ID() - if nll.Since(options.Since) { - logChannel <- nll - } - } - options.WaitGroup.Done() - }() - return nil -} - -// getLogFile returns an hp tail for a container given options -func getLogFile(path string, options *LogOptions) (*tail.Tail, []*LogLine, error) { +// GetLogFile returns an hp tail for a container given options +func GetLogFile(path string, options *LogOptions) (*tail.Tail, []*LogLine, error) { var ( whence int err error @@ -154,7 +88,7 @@ func getTailLog(path string, tail int) ([]*LogLine, error) { if len(splitContent[i]) == 0 { continue } - nll, err := newLogLine(splitContent[i]) + nll, err := NewLogLine(splitContent[i]) if err != nil { return nil, err } @@ -191,7 +125,7 @@ func (l *LogLine) String(options *LogOptions) string { out = fmt.Sprintf("%s ", cid) } if options.Timestamps { - out = out + fmt.Sprintf("%s ", l.Time.Format(logTimeFormat)) + out = out + fmt.Sprintf("%s ", l.Time.Format(LogTimeFormat)) } return out + l.Msg } @@ -201,13 +135,13 @@ func (l *LogLine) Since(since time.Time) bool { return l.Time.After(since) } -// newLogLine creates a logLine struct from a container log string -func newLogLine(line string) (*LogLine, error) { +// NewLogLine creates a logLine struct from a container log string +func NewLogLine(line string) (*LogLine, error) { splitLine := strings.Split(line, " ") if len(splitLine) < 4 { return nil, errors.Errorf("'%s' is not a valid container log line", line) } - logTime, err := time.Parse(logTimeFormat, splitLine[0]) + logTime, err := time.Parse(LogTimeFormat, splitLine[0]) if err != nil { return nil, errors.Wrapf(err, "unable to convert time %s from container log", splitLine[0]) } @@ -222,7 +156,7 @@ func newLogLine(line string) (*LogLine, error) { // Partial returns a bool if the log line is a partial log type func (l *LogLine) Partial() bool { - if l.ParseLogType == partialLogType { + if l.ParseLogType == PartialLogType { return true } return false diff --git a/libpod/oci.go b/libpod/oci.go index 343738a3a..efb5e42cc 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -217,7 +217,7 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container, useRuntime bool) erro // If not using the OCI runtime, we don't need to do most of this. if !useRuntime { // If the container's not running, nothing to do. - if ctr.state.State != ContainerStateRunning && ctr.state.State != ContainerStatePaused { + if ctr.state.State != define.ContainerStateRunning && ctr.state.State != define.ContainerStatePaused { return nil } @@ -233,7 +233,7 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container, useRuntime bool) erro } // Alright, it exists. Transition to Stopped state. - ctr.state.State = ContainerStateStopped + ctr.state.State = define.ContainerStateStopped // Read the exit file to get our stopped time and exit code. return ctr.handleExitFile(exitFile, info) @@ -264,7 +264,7 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container, useRuntime bool) erro ctr.removeConmonFiles() ctr.state.ExitCode = -1 ctr.state.FinishedTime = time.Now() - ctr.state.State = ContainerStateExited + ctr.state.State = define.ContainerStateExited return nil } return errors.Wrapf(err, "error getting container %s state. stderr/out: %s", ctr.ID(), out) @@ -283,13 +283,13 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container, useRuntime bool) erro switch state.Status { case "created": - ctr.state.State = ContainerStateCreated + ctr.state.State = define.ContainerStateCreated case "paused": - ctr.state.State = ContainerStatePaused + ctr.state.State = define.ContainerStatePaused case "running": - ctr.state.State = ContainerStateRunning + ctr.state.State = define.ContainerStateRunning case "stopped": - ctr.state.State = ContainerStateStopped + ctr.state.State = define.ContainerStateStopped default: return errors.Wrapf(define.ErrInternal, "unrecognized status returned by runtime for container %s: %s", ctr.ID(), state.Status) @@ -297,7 +297,7 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container, useRuntime bool) erro // Only grab exit status if we were not already stopped // If we were, it should already be in the database - if ctr.state.State == ContainerStateStopped && oldState != ContainerStateStopped { + if ctr.state.State == define.ContainerStateStopped && oldState != define.ContainerStateStopped { var fi os.FileInfo chWait := make(chan error) defer close(chWait) diff --git a/libpod/pod_api.go b/libpod/pod_api.go index 3126ced4c..c7b0353bd 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -113,7 +113,7 @@ func (p *Pod) StopWithTimeout(ctx context.Context, cleanup bool, timeout int) (m } // Ignore containers that are not running - if ctr.state.State != ContainerStateRunning { + if ctr.state.State != define.ContainerStateRunning { ctr.lock.Unlock() continue } @@ -181,7 +181,7 @@ func (p *Pod) Pause() (map[string]error, error) { } // Ignore containers that are not running - if ctr.state.State != ContainerStateRunning { + if ctr.state.State != define.ContainerStateRunning { ctr.lock.Unlock() continue } @@ -240,7 +240,7 @@ func (p *Pod) Unpause() (map[string]error, error) { } // Ignore containers that are not paused - if ctr.state.State != ContainerStatePaused { + if ctr.state.State != define.ContainerStatePaused { ctr.lock.Unlock() continue } @@ -353,7 +353,7 @@ func (p *Pod) Kill(signal uint) (map[string]error, error) { } // Ignore containers that are not running - if ctr.state.State != ContainerStateRunning { + if ctr.state.State != define.ContainerStateRunning { ctr.lock.Unlock() continue } @@ -383,7 +383,7 @@ func (p *Pod) Kill(signal uint) (map[string]error, error) { // Status gets the status of all containers in the pod // Returns a map of Container ID to Container Status -func (p *Pod) Status() (map[string]ContainerStatus, error) { +func (p *Pod) Status() (map[string]define.ContainerStatus, error) { p.lock.Lock() defer p.lock.Unlock() @@ -403,7 +403,7 @@ func (p *Pod) Status() (map[string]ContainerStatus, error) { } // Now that all containers are locked, get their status - status := make(map[string]ContainerStatus, len(allCtrs)) + status := make(map[string]define.ContainerStatus, len(allCtrs)) for _, ctr := range allCtrs { if err := ctr.syncContainer(); err != nil { return nil, err diff --git a/libpod/pod_top_linux.go b/libpod/pod_top_linux.go index e08e5e83a..80221c3a9 100644 --- a/libpod/pod_top_linux.go +++ b/libpod/pod_top_linux.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/rootless" "github.com/containers/psgo" ) @@ -34,7 +35,7 @@ func (p *Pod) GetPodPidInformation(descriptors []string) ([]string, error) { c.lock.Unlock() return nil, err } - if c.state.State == ContainerStateRunning { + if c.state.State == define.ContainerStateRunning { pid := strconv.Itoa(c.state.PID) pids = append(pids, pid) } diff --git a/libpod/runtime.go b/libpod/runtime.go index 152af031a..5a618f592 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -10,6 +10,7 @@ import ( "strings" "sync" "syscall" + "time" "github.com/BurntSushi/toml" is "github.com/containers/image/storage" @@ -73,9 +74,8 @@ var ( OverrideConfigPath = etcDir + "/containers/libpod.conf" // DefaultInfraImage to use for infra container - DefaultInfraImage = "k8s.gcr.io/pause:3.1" + // DefaultInfraCommand to be run in an infra container - DefaultInfraCommand = "/pause" // DefaultSHMLockPath is the default path for SHM locks DefaultSHMLockPath = "/libpod_lock" @@ -285,14 +285,11 @@ func defaultRuntimeConfig() (RuntimeConfig, error) { }, ConmonPath: []string{ "/usr/libexec/podman/conmon", - "/usr/libexec/crio/conmon", "/usr/local/lib/podman/conmon", - "/usr/local/libexec/crio/conmon", "/usr/bin/conmon", "/usr/sbin/conmon", "/usr/local/bin/conmon", "/usr/local/sbin/conmon", - "/usr/lib/crio/bin/conmon", }, ConmonEnvVars: []string{ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", @@ -305,8 +302,8 @@ func defaultRuntimeConfig() (RuntimeConfig, error) { NoPivotRoot: false, CNIConfigDir: etcDir + "/cni/net.d/", CNIPluginDir: []string{"/usr/libexec/cni", "/usr/lib/cni", "/usr/local/lib/cni", "/opt/cni/bin"}, - InfraCommand: DefaultInfraCommand, - InfraImage: DefaultInfraImage, + InfraCommand: define.DefaultInfraCommand, + InfraImage: define.DefaultInfraImage, EnablePortReservation: true, EnableLabeling: true, NumLocks: 2048, @@ -314,6 +311,46 @@ func defaultRuntimeConfig() (RuntimeConfig, error) { }, nil } +// SetXdgRuntimeDir ensures the XDG_RUNTIME_DIR env variable is set +// containers/image uses XDG_RUNTIME_DIR to locate the auth file. +// It internally calls EnableLinger() so that the user's processes are not +// killed once the session is terminated. EnableLinger() also attempts to +// get the runtime directory when XDG_RUNTIME_DIR is not specified. +func SetXdgRuntimeDir() error { + if !rootless.IsRootless() { + return nil + } + + runtimeDir := os.Getenv("XDG_RUNTIME_DIR") + + runtimeDirLinger, err := rootless.EnableLinger() + if err != nil { + return errors.Wrapf(err, "error enabling user session") + } + if runtimeDir == "" && runtimeDirLinger != "" { + if _, err := os.Stat(runtimeDirLinger); err != nil && os.IsNotExist(err) { + chWait := make(chan error) + defer close(chWait) + if _, err := WaitForFile(runtimeDirLinger, chWait, time.Second*10); err != nil { + return errors.Wrapf(err, "waiting for directory '%s'", runtimeDirLinger) + } + } + runtimeDir = runtimeDirLinger + } + + if runtimeDir == "" { + var err error + runtimeDir, err = util.GetRootlessRuntimeDir() + if err != nil { + return err + } + } + if err := os.Setenv("XDG_RUNTIME_DIR", runtimeDir); err != nil { + return errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR") + } + return nil +} + func getDefaultTmpDir() (string, error) { if !rootless.IsRootless() { return "/var/run/libpod", nil @@ -336,25 +373,6 @@ func getDefaultTmpDir() (string, error) { return filepath.Join(libpodRuntimeDir, "tmp"), nil } -// SetXdgRuntimeDir ensures the XDG_RUNTIME_DIR env variable is set -// containers/image uses XDG_RUNTIME_DIR to locate the auth file. -func SetXdgRuntimeDir(val string) error { - if !rootless.IsRootless() { - return nil - } - if val == "" { - var err error - val, err = util.GetRootlessRuntimeDir() - if err != nil { - return err - } - } - if err := os.Setenv("XDG_RUNTIME_DIR", val); err != nil { - return errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR") - } - return nil -} - // NewRuntime creates a new container runtime // Options can be passed to override the default configuration for the runtime func NewRuntime(ctx context.Context, options ...RuntimeOption) (runtime *Runtime, err error) { @@ -376,7 +394,7 @@ func NewRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. func homeDir() (string, error) { home := os.Getenv("HOME") if home == "" { - usr, err := user.Current() + usr, err := user.LookupId(fmt.Sprintf("%d", rootless.GetRootlessUID())) if err != nil { return "", errors.Wrapf(err, "unable to resolve HOME directory") } @@ -394,28 +412,33 @@ func getRootlessConfigPath() (string, error) { return filepath.Join(home, ".config/containers/libpod.conf"), nil } -func getConfigPath() string { +func getConfigPath() (string, error) { if rootless.IsRootless() { - rootlessConfigPath, err := getRootlessConfigPath() + path, err := getRootlessConfigPath() if err != nil { - if _, err := os.Stat(rootlessConfigPath); err == nil { - return rootlessConfigPath - } + return "", err + } + if _, err := os.Stat(path); err == nil { + return path, nil } + return "", err } if _, err := os.Stat(OverrideConfigPath); err == nil { // Use the override configuration path - return OverrideConfigPath + return OverrideConfigPath, nil } if _, err := os.Stat(ConfigPath); err == nil { - return ConfigPath + return ConfigPath, nil } - return "" + return "", nil } // DefaultRuntimeConfig reads default config path and returns the RuntimeConfig func DefaultRuntimeConfig() (*RuntimeConfig, error) { - configPath := getConfigPath() + configPath, err := getConfigPath() + if err != nil { + return nil, err + } contents, err := ioutil.ReadFile(configPath) if err != nil { @@ -463,8 +486,10 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. runtime.config.StaticDir = filepath.Join(storageConf.GraphRoot, "libpod") runtime.config.VolumePath = filepath.Join(storageConf.GraphRoot, "volumes") - configPath := getConfigPath() - rootlessConfigPath := "" + configPath, err := getConfigPath() + if err != nil { + return nil, err + } if rootless.IsRootless() { home, err := homeDir() if err != nil { @@ -476,23 +501,6 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. runtime.config.SignaturePolicyPath = newPath } } - - rootlessConfigPath, err = getRootlessConfigPath() - if err != nil { - return nil, err - } - - runtimeDir, err := util.GetRootlessRuntimeDir() - if err != nil { - return nil, err - } - - // containers/image uses XDG_RUNTIME_DIR to locate the auth file. - // So make sure the env variable is set. - if err := SetXdgRuntimeDir(runtimeDir); err != nil { - return nil, errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR") - } - } if userConfigPath != "" { @@ -602,7 +610,13 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. return nil, errors.Wrapf(err, "error configuring runtime") } } - if rootlessConfigPath != "" { + + if rootless.IsRootless() && configPath == "" { + configPath, err := getRootlessConfigPath() + if err != nil { + return nil, err + } + // storage.conf storageConfFile, err := storage.DefaultConfigFile(rootless.IsRootless()) if err != nil { @@ -615,16 +629,16 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. } if configPath != "" { - os.MkdirAll(filepath.Dir(rootlessConfigPath), 0755) - file, err := os.OpenFile(rootlessConfigPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) + os.MkdirAll(filepath.Dir(configPath), 0755) + file, err := os.OpenFile(configPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) if err != nil && !os.IsExist(err) { - return nil, errors.Wrapf(err, "cannot open file %s", rootlessConfigPath) + return nil, errors.Wrapf(err, "cannot open file %s", configPath) } if err == nil { defer file.Close() enc := toml.NewEncoder(file) if err := enc.Encode(runtime.config); err != nil { - os.Remove(rootlessConfigPath) + os.Remove(configPath) } } } @@ -1194,21 +1208,21 @@ func (r *Runtime) refresh(alivePath string) error { } // Info returns the store and host information -func (r *Runtime) Info() ([]InfoData, error) { - info := []InfoData{} +func (r *Runtime) Info() ([]define.InfoData, error) { + info := []define.InfoData{} // get host information hostInfo, err := r.hostInfo() if err != nil { return nil, errors.Wrapf(err, "error getting host info") } - info = append(info, InfoData{Type: "host", Data: hostInfo}) + info = append(info, define.InfoData{Type: "host", Data: hostInfo}) // get store information storeInfo, err := r.storeInfo() if err != nil { return nil, errors.Wrapf(err, "error getting store info") } - info = append(info, InfoData{Type: "store", Data: storeInfo}) + info = append(info, define.InfoData{Type: "store", Data: storeInfo}) reg, err := sysreg.GetRegistries() if err != nil { @@ -1228,7 +1242,7 @@ func (r *Runtime) Info() ([]InfoData, error) { return nil, errors.Wrapf(err, "error getting registries") } registries["blocked"] = breg - info = append(info, InfoData{Type: "registries", Data: registries}) + info = append(info, define.InfoData{Type: "registries", Data: registries}) return info, nil } diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 79e18dcd1..0d0f700a6 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -133,7 +133,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container, restore bo logrus.Debugf("Allocated lock %d for container %s", ctr.lock.ID(), ctr.ID()) ctr.valid = true - ctr.state.State = ContainerStateConfigured + ctr.state.State = config2.ContainerStateConfigured ctr.runtime = r if ctr.config.OCIRuntime == "" { @@ -370,7 +370,7 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool, } } - if c.state.State == ContainerStatePaused { + if c.state.State == config2.ContainerStatePaused { if err := c.ociRuntime.killContainer(c, 9); err != nil { return err } @@ -384,7 +384,7 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool, } // Check that the container's in a good state to be removed - if c.state.State == ContainerStateRunning { + if c.state.State == config2.ContainerStateRunning { if err := c.ociRuntime.stopContainer(c, c.StopTimeout()); err != nil { return errors.Wrapf(err, "cannot remove container %s as it could not be stopped", c.ID()) } @@ -464,8 +464,8 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool, // Delete the container. // Not needed in Configured and Exited states, where the container // doesn't exist in the runtime - if c.state.State != ContainerStateConfigured && - c.state.State != ContainerStateExited { + if c.state.State != config2.ContainerStateConfigured && + c.state.State != config2.ContainerStateExited { if err := c.delete(ctx); err != nil { if cleanupErr == nil { cleanupErr = err @@ -582,7 +582,7 @@ func (r *Runtime) GetAllContainers() ([]*Container, error) { func (r *Runtime) GetRunningContainers() ([]*Container, error) { running := func(c *Container) bool { state, _ := c.State() - return state == ContainerStateRunning + return state == config2.ContainerStateRunning } return r.GetContainers(running) } diff --git a/libpod/runtime_migrate.go b/libpod/runtime_migrate.go index e32e6edf6..ad45579d3 100644 --- a/libpod/runtime_migrate.go +++ b/libpod/runtime_migrate.go @@ -5,6 +5,7 @@ package libpod import ( "context" "fmt" + "github.com/containers/libpod/pkg/util" "io/ioutil" "os" "path/filepath" @@ -12,7 +13,6 @@ import ( "syscall" "github.com/containers/libpod/pkg/rootless" - "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) diff --git a/libpod/state_test.go b/libpod/state_test.go index be68a2d69..26a1dee7d 100644 --- a/libpod/state_test.go +++ b/libpod/state_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/lock" "github.com/containers/storage" "github.com/stretchr/testify/assert" @@ -700,7 +701,7 @@ func TestSaveAndUpdateContainer(t *testing.T) { retrievedCtr, err := state.Container(testCtr.ID()) require.NoError(t, err) - retrievedCtr.state.State = ContainerStateStopped + retrievedCtr.state.State = define.ContainerStateStopped retrievedCtr.state.ExitCode = 127 retrievedCtr.state.FinishedTime = time.Now() @@ -729,7 +730,7 @@ func TestSaveAndUpdateContainerSameNamespaceSucceeds(t *testing.T) { retrievedCtr, err := state.Container(testCtr.ID()) assert.NoError(t, err) - retrievedCtr.state.State = ContainerStateStopped + retrievedCtr.state.State = define.ContainerStateStopped retrievedCtr.state.ExitCode = 127 retrievedCtr.state.FinishedTime = time.Now() diff --git a/libpod/stats.go b/libpod/stats.go index e003f145b..da383e9d9 100644 --- a/libpod/stats.go +++ b/libpod/stats.go @@ -26,7 +26,7 @@ func (c *Container) GetContainerStats(previousStats *ContainerStats) (*Container } } - if c.state.State != ContainerStateRunning { + if c.state.State != define.ContainerStateRunning { return stats, define.ErrCtrStateInvalid } @@ -61,7 +61,7 @@ func (c *Container) GetContainerStats(previousStats *ContainerStats) (*Container stats.MemLimit = getMemLimit(cgroupStats.Memory.Usage.Limit) stats.MemPerc = (float64(stats.MemUsage) / float64(stats.MemLimit)) * 100 stats.PIDs = 0 - if conState == ContainerStateRunning { + if conState == define.ContainerStateRunning { stats.PIDs = cgroupStats.Pids.Current } stats.BlockInput, stats.BlockOutput = calculateBlockIO(cgroupStats) diff --git a/libpod/util.go b/libpod/util.go index 7b3a03785..b0c25074b 100644 --- a/libpod/util.go +++ b/libpod/util.go @@ -24,17 +24,6 @@ const ( DefaultTransport = "docker://" ) -// OpenExclusiveFile opens a file for writing and ensure it doesn't already exist -func OpenExclusiveFile(path string) (*os.File, error) { - baseDir := filepath.Dir(path) - if baseDir != "" { - if _, err := os.Stat(baseDir); err != nil { - return nil, err - } - } - return os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) -} - // FuncTimer helps measure the execution time of a function // For debug purposes, do not leave in code // used like defer FuncTimer("foo") |