diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container.go | 9 | ||||
-rw-r--r-- | libpod/container_config.go | 4 | ||||
-rw-r--r-- | libpod/container_inspect.go | 39 | ||||
-rw-r--r-- | libpod/container_internal.go | 12 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 210 | ||||
-rw-r--r-- | libpod/define/annotations.go | 12 | ||||
-rw-r--r-- | libpod/define/container_inspect.go | 40 | ||||
-rw-r--r-- | libpod/healthcheck_linux.go | 10 | ||||
-rw-r--r-- | libpod/kube.go | 2 | ||||
-rw-r--r-- | libpod/network/internal/util/util.go | 2 | ||||
-rw-r--r-- | libpod/options.go | 22 | ||||
-rw-r--r-- | libpod/runtime_img.go | 17 |
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()) } } } |