summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container.go9
-rw-r--r--libpod/container_config.go4
-rw-r--r--libpod/container_inspect.go39
-rw-r--r--libpod/container_internal.go12
-rw-r--r--libpod/container_internal_linux.go210
-rw-r--r--libpod/define/annotations.go12
-rw-r--r--libpod/define/container_inspect.go40
-rw-r--r--libpod/healthcheck_linux.go10
-rw-r--r--libpod/kube.go2
-rw-r--r--libpod/network/internal/util/util.go2
-rw-r--r--libpod/options.go22
-rw-r--r--libpod/runtime_img.go17
12 files changed, 302 insertions, 77 deletions
diff --git a/libpod/container.go b/libpod/container.go
index 1270f2112..c746f97c7 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -213,6 +213,15 @@ type ContainerState struct {
// containerPlatformState holds platform-specific container state.
containerPlatformState
+
+ // Following checkpoint/restore related information is displayed
+ // if the container has been checkpointed or restored.
+ CheckpointedTime time.Time `json:"checkpointedTime,omitempty"`
+ RestoredTime time.Time `json:"restoredTime,omitempty"`
+ CheckpointLog string `json:"checkpointLog,omitempty"`
+ CheckpointPath string `json:"checkpointPath,omitempty"`
+ RestoreLog string `json:"restoreLog,omitempty"`
+ Restored bool `json:"restored,omitempty"`
}
// ContainerNamedVolume is a named volume that will be mounted into the
diff --git a/libpod/container_config.go b/libpod/container_config.go
index adc585fa1..a43fd632b 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -163,6 +163,8 @@ type ContainerRootFSConfig struct {
// Volatile specifies whether the container storage can be optimized
// at the cost of not syncing all the dirty files in memory.
Volatile bool `json:"volatile,omitempty"`
+ // Passwd allows to user to override podman's passwd/group file setup
+ Passwd *bool `json:"passwd,omitempty"`
}
// ContainerSecurityConfig is an embedded sub-config providing security configuration
@@ -196,6 +198,8 @@ type ContainerSecurityConfig struct {
// Groups are additional groups to add the container's user to. These
// are resolved within the container using the container's /etc/passwd.
Groups []string `json:"groups,omitempty"`
+ // HostUsers are a list of host user accounts to add to /etc/passwd
+ HostUsers []string `json:"HostUsers,omitempty"`
// AddCurrentUserPasswdEntry indicates that Libpod should ensure that
// the container's /etc/passwd contains an entry for the user running
// Libpod - mostly used in rootless containers where the user running
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index 83b643266..f72700ab6 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -113,20 +113,26 @@ func (c *Container) getContainerInspectData(size bool, driverData *define.Driver
Path: path,
Args: args,
State: &define.InspectContainerState{
- OciVersion: ctrSpec.Version,
- Status: runtimeInfo.State.String(),
- Running: runtimeInfo.State == define.ContainerStateRunning,
- Paused: runtimeInfo.State == define.ContainerStatePaused,
- OOMKilled: runtimeInfo.OOMKilled,
- Dead: runtimeInfo.State.String() == "bad state",
- Pid: runtimeInfo.PID,
- ConmonPid: runtimeInfo.ConmonPID,
- ExitCode: runtimeInfo.ExitCode,
- Error: "", // can't get yet
- StartedAt: runtimeInfo.StartedTime,
- FinishedAt: runtimeInfo.FinishedTime,
- Checkpointed: runtimeInfo.Checkpointed,
- CgroupPath: cgroupPath,
+ OciVersion: ctrSpec.Version,
+ Status: runtimeInfo.State.String(),
+ Running: runtimeInfo.State == define.ContainerStateRunning,
+ Paused: runtimeInfo.State == define.ContainerStatePaused,
+ OOMKilled: runtimeInfo.OOMKilled,
+ Dead: runtimeInfo.State.String() == "bad state",
+ Pid: runtimeInfo.PID,
+ ConmonPid: runtimeInfo.ConmonPID,
+ ExitCode: runtimeInfo.ExitCode,
+ Error: "", // can't get yet
+ StartedAt: runtimeInfo.StartedTime,
+ FinishedAt: runtimeInfo.FinishedTime,
+ Checkpointed: runtimeInfo.Checkpointed,
+ CgroupPath: cgroupPath,
+ RestoredAt: runtimeInfo.RestoredTime,
+ CheckpointedAt: runtimeInfo.CheckpointedTime,
+ Restored: runtimeInfo.Restored,
+ CheckpointPath: runtimeInfo.CheckpointPath,
+ CheckpointLog: runtimeInfo.CheckpointLog,
+ RestoreLog: runtimeInfo.RestoreLog,
},
Image: config.RootfsImageID,
ImageName: config.RootfsImageName,
@@ -371,6 +377,8 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.Insp
ctrConfig.Umask = c.config.Umask
}
+ ctrConfig.Passwd = c.config.Passwd
+
return ctrConfig
}
@@ -485,9 +493,6 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
if ctrSpec.Linux.Resources.Memory.Limit != nil {
hostConfig.Memory = *ctrSpec.Linux.Resources.Memory.Limit
}
- if ctrSpec.Linux.Resources.Memory.Kernel != nil {
- hostConfig.KernelMemory = *ctrSpec.Linux.Resources.Memory.Kernel
- }
if ctrSpec.Linux.Resources.Memory.Reservation != nil {
hostConfig.MemoryReservation = *ctrSpec.Linux.Resources.Memory.Reservation
}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 7df82eb18..7ae9daefa 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -634,6 +634,12 @@ func resetState(state *ContainerState) {
state.RestartPolicyMatch = false
state.RestartCount = 0
state.Checkpointed = false
+ state.Restored = false
+ state.CheckpointedTime = time.Time{}
+ state.RestoredTime = time.Time{}
+ state.CheckpointPath = ""
+ state.CheckpointLog = ""
+ state.RestoreLog = ""
}
// Refresh refreshes the container's state after a restart.
@@ -1111,6 +1117,12 @@ func (c *Container) init(ctx context.Context, retainRetries bool) error {
}
c.state.Checkpointed = false
+ c.state.Restored = false
+ c.state.CheckpointedTime = time.Time{}
+ c.state.RestoredTime = time.Time{}
+ c.state.CheckpointPath = ""
+ c.state.CheckpointLog = ""
+ c.state.RestoreLog = ""
c.state.ExitCode = 0
c.state.Exited = false
c.state.State = define.ContainerStateCreated
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index f4b629a83..7745646b6 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -305,13 +305,40 @@ func (c *Container) getUserOverrides() *lookup.Overrides {
return &overrides
}
+func lookupHostUser(name string) (*runcuser.ExecUser, error) {
+ var execUser runcuser.ExecUser
+ // Lookup User on host
+ u, err := util.LookupUser(name)
+ if err != nil {
+ return &execUser, err
+ }
+ uid, err := strconv.ParseUint(u.Uid, 8, 32)
+ if err != nil {
+ return &execUser, err
+ }
+
+ gid, err := strconv.ParseUint(u.Gid, 8, 32)
+ if err != nil {
+ return &execUser, err
+ }
+ execUser.Uid = int(uid)
+ execUser.Gid = int(gid)
+ execUser.Home = u.HomeDir
+ return &execUser, nil
+}
+
// Generate spec for a container
// Accepts a map of the container's dependencies
func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
overrides := c.getUserOverrides()
execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, c.config.User, overrides)
if err != nil {
- return nil, err
+ if util.StringInSlice(c.config.User, c.config.HostUsers) {
+ execUser, err = lookupHostUser(c.config.User)
+ }
+ if err != nil {
+ return nil, err
+ }
}
g := generate.NewFromSpec(c.config.Spec)
@@ -990,6 +1017,7 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error {
includeFiles := []string{
"artifacts",
+ metadata.DevShmCheckpointTar,
metadata.ConfigDumpFile,
metadata.SpecDumpFile,
metadata.NetworkStatusFile,
@@ -1134,11 +1162,38 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
return nil, 0, err
}
+ // Setting CheckpointLog early in case there is a failure.
+ c.state.CheckpointLog = path.Join(c.bundlePath(), "dump.log")
+ c.state.CheckpointPath = c.CheckpointPath()
+
runtimeCheckpointDuration, err := c.ociRuntime.CheckpointContainer(c, options)
if err != nil {
return nil, 0, err
}
+ // Keep the content of /dev/shm directory
+ if c.config.ShmDir != "" && c.state.BindMounts["/dev/shm"] == c.config.ShmDir {
+ shmDirTarFileFullPath := filepath.Join(c.bundlePath(), metadata.DevShmCheckpointTar)
+
+ shmDirTarFile, err := os.Create(shmDirTarFileFullPath)
+ if err != nil {
+ return nil, 0, err
+ }
+ defer shmDirTarFile.Close()
+
+ input, err := archive.TarWithOptions(c.config.ShmDir, &archive.TarOptions{
+ Compression: archive.Uncompressed,
+ IncludeSourceDir: true,
+ })
+ if err != nil {
+ return nil, 0, err
+ }
+
+ if _, err = io.Copy(shmDirTarFile, input); err != nil {
+ return nil, 0, err
+ }
+ }
+
// Save network.status. This is needed to restore the container with
// the same IP. Currently limited to one IP address in a container
// with one interface.
@@ -1169,6 +1224,9 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
if !options.KeepRunning && !options.PreCheckPoint {
c.state.State = define.ContainerStateStopped
c.state.Checkpointed = true
+ c.state.CheckpointedTime = time.Now()
+ c.state.Restored = false
+ c.state.RestoredTime = time.Time{}
// Cleanup Storage and Network
if err := c.cleanup(ctx); err != nil {
@@ -1216,6 +1274,8 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
logrus.Debugf("Unable to remove file %s", file)
}
}
+ // The file has been deleted. Do not mention it.
+ c.state.CheckpointLog = ""
}
c.state.FinishedTime = time.Now()
@@ -1293,6 +1353,10 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
return nil, 0, err
}
+ // Setting RestoreLog early in case there is a failure.
+ c.state.RestoreLog = path.Join(c.bundlePath(), "restore.log")
+ c.state.CheckpointPath = c.CheckpointPath()
+
// Read network configuration from checkpoint
var netStatus map[string]types.StatusBlock
_, err := metadata.ReadJSONFile(&netStatus, c.bundlePath(), metadata.NetworkStatusFile)
@@ -1473,6 +1537,24 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
}
}
+ // Restore /dev/shm content
+ if c.config.ShmDir != "" && c.state.BindMounts["/dev/shm"] == c.config.ShmDir {
+ shmDirTarFileFullPath := filepath.Join(c.bundlePath(), metadata.DevShmCheckpointTar)
+ if _, err := os.Stat(shmDirTarFileFullPath); err != nil {
+ logrus.Debug("Container checkpoint doesn't contain dev/shm: ", err.Error())
+ } else {
+ shmDirTarFile, err := os.Open(shmDirTarFileFullPath)
+ if err != nil {
+ return nil, 0, err
+ }
+ defer shmDirTarFile.Close()
+
+ if err := archive.UntarUncompressed(shmDirTarFile, c.config.ShmDir, nil); err != nil {
+ return nil, 0, err
+ }
+ }
+ }
+
// Cleanup for a working restore.
if err := c.removeConmonFiles(); err != nil {
return nil, 0, err
@@ -1559,6 +1641,9 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
c.state.State = define.ContainerStateRunning
c.state.Checkpointed = false
+ c.state.Restored = true
+ c.state.CheckpointedTime = time.Time{}
+ c.state.RestoredTime = time.Now()
if !options.Keep {
// Delete all checkpoint related files. At this point, in theory, all files
@@ -1569,6 +1654,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
if err != nil {
logrus.Debugf("Non-fatal: removal of checkpoint directory (%s) failed: %v", c.CheckpointPath(), err)
}
+ c.state.CheckpointPath = ""
err = os.RemoveAll(c.PreCheckPointPath())
if err != nil {
logrus.Debugf("Non-fatal: removal of pre-checkpoint directory (%s) failed: %v", c.PreCheckPointPath(), err)
@@ -1589,6 +1675,8 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
logrus.Debugf("Non-fatal: removal of checkpoint file (%s) failed: %v", file, err)
}
}
+ c.state.CheckpointLog = ""
+ c.state.RestoreLog = ""
}
return criuStatistics, runtimeRestoreDuration, c.save()
@@ -1716,11 +1804,9 @@ func (c *Container) makeBindMounts() error {
}
if !c.config.UseImageHosts {
- newHosts, err := c.generateHosts("/etc/hosts")
- if err != nil {
+ if err := c.updateHosts("/etc/hosts"); err != nil {
return errors.Wrapf(err, "error creating hosts file for container %s", c.ID())
}
- c.state.BindMounts["/etc/hosts"] = newHosts
}
}
@@ -1737,32 +1823,32 @@ func (c *Container) makeBindMounts() error {
}
} else {
if !c.config.UseImageHosts && c.state.BindMounts["/etc/hosts"] == "" {
- newHosts, err := c.generateHosts("/etc/hosts")
- if err != nil {
+ if err := c.updateHosts("/etc/hosts"); err != nil {
return errors.Wrapf(err, "error creating hosts file for container %s", c.ID())
}
- c.state.BindMounts["/etc/hosts"] = newHosts
}
}
// SHM is always added when we mount the container
c.state.BindMounts["/dev/shm"] = c.config.ShmDir
- newPasswd, newGroup, err := c.generatePasswdAndGroup()
- if err != nil {
- return errors.Wrapf(err, "error creating temporary passwd file for container %s", c.ID())
- }
- if newPasswd != "" {
- // Make /etc/passwd
- // If it already exists, delete so we can recreate
- delete(c.state.BindMounts, "/etc/passwd")
- c.state.BindMounts["/etc/passwd"] = newPasswd
- }
- if newGroup != "" {
- // Make /etc/group
- // If it already exists, delete so we can recreate
- delete(c.state.BindMounts, "/etc/group")
- c.state.BindMounts["/etc/group"] = newGroup
+ if c.config.Passwd == nil || *c.config.Passwd {
+ newPasswd, newGroup, err := c.generatePasswdAndGroup()
+ if err != nil {
+ return errors.Wrapf(err, "error creating temporary passwd file for container %s", c.ID())
+ }
+ if newPasswd != "" {
+ // Make /etc/passwd
+ // If it already exists, delete so we can recreate
+ delete(c.state.BindMounts, "/etc/passwd")
+ c.state.BindMounts["/etc/passwd"] = newPasswd
+ }
+ if newGroup != "" {
+ // Make /etc/group
+ // If it already exists, delete so we can recreate
+ delete(c.state.BindMounts, "/etc/group")
+ c.state.BindMounts["/etc/group"] = newGroup
+ }
}
// Make /etc/hostname
@@ -2029,18 +2115,29 @@ func (c *Container) generateResolvConf() (string, error) {
return destPath, nil
}
-// generateHosts creates a containers hosts file
-func (c *Container) generateHosts(path string) (string, error) {
+// updateHosts updates the container's hosts file
+func (c *Container) updateHosts(path string) error {
+ var hosts string
+
orig, err := ioutil.ReadFile(path)
if err != nil {
- return "", err
+ // Ignore if the path does not exist
+ if !os.IsNotExist(err) {
+ return err
+ }
+ } else {
+ hosts = string(orig)
}
- hosts := string(orig)
- hosts += c.getHosts()
+ hosts += c.getHosts()
hosts = c.appendLocalhost(hosts)
- return c.writeStringToRundir("hosts", hosts)
+ newHosts, err := c.writeStringToRundir("hosts", hosts)
+ if err != nil {
+ return err
+ }
+ c.state.BindMounts["/etc/hosts"] = newHosts
+ return nil
}
// based on networking mode we may want to append the localhost
@@ -2135,11 +2232,24 @@ func (c *Container) getHosts() string {
}
}
} else if c.config.NetMode.IsSlirp4netns() {
- gatewayIP, err := GetSlirp4netnsGateway(c.slirp4netnsSubnet)
- if err != nil {
- logrus.Warn("Failed to determine gatewayIP: ", err.Error())
- } else {
- hosts += fmt.Sprintf("%s host.containers.internal\n", gatewayIP.String())
+ // getLocalIP returns the non loopback local IP of the host
+ getLocalIP := func() string {
+ addrs, err := net.InterfaceAddrs()
+ if err != nil {
+ return ""
+ }
+ for _, address := range addrs {
+ // check the address type and if it is not a loopback the display it
+ if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
+ if ipnet.IP.To4() != nil {
+ return ipnet.IP.String()
+ }
+ }
+ }
+ return ""
+ }
+ if ip := getLocalIP(); ip != "" {
+ hosts += fmt.Sprintf("%s\t%s\n", ip, "host.containers.internal")
}
} else {
logrus.Debug("Network configuration does not support host.containers.internal address")
@@ -2265,12 +2375,25 @@ func (c *Container) generateUserGroupEntry(addedGID int) (string, int, error) {
// /etc/passwd via AddCurrentUserPasswdEntry (though this does not trigger if
// the user in question already exists in /etc/passwd) or the UID to be added
// is 0).
+// 3. The user specified additional host user accounts to add the the /etc/passwd file
// Returns password entry (as a string that can be appended to /etc/passwd) and
// any error that occurred.
func (c *Container) generatePasswdEntry() (string, error) {
passwdString := ""
addedUID := 0
+ for _, userid := range c.config.HostUsers {
+ // Lookup User on host
+ u, err := util.LookupUser(userid)
+ if err != nil {
+ return "", err
+ }
+ entry, err := c.userPasswdEntry(u)
+ if err != nil {
+ return "", err
+ }
+ passwdString += entry
+ }
if c.config.AddCurrentUserPasswdEntry {
entry, uid, _, err := c.generateCurrentUserPasswdEntry()
if err != nil {
@@ -2303,17 +2426,25 @@ func (c *Container) generateCurrentUserPasswdEntry() (string, int, int, error) {
if err != nil {
return "", 0, 0, errors.Wrapf(err, "failed to get current user")
}
+ pwd, err := c.userPasswdEntry(u)
+ if err != nil {
+ return "", 0, 0, err
+ }
+
+ return pwd, uid, rootless.GetRootlessGID(), nil
+}
+func (c *Container) userPasswdEntry(u *user.User) (string, error) {
// Lookup the user to see if it exists in the container image.
- _, err = lookup.GetUser(c.state.Mountpoint, u.Username)
+ _, err := lookup.GetUser(c.state.Mountpoint, u.Username)
if err != runcuser.ErrNoPasswdEntries {
- return "", 0, 0, err
+ return "", err
}
// Lookup the UID to see if it exists in the container image.
_, err = lookup.GetUser(c.state.Mountpoint, u.Uid)
if err != runcuser.ErrNoPasswdEntries {
- return "", 0, 0, err
+ return "", err
}
// If the user's actual home directory exists, or was mounted in - use
@@ -2347,7 +2478,7 @@ func (c *Container) generateCurrentUserPasswdEntry() (string, int, int, error) {
c.config.Spec.Process.Env = append(c.config.Spec.Process.Env, fmt.Sprintf("HOME=%s", homeDir))
}
- return fmt.Sprintf("%s:*:%s:%s:%s:%s:/bin/sh\n", u.Username, u.Uid, u.Gid, u.Name, homeDir), uid, rootless.GetRootlessGID(), nil
+ return fmt.Sprintf("%s:*:%s:%s:%s:%s:/bin/sh\n", u.Username, u.Uid, u.Gid, u.Name, homeDir), nil
}
// generateUserPasswdEntry generates an /etc/passwd entry for the container user
@@ -2402,7 +2533,7 @@ func (c *Container) generateUserPasswdEntry(addedUID int) (string, int, int, err
// generatePasswdAndGroup generates container-specific passwd and group files
// iff g.config.User is a number or we are configured to make a passwd entry for
-// the current user.
+// the current user or the user specified HostsUsers
// Returns path to file to mount at /etc/passwd, path to file to mount at
// /etc/group, and any error that occurred. If no passwd/group file were
// required, the empty string will be returned for those path (this may occur
@@ -2413,7 +2544,8 @@ func (c *Container) generateUserPasswdEntry(addedUID int) (string, int, int, err
// with a bind mount). This is done in cases where the container is *not*
// read-only. In this case, the function will return nothing ("", "", nil).
func (c *Container) generatePasswdAndGroup() (string, string, error) {
- if !c.config.AddCurrentUserPasswdEntry && c.config.User == "" {
+ if !c.config.AddCurrentUserPasswdEntry && c.config.User == "" &&
+ len(c.config.HostUsers) == 0 {
return "", "", nil
}
diff --git a/libpod/define/annotations.go b/libpod/define/annotations.go
index f6b1c06ea..3964a1237 100644
--- a/libpod/define/annotations.go
+++ b/libpod/define/annotations.go
@@ -66,3 +66,15 @@ const (
// annotation.
InspectResponseFalse = "FALSE"
)
+
+// IsReservedAnnotation returns true if the specified value corresponds to an
+// already reserved annotation that Podman sets during container creation.
+func IsReservedAnnotation(value string) bool {
+ switch value {
+ case InspectAnnotationCIDFile, InspectAnnotationAutoremove, InspectAnnotationVolumesFrom, InspectAnnotationPrivileged, InspectAnnotationPublishAll, InspectAnnotationInit, InspectAnnotationLabel, InspectAnnotationSeccomp, InspectAnnotationApparmor, InspectResponseTrue, InspectResponseFalse:
+ return true
+
+ default:
+ return false
+ }
+}
diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go
index 677b39218..ba73e4196 100644
--- a/libpod/define/container_inspect.go
+++ b/libpod/define/container_inspect.go
@@ -68,6 +68,8 @@ type InspectContainerConfig struct {
Timeout uint `json:"Timeout"`
// StopTimeout is time before container is stopped when calling stop
StopTimeout uint `json:"StopTimeout"`
+ // Passwd determines whether or not podman can add entries to /etc/passwd and /etc/group
+ Passwd *bool `json:"Passwd,omitempty"`
}
// InspectRestartPolicy holds information about the container's restart policy.
@@ -189,22 +191,28 @@ type InspectMount struct {
// Docker, but here we see more fields that are unused (nonsensical in the
// context of Libpod).
type InspectContainerState struct {
- OciVersion string `json:"OciVersion"`
- Status string `json:"Status"`
- Running bool `json:"Running"`
- Paused bool `json:"Paused"`
- Restarting bool `json:"Restarting"` // TODO
- OOMKilled bool `json:"OOMKilled"`
- Dead bool `json:"Dead"`
- Pid int `json:"Pid"`
- ConmonPid int `json:"ConmonPid,omitempty"`
- ExitCode int32 `json:"ExitCode"`
- Error string `json:"Error"` // TODO
- StartedAt time.Time `json:"StartedAt"`
- FinishedAt time.Time `json:"FinishedAt"`
- Health HealthCheckResults `json:"Health,omitempty"`
- Checkpointed bool `json:"Checkpointed,omitempty"`
- CgroupPath string `json:"CgroupPath,omitempty"`
+ OciVersion string `json:"OciVersion"`
+ Status string `json:"Status"`
+ Running bool `json:"Running"`
+ Paused bool `json:"Paused"`
+ Restarting bool `json:"Restarting"` // TODO
+ OOMKilled bool `json:"OOMKilled"`
+ Dead bool `json:"Dead"`
+ Pid int `json:"Pid"`
+ ConmonPid int `json:"ConmonPid,omitempty"`
+ ExitCode int32 `json:"ExitCode"`
+ Error string `json:"Error"` // TODO
+ StartedAt time.Time `json:"StartedAt"`
+ FinishedAt time.Time `json:"FinishedAt"`
+ Health HealthCheckResults `json:"Health,omitempty"`
+ Checkpointed bool `json:"Checkpointed,omitempty"`
+ CgroupPath string `json:"CgroupPath,omitempty"`
+ CheckpointedAt time.Time `json:"CheckpointedAt,omitempty"`
+ RestoredAt time.Time `json:"RestoredAt,omitempty"`
+ CheckpointLog string `json:"CheckpointLog,omitempty"`
+ CheckpointPath string `json:"CheckpointPath,omitempty"`
+ RestoreLog string `json:"RestoreLog,omitempty"`
+ Restored bool `json:"Restored,omitempty"`
}
// Healthcheck returns the HealthCheckResults. This is used for old podman compat
diff --git a/libpod/healthcheck_linux.go b/libpod/healthcheck_linux.go
index 2c19e0a61..a1f3e8491 100644
--- a/libpod/healthcheck_linux.go
+++ b/libpod/healthcheck_linux.go
@@ -73,6 +73,16 @@ func (c *Container) removeTransientFiles(ctx context.Context) error {
defer conn.Close()
timerFile := fmt.Sprintf("%s.timer", c.ID())
serviceFile := fmt.Sprintf("%s.service", c.ID())
+
+ // If the service has failed (the healthcheck has failed), then
+ // the .service file is not removed on stopping the unit file. If
+ // we check the properties of the service, it will automatically
+ // reset the state. But checking the state takes msecs vs usecs to
+ // blindly call reset.
+ if err := conn.ResetFailedUnitContext(ctx, serviceFile); err != nil {
+ logrus.Debugf("failed to reset unit file: %q", err)
+ }
+
// We want to ignore errors where the timer unit and/or service unit has already
// been removed. The error return is generic so we have to check against the
// string in the error
diff --git a/libpod/kube.go b/libpod/kube.go
index 4e61b5377..d667616d0 100644
--- a/libpod/kube.go
+++ b/libpod/kube.go
@@ -747,7 +747,7 @@ func libpodEnvVarsToKubeEnvVars(envs []string, imageEnvs []string) ([]v1.EnvVar,
defaultEnv := env.DefaultEnvVariables()
envVars := make([]v1.EnvVar, 0, len(envs))
imageMap := make(map[string]string, len(imageEnvs))
- for _, ie := range envs {
+ for _, ie := range imageEnvs {
split := strings.SplitN(ie, "=", 2)
imageMap[split[0]] = split[1]
}
diff --git a/libpod/network/internal/util/util.go b/libpod/network/internal/util/util.go
index bf9d70aba..d9b9a8dc0 100644
--- a/libpod/network/internal/util/util.go
+++ b/libpod/network/internal/util/util.go
@@ -78,7 +78,7 @@ func GetUsedSubnets(n NetUtil) ([]*net.IPNet, error) {
return append(subnets, liveSubnets...), nil
}
-// GetFreeIPv6NetworkSubnet returns a unused ipv4 subnet
+// GetFreeIPv4NetworkSubnet returns a unused ipv4 subnet
func GetFreeIPv4NetworkSubnet(usedNetworks []*net.IPNet) (*types.Subnet, error) {
// the default podman network is 10.88.0.0/16
// start locking for free /24 networks
diff --git a/libpod/options.go b/libpod/options.go
index e6fa987a8..204f2a457 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1768,6 +1768,17 @@ func WithPidFile(pidFile string) CtrCreateOption {
}
}
+// WithHostUsers indicates host users to add to /etc/passwd
+func WithHostUsers(hostUsers []string) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return define.ErrCtrFinalized
+ }
+ ctr.config.HostUsers = hostUsers
+ return nil
+ }
+}
+
// WithInitCtrType indicates the container is a initcontainer
func WithInitCtrType(containerType string) CtrCreateOption {
return func(ctr *Container) error {
@@ -1794,6 +1805,17 @@ func WithHostDevice(dev []specs.LinuxDevice) CtrCreateOption {
}
}
+// WithSelectedPasswordManagement makes it so that the container either does or does not setup /etc/passwd or /etc/group
+func WithSelectedPasswordManagement(passwd *bool) CtrCreateOption {
+ return func(c *Container) error {
+ if c.valid {
+ return define.ErrCtrFinalized
+ }
+ c.config.Passwd = passwd
+ return nil
+ }
+}
+
// Pod Creation Options
// WithPodCreateCommand adds the full command plus arguments of the current
diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go
index 52ac0d4d7..bf0fc4585 100644
--- a/libpod/runtime_img.go
+++ b/libpod/runtime_img.go
@@ -36,10 +36,21 @@ func (r *Runtime) RemoveContainersForImageCallback(ctx context.Context) libimage
return err
}
for _, ctr := range ctrs {
- if ctr.config.RootfsImageID == imageID {
- var timeout *uint
+ if ctr.config.RootfsImageID != imageID {
+ continue
+ }
+ var timeout *uint
+ if ctr.config.IsInfra {
+ pod, err := r.state.Pod(ctr.config.Pod)
+ if err != nil {
+ return errors.Wrapf(err, "container %s is in pod %s, but pod cannot be retrieved", ctr.ID(), pod.ID())
+ }
+ if err := r.removePod(ctx, pod, true, true, timeout); err != nil {
+ return errors.Wrapf(err, "removing image %s: container %s using image could not be removed", imageID, ctr.ID())
+ }
+ } else {
if err := r.removeContainer(ctx, ctr, true, false, false, timeout); err != nil {
- return errors.Wrapf(err, "error removing image %s: container %s using image could not be removed", imageID, ctr.ID())
+ return errors.Wrapf(err, "removing image %s: container %s using image could not be removed", imageID, ctr.ID())
}
}
}