mirror of https://github.com/containers/podman.git
				
				
				
			Merge pull request #1089 from mheon/add_exited
Record whether the container has exited
This commit is contained in:
		
						commit
						4729fd4255
					
				|  | @ -38,9 +38,10 @@ type BatchContainerStruct struct { | |||
| 	ConConfig   *libpod.ContainerConfig | ||||
| 	ConState    libpod.ContainerStatus | ||||
| 	ExitCode    int32 | ||||
| 	Exited      bool | ||||
| 	Pid         int | ||||
| 	RootFsSize, RwSize int64 | ||||
| 	StartedTime time.Time | ||||
| 	Size        *ContainerSize | ||||
| } | ||||
| 
 | ||||
| // Namespace describes output for ps namespace
 | ||||
|  | @ -55,6 +56,13 @@ type Namespace struct { | |||
| 	UTS    string `json:"uts,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // ContainerSize holds the size of the container's root filesystem and top
 | ||||
| // read-write layer
 | ||||
| type ContainerSize struct { | ||||
| 	RootFsSize int64 `json:"rootFsSize"` | ||||
| 	RwSize     int64 `json:"rwSize"` | ||||
| } | ||||
| 
 | ||||
| // BatchContainer is used in ps to reduce performance hits by "batching"
 | ||||
| // locks.
 | ||||
| func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStruct, error) { | ||||
|  | @ -63,8 +71,9 @@ func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStru | |||
| 		conState    libpod.ContainerStatus | ||||
| 		err         error | ||||
| 		exitCode    int32 | ||||
| 		exited      bool | ||||
| 		pid         int | ||||
| 		rootFsSize, rwSize int64 | ||||
| 		size        *ContainerSize | ||||
| 		startedTime time.Time | ||||
| 	) | ||||
| 
 | ||||
|  | @ -75,7 +84,7 @@ func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStru | |||
| 			return errors.Wrapf(err, "unable to obtain container state") | ||||
| 		} | ||||
| 
 | ||||
| 		exitCode, err = c.ExitCode() | ||||
| 		exitCode, exited, err = c.ExitCode() | ||||
| 		if err != nil { | ||||
| 			return errors.Wrapf(err, "unable to obtain container exit code") | ||||
| 		} | ||||
|  | @ -95,16 +104,20 @@ func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStru | |||
| 			} | ||||
| 		} | ||||
| 		if opts.Size { | ||||
| 			rootFsSize, err = c.RootFsSize() | ||||
| 			size = new(ContainerSize) | ||||
| 
 | ||||
| 			rootFsSize, err := c.RootFsSize() | ||||
| 			if err != nil { | ||||
| 				logrus.Errorf("error getting root fs size for %q: %v", c.ID(), err) | ||||
| 			} | ||||
| 
 | ||||
| 			rwSize, err = c.RWSize() | ||||
| 			rwSize, err := c.RWSize() | ||||
| 			if err != nil { | ||||
| 				logrus.Errorf("error getting rw size for %q: %v", c.ID(), err) | ||||
| 			} | ||||
| 
 | ||||
| 			size.RootFsSize = rootFsSize | ||||
| 			size.RwSize = rwSize | ||||
| 		} | ||||
| 		return nil | ||||
| 	}) | ||||
|  | @ -115,10 +128,10 @@ func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStru | |||
| 		ConConfig:   conConfig, | ||||
| 		ConState:    conState, | ||||
| 		ExitCode:    exitCode, | ||||
| 		Exited:      exited, | ||||
| 		Pid:         pid, | ||||
| 		RootFsSize:  rootFsSize, | ||||
| 		RwSize:      rwSize, | ||||
| 		StartedTime: startedTime, | ||||
| 		Size:        size, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -58,12 +58,12 @@ type psJSONParams struct { | |||
| 	Command          []string                      `json:"command"` | ||||
| 	CreatedAt        time.Time                     `json:"createdAt"` | ||||
| 	ExitCode         int32                         `json:"exitCode"` | ||||
| 	Exited           bool                          `json:"exited"` | ||||
| 	RunningFor       time.Duration                 `json:"runningFor"` | ||||
| 	Status           string                        `json:"status"` | ||||
| 	PID              int                           `json:"PID"` | ||||
| 	Ports            []ocicni.PortMapping          `json:"ports"` | ||||
| 	RootFsSize       int64                     `json:"rootFsSize"` | ||||
| 	RWSize           int64                     `json:"rwSize"` | ||||
| 	Size             *batchcontainer.ContainerSize `json:"size,omitempty"` | ||||
| 	Names            string                        `json:"names"` | ||||
| 	Labels           fields.Set                    `json:"labels"` | ||||
| 	Mounts           []string                      `json:"mounts"` | ||||
|  | @ -114,7 +114,10 @@ func (a psSortedStatus) Less(i, j int) bool { return a.psSorted[i].Status < a.ps | |||
| type psSortedSize struct{ psSorted } | ||||
| 
 | ||||
| func (a psSortedSize) Less(i, j int) bool { | ||||
| 	return a.psSorted[i].RootFsSize < a.psSorted[j].RootFsSize | ||||
| 	if a.psSorted[i].Size == nil || a.psSorted[j].Size == nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	return a.psSorted[i].Size.RootFsSize < a.psSorted[j].Size.RootFsSize | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
|  | @ -279,22 +282,16 @@ func checkFlagsPassed(c *cli.Context) error { | |||
| 	if c.Int("last") >= 0 && c.Bool("latest") { | ||||
| 		return errors.Errorf("last and latest are mutually exclusive") | ||||
| 	} | ||||
| 	// quiet, size, namespace, and format with Go template are mutually exclusive
 | ||||
| 	flags := 0 | ||||
| 	// Quiet conflicts with size, namespace, and format with a Go template
 | ||||
| 	if c.Bool("quiet") { | ||||
| 		flags++ | ||||
| 		if c.Bool("size") || c.Bool("namespace") || (c.IsSet("format") && | ||||
| 			c.String("format") != formats.JSONString) { | ||||
| 			return errors.Errorf("quiet conflicts with size, namespace, and format with go template") | ||||
| 		} | ||||
| 	if c.Bool("size") { | ||||
| 		flags++ | ||||
| 	} | ||||
| 	if c.Bool("namespace") { | ||||
| 		flags++ | ||||
| 	} | ||||
| 	if c.IsSet("format") && c.String("format") != formats.JSONString { | ||||
| 		flags++ | ||||
| 	} | ||||
| 	if flags > 1 { | ||||
| 		return errors.Errorf("quiet, size, namespace, and format with Go template are mutually exclusive") | ||||
| 	// Size and namespace conflict with each other
 | ||||
| 	if c.Bool("size") && c.Bool("namespace") { | ||||
| 		return errors.Errorf("size and namespace options conflict") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | @ -324,8 +321,8 @@ func generateContainerFilterFuncs(filter, filterValue string, runtime *libpod.Ru | |||
| 			return nil, errors.Wrapf(err, "exited code out of range %q", filterValue) | ||||
| 		} | ||||
| 		return func(c *libpod.Container) bool { | ||||
| 			ec, err := c.ExitCode() | ||||
| 			if ec == int32(exitCode) && err == nil { | ||||
| 			ec, exited, err := c.ExitCode() | ||||
| 			if ec == int32(exitCode) && err == nil && exited == true { | ||||
| 				return true | ||||
| 			} | ||||
| 			return false | ||||
|  | @ -496,7 +493,10 @@ func getTemplateOutput(psParams []psJSONParams, opts batchcontainer.PsOptions) ( | |||
| 			ns = psParam.Namespaces | ||||
| 		} | ||||
| 		if opts.Size { | ||||
| 			size = units.HumanSizeWithPrecision(float64(psParam.RWSize), 3) + " (virtual " + units.HumanSizeWithPrecision(float64(psParam.RootFsSize), 3) + ")" | ||||
| 			if psParam.Size == nil { | ||||
| 				return nil, errors.Errorf("Container %s does not have a size struct", psParam.ID) | ||||
| 			} | ||||
| 			size = units.HumanSizeWithPrecision(float64(psParam.Size.RwSize), 3) + " (virtual " + units.HumanSizeWithPrecision(float64(psParam.Size.RootFsSize), 3) + ")" | ||||
| 		} | ||||
| 		runningFor := units.HumanDuration(psParam.RunningFor) | ||||
| 
 | ||||
|  | @ -581,17 +581,20 @@ func getAndSortJSONParams(containers []*libpod.Container, opts batchcontainer.Ps | |||
| 			ImageID:          batchInfo.ConConfig.RootfsImageID, | ||||
| 			Command:          batchInfo.ConConfig.Spec.Process.Args, | ||||
| 			CreatedAt:        batchInfo.ConConfig.CreatedTime, | ||||
| 			ExitCode:         batchInfo.ExitCode, | ||||
| 			Exited:           batchInfo.Exited, | ||||
| 			Status:           batchInfo.ConState.String(), | ||||
| 			PID:              batchInfo.Pid, | ||||
| 			Ports:            batchInfo.ConConfig.PortMappings, | ||||
| 			RootFsSize: batchInfo.RootFsSize, | ||||
| 			RWSize:     batchInfo.RwSize, | ||||
| 			Size:             batchInfo.Size, | ||||
| 			Names:            batchInfo.ConConfig.Name, | ||||
| 			Labels:           batchInfo.ConConfig.Labels, | ||||
| 			Mounts:           batchInfo.ConConfig.UserVolumes, | ||||
| 			ContainerRunning: batchInfo.ConState == libpod.ContainerStateRunning, | ||||
| 			Namespaces:       ns, | ||||
| 		} | ||||
| 
 | ||||
| 		if !batchInfo.StartedTime.IsZero() { | ||||
| 		if !batchInfo.StartedTime.IsZero() && batchInfo.ConState == libpod.ContainerStateRunning { | ||||
| 			params.RunningFor = time.Since(batchInfo.StartedTime) | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
|  | @ -135,6 +135,8 @@ type containerState struct { | |||
| 	FinishedTime time.Time `json:"finishedTime,omitempty"` | ||||
| 	// ExitCode is the exit code returned when the container stopped
 | ||||
| 	ExitCode int32 `json:"exitCode,omitempty"` | ||||
| 	// Exited is whether the container has exited
 | ||||
| 	Exited bool `json:"exited,omitempty"` | ||||
| 	// OOMKilled indicates that the container was killed as it ran out of
 | ||||
| 	// memory
 | ||||
| 	OOMKilled bool `json:"oomKilled,omitempty"` | ||||
|  | @ -667,16 +669,18 @@ func (c *Container) FinishedTime() (time.Time, error) { | |||
| } | ||||
| 
 | ||||
| // ExitCode returns the exit code of the container as
 | ||||
| // an int32
 | ||||
| func (c *Container) ExitCode() (int32, error) { | ||||
| // an int32, and whether the container has exited.
 | ||||
| // If the container has not exited, exit code will always be 0.
 | ||||
| // If the container restarts, the exit code is reset to 0.
 | ||||
| func (c *Container) ExitCode() (int32, bool, error) { | ||||
| 	if !c.batched { | ||||
| 		c.lock.Lock() | ||||
| 		defer c.lock.Unlock() | ||||
| 		if err := c.syncContainer(); err != nil { | ||||
| 			return 0, errors.Wrapf(err, "error updating container %s state", c.ID()) | ||||
| 			return 0, false, errors.Wrapf(err, "error updating container %s state", c.ID()) | ||||
| 		} | ||||
| 	} | ||||
| 	return c.state.ExitCode, nil | ||||
| 	return c.state.ExitCode, c.state.Exited, nil | ||||
| } | ||||
| 
 | ||||
| // OOMKilled returns whether the container was killed by an OOM condition
 | ||||
|  |  | |||
|  | @ -586,6 +586,8 @@ func (c *Container) reinit(ctx context.Context) error { | |||
| 	// Set and save now to make sure that, if the init() below fails
 | ||||
| 	// we still have a valid state
 | ||||
| 	c.state.State = ContainerStateConfigured | ||||
| 	c.state.ExitCode = 0 | ||||
| 	c.state.Exited = false | ||||
| 	if err := c.save(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  |  | |||
|  | @ -450,6 +450,7 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container) error { | |||
| 			ctr.state.OOMKilled = true | ||||
| 		} | ||||
| 
 | ||||
| 		ctr.state.Exited = true | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
|  |  | |||
|  | @ -65,13 +65,15 @@ func makeListContainer(containerID string, batchInfo batchcontainer.BatchContain | |||
| 		Runningfor:       time.Since(batchInfo.ConConfig.CreatedTime).String(), | ||||
| 		Status:           batchInfo.ConState.String(), | ||||
| 		Ports:            ports, | ||||
| 		Rootfssize:       batchInfo.RootFsSize, | ||||
| 		Rwsize:           batchInfo.RwSize, | ||||
| 		Names:            batchInfo.ConConfig.Name, | ||||
| 		Labels:           batchInfo.ConConfig.Labels, | ||||
| 		Mounts:           mounts, | ||||
| 		Containerrunning: batchInfo.ConState == libpod.ContainerStateRunning, | ||||
| 		Namespaces:       namespace, | ||||
| 	} | ||||
| 	if batchInfo.Size != nil { | ||||
| 		lc.Rootfssize = batchInfo.Size.RootFsSize | ||||
| 		lc.Rwsize = batchInfo.Size.RwSize | ||||
| 	} | ||||
| 	return lc | ||||
| } | ||||
|  |  | |||
|  | @ -201,12 +201,15 @@ var _ = Describe("Podman ps", func() { | |||
| 		session.WaitWithDefaultTimeout() | ||||
| 		Expect(session.ExitCode()).To(Equal(0)) | ||||
| 
 | ||||
| 		session = podmanTest.Podman([]string{"ps", "-a", "--sort=size", "--format", "{{.Size}}"}) | ||||
| 		session = podmanTest.Podman([]string{"ps", "-a", "-s", "--sort=size", "--format", "{{.Size}}"}) | ||||
| 		session.WaitWithDefaultTimeout() | ||||
| 		Expect(session.ExitCode()).To(Equal(0)) | ||||
| 
 | ||||
| 		sortedArr := session.OutputToStringArray() | ||||
| 
 | ||||
| 		// TODO: This may be broken - the test was running without the
 | ||||
| 		// ability to perform any sorting for months and succeeded
 | ||||
| 		// without error.
 | ||||
| 		Expect(sort.SliceIsSorted(sortedArr, func(i, j int) bool { | ||||
| 			r := regexp.MustCompile(`^\S+\s+\(virtual (\S+)\)`) | ||||
| 			matches1 := r.FindStringSubmatch(sortedArr[i]) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue