diff options
Diffstat (limited to 'libpod')
35 files changed, 244 insertions, 129 deletions
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go index 4b6ff2c1d..be2787670 100644 --- a/libpod/boltdb_state.go +++ b/libpod/boltdb_state.go @@ -243,9 +243,7 @@ func (s *BoltState) Refresh() error { return errors.Wrapf(err, "error unmarshalling state for container %s", string(id)) } - if err := resetState(state); err != nil { - return errors.Wrapf(err, "error resetting state for container %s", string(id)) - } + resetState(state) newStateBytes, err := json.Marshal(state) if err != nil { diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go index 21d55bf77..70abbb39c 100644 --- a/libpod/boltdb_state_internal.go +++ b/libpod/boltdb_state_internal.go @@ -407,10 +407,7 @@ func (s *BoltState) getContainerFromDB(id []byte, ctr *Container, ctrsBkt *bolt. ociRuntime, ok := s.runtime.ociRuntimes[runtimeName] if !ok { // Use a MissingRuntime implementation - ociRuntime, err = getMissingRuntime(runtimeName, s.runtime) - if err != nil { - return err - } + ociRuntime = getMissingRuntime(runtimeName, s.runtime) } ctr.ociRuntime = ociRuntime } diff --git a/libpod/container_api.go b/libpod/container_api.go index d366ffb84..d43cb4829 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -39,7 +39,7 @@ func (c *Container) Init(ctx context.Context) (err error) { } // don't recursively start - if err := c.checkDependenciesAndHandleError(ctx); err != nil { + if err := c.checkDependenciesAndHandleError(); err != nil { return err } @@ -146,7 +146,7 @@ func (c *Container) RestartWithTimeout(ctx context.Context, timeout uint) (err e } } - if err = c.checkDependenciesAndHandleError(ctx); err != nil { + if err = c.checkDependenciesAndHandleError(); err != nil { return err } diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index b26dcddf6..c6d9e1a65 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -90,7 +90,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) } namedVolumes, mounts := c.sortUserVolumes(ctrSpec) - inspectMounts, err := c.getInspectMounts(ctrSpec, namedVolumes, mounts) + inspectMounts, err := c.getInspectMounts(namedVolumes, mounts) if err != nil { return nil, err } @@ -164,10 +164,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) } data.NetworkSettings = networkConfig - inspectConfig, err := c.generateInspectContainerConfig(ctrSpec) - if err != nil { - return nil, err - } + inspectConfig := c.generateInspectContainerConfig(ctrSpec) data.Config = inspectConfig hostConfig, err := c.generateInspectContainerHostConfig(ctrSpec, namedVolumes, mounts) @@ -195,7 +192,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) // Get inspect-formatted mounts list. // Only includes user-specified mounts. Only includes bind mounts and named // volumes, not tmpfs volumes. -func (c *Container) getInspectMounts(ctrSpec *spec.Spec, namedVolumes []*ContainerNamedVolume, mounts []spec.Mount) ([]define.InspectMount, error) { +func (c *Container) getInspectMounts(namedVolumes []*ContainerNamedVolume, mounts []spec.Mount) ([]define.InspectMount, error) { inspectMounts := []define.InspectMount{} // No mounts, return early @@ -278,7 +275,7 @@ func parseMountOptionsForInspect(options []string, mount *define.InspectMount) { } // Generate the InspectContainerConfig struct for the Config field of Inspect. -func (c *Container) generateInspectContainerConfig(spec *spec.Spec) (*define.InspectContainerConfig, error) { +func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.InspectContainerConfig { ctrConfig := new(define.InspectContainerConfig) ctrConfig.Hostname = c.Hostname() @@ -325,7 +322,7 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) (*define.Ins ctrConfig.CreateCommand = c.config.CreateCommand - return ctrConfig, nil + return ctrConfig } // Generate the InspectContainerHostConfig struct for the HostConfig field of diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 43e873bd6..73e0b2118 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -239,7 +239,7 @@ func (c *Container) handleRestartPolicy(ctx context.Context) (restarted bool, er logrus.Debugf("Restarting container %s due to restart policy %s", c.ID(), c.config.RestartPolicy) // Need to check if dependencies are alive. - if err = c.checkDependenciesAndHandleError(ctx); err != nil { + if err = c.checkDependenciesAndHandleError(); err != nil { return false, err } @@ -513,7 +513,7 @@ func (c *Container) teardownStorage() error { // Reset resets state fields to default values. // It is performed before a refresh and clears the state after a reboot. // It does not save the results - assumes the database will do that for us. -func resetState(state *ContainerState) error { +func resetState(state *ContainerState) { state.PID = 0 state.ConmonPID = 0 state.Mountpoint = "" @@ -527,8 +527,6 @@ func resetState(state *ContainerState) error { state.StoppedByUser = false state.RestartPolicyMatch = false state.RestartCount = 0 - - return nil } // Refresh refreshes the container's state after a restart. @@ -756,7 +754,7 @@ func (c *Container) prepareToStart(ctx context.Context, recursive bool) (err err } if !recursive { - if err := c.checkDependenciesAndHandleError(ctx); err != nil { + if err := c.checkDependenciesAndHandleError(); err != nil { return err } } else { @@ -792,7 +790,7 @@ func (c *Container) prepareToStart(ctx context.Context, recursive bool) (err err } // checks dependencies are running and prints a helpful message -func (c *Container) checkDependenciesAndHandleError(ctx context.Context) error { +func (c *Container) checkDependenciesAndHandleError() error { notRunning, err := c.checkDependenciesRunning() if err != nil { return errors.Wrapf(err, "error checking dependencies for container %s", c.ID()) @@ -1209,13 +1207,35 @@ func (c *Container) stop(timeout uint) error { } } + // Check if conmon is still alive. + // If it is not, we won't be getting an exit file. + conmonAlive, err := c.ociRuntime.CheckConmonRunning(c) + if err != nil { + return err + } + if err := c.ociRuntime.StopContainer(c, timeout, all); err != nil { return err } + c.newContainerEvent(events.Stop) + c.state.PID = 0 c.state.ConmonPID = 0 c.state.StoppedByUser = true + + if !conmonAlive { + // Conmon is dead, so we can't epect an exit code. + c.state.ExitCode = -1 + c.state.FinishedTime = time.Now() + c.state.State = define.ContainerStateStopped + if err := c.save(); err != nil { + logrus.Errorf("Error saving container %s status: %v", c.ID(), err) + } + + return errors.Wrapf(define.ErrConmonDead, "container %s conmon process missing, cannot retrieve exit code", c.ID()) + } + if err := c.save(); err != nil { return errors.Wrapf(err, "error saving container %s state after stopping", c.ID()) } @@ -1225,8 +1245,6 @@ func (c *Container) stop(timeout uint) error { return err } - c.newContainerEvent(events.Stop) - return nil } diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index d08e012a6..12c1abf1c 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -79,7 +79,8 @@ func (c *Container) prepare() error { go func() { defer wg.Done() // Set up network namespace if not already set up - if c.config.CreateNetNS && c.state.NetNS == nil && !c.config.PostConfigureNetNS { + noNetNS := c.state.NetNS == nil + if c.config.CreateNetNS && noNetNS && !c.config.PostConfigureNetNS { netNS, networkStatus, createNetNSErr = c.runtime.createNetNS(c) if createNetNSErr != nil { return @@ -94,7 +95,7 @@ func (c *Container) prepare() error { } // handle rootless network namespace setup - if c.state.NetNS != nil && c.config.NetMode.IsSlirp4netns() && !c.config.PostConfigureNetNS { + if noNetNS && c.config.NetMode.IsSlirp4netns() && !c.config.PostConfigureNetNS { createNetNSErr = c.runtime.setupRootlessNetNS(c) } }() @@ -392,7 +393,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { } for _, i := range c.config.Spec.Linux.Namespaces { - if i.Type == spec.UTSNamespace { + if i.Type == spec.UTSNamespace && i.Path == "" { hostname := c.Hostname() g.SetHostname(hostname) g.AddProcessEnv("HOSTNAME", hostname) @@ -591,7 +592,8 @@ func (c *Container) addNamespaceContainer(g *generate.Generator, ns LinuxNS, ctr if specNS == spec.UTSNamespace { hostname := nsCtr.Hostname() - g.SetHostname(hostname) + // Joining an existing namespace, cannot set the hostname + g.SetHostname("") g.AddProcessEnv("HOSTNAME", hostname) } @@ -1171,6 +1173,15 @@ func (c *Container) makeBindMounts() error { // finally, save it in the new container c.state.BindMounts["/etc/hosts"] = hostsPath } + + if !hasCurrentUserMapped(c) { + if err := makeAccessible(resolvPath, c.RootUID(), c.RootGID()); err != nil { + return err + } + if err := makeAccessible(hostsPath, c.RootUID(), c.RootGID()); err != nil { + return err + } + } } else { if !c.config.UseImageResolvConf { newResolv, err := c.generateResolvConf() @@ -1302,7 +1313,7 @@ func (c *Container) generateResolvConf() (string, error) { } } - var dns []net.IP + dns := make([]net.IP, 0, len(c.runtime.config.Containers.DNSServers)) for _, i := range c.runtime.config.Containers.DNSServers { result := net.ParseIP(i) if result == nil { @@ -1382,7 +1393,9 @@ func (c *Container) generateHosts(path string) (string, error) { // local hosts file. netCtr is the container from which the netNS information is // taken. // path is the basis of the hosts file, into which netCtr's netNS information will be appended. -func (c *Container) appendHosts(path string, netCtr *Container) (string, error) { +// FIXME. Path should be used by this function,but I am not sure what is correct; remove //lint +// once this is fixed +func (c *Container) appendHosts(path string, netCtr *Container) (string, error) { //nolint return c.appendStringToRundir("hosts", netCtr.getHosts()) } diff --git a/libpod/container_log.go b/libpod/container_log.go index bfa303e84..c3a84d048 100644 --- a/libpod/container_log.go +++ b/libpod/container_log.go @@ -19,7 +19,7 @@ func (r *Runtime) Log(containers []*Container, options *logs.LogOptions, logChan return nil } -// ReadLog reads a containers log based on the input options and returns loglines over a channel +// 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 @@ -61,7 +61,7 @@ func (c *Container) readFromLogFile(options *logs.LogOptions, logChannel chan *l partial += nll.Msg continue } else if !nll.Partial() && len(partial) > 1 { - nll.Msg = partial + nll.Msg = partial + nll.Msg partial = "" } nll.CID = c.ID() diff --git a/libpod/define/errors.go b/libpod/define/errors.go index 16df2a1cc..083553b7e 100644 --- a/libpod/define/errors.go +++ b/libpod/define/errors.go @@ -141,6 +141,9 @@ var ( // ErrConmonOutdated indicates the version of conmon found (whether via the configuration or $PATH) // is out of date for the current podman version ErrConmonOutdated = errors.New("outdated conmon version") + // ErrConmonDead indicates that the container's conmon process has been + // killed, preventing normal operation. + ErrConmonDead = errors.New("conmon process killed") // ErrImageInUse indicates the requested operation failed because the image was in use ErrImageInUse = errors.New("image is being used") diff --git a/libpod/define/info.go b/libpod/define/info.go index f136936f7..47c53d067 100644 --- a/libpod/define/info.go +++ b/libpod/define/info.go @@ -43,7 +43,7 @@ type RemoteSocket struct { Exists bool `json:"exists,omitempty"` } -// SlirpInfo describes the slirp exectuable that +// SlirpInfo describes the slirp executable that // is being being used. type SlirpInfo struct { Executable string `json:"executable"` diff --git a/libpod/define/pod_inspect.go b/libpod/define/pod_inspect.go index 26fd2cab4..7f06e16fc 100644 --- a/libpod/define/pod_inspect.go +++ b/libpod/define/pod_inspect.go @@ -18,6 +18,9 @@ type InspectPodData struct { Namespace string `json:"Namespace,omitempty"` // Created is the time when the pod was created. Created time.Time + // CreateCommand is the full command plus arguments of the process the + // container has been created with. + CreateCommand []string `json:"CreateCommand,omitempty"` // State represents the current state of the pod. State string `json:"State"` // Hostname is the hostname that the pod will set. diff --git a/libpod/events.go b/libpod/events.go index 20ebecc66..3d07c5d76 100644 --- a/libpod/events.go +++ b/libpod/events.go @@ -85,10 +85,7 @@ func (r *Runtime) Events(options events.ReadOptions) error { // GetEvents reads the event log and returns events based on input filters func (r *Runtime) GetEvents(filters []string) ([]*events.Event, error) { - var ( - logEvents []*events.Event - readErr error - ) + var readErr error eventChannel := make(chan *events.Event) options := events.ReadOptions{ EventChannel: eventChannel, @@ -106,6 +103,7 @@ func (r *Runtime) GetEvents(filters []string) ([]*events.Event, error) { if readErr != nil { return nil, readErr } + logEvents := make([]*events.Event, 0, len(eventChannel)) for e := range eventChannel { logEvents = append(logEvents, e) } diff --git a/libpod/events/filters.go b/libpod/events/filters.go index b3c5eda6e..6eed1f61d 100644 --- a/libpod/events/filters.go +++ b/libpod/events/filters.go @@ -81,7 +81,7 @@ func parseFilter(filter string) (string, string, error) { } func generateEventOptions(filters []string, since, until string) ([]EventFilter, error) { - var options []EventFilter + options := make([]EventFilter, 0, len(filters)) for _, filter := range filters { key, val, err := parseFilter(filter) if err != nil { diff --git a/libpod/filters/pods.go b/libpod/filters/pods.go index 9bf436eab..0edb9fbf2 100644 --- a/libpod/filters/pods.go +++ b/libpod/filters/pods.go @@ -57,13 +57,13 @@ func GeneratePodFilterFunc(filter, filterValue string) ( return nil, errors.Errorf("%s is not a valid status", filterValue) } return func(p *libpod.Pod) bool { - ctr_statuses, err := p.Status() + ctrStatuses, err := p.Status() if err != nil { return false } - for _, ctr_status := range ctr_statuses { - state := ctr_status.String() - if ctr_status == define.ContainerStateConfigured { + for _, ctrStatus := range ctrStatuses { + state := ctrStatus.String() + if ctrStatus == define.ContainerStateConfigured { state = "created" } if state == filterValue { diff --git a/libpod/image/filters.go b/libpod/image/filters.go index 747eba165..9d99fb344 100644 --- a/libpod/image/filters.go +++ b/libpod/image/filters.go @@ -102,8 +102,8 @@ func ReferenceFilter(ctx context.Context, referenceFilter string) ResultFilter { } } -// IdFilter allows you to filter by image Id -func IdFilter(idFilter string) ResultFilter { +// IDFilter allows you to filter by image Id +func IDFilter(idFilter string) ResultFilter { return func(i *Image) bool { return i.ID() == idFilter } @@ -172,7 +172,7 @@ func (ir *Runtime) createFilterFuncs(filters []string, img *Image) ([]ResultFilt case "reference": filterFuncs = append(filterFuncs, ReferenceFilter(ctx, splitFilter[1])) case "id": - filterFuncs = append(filterFuncs, IdFilter(splitFilter[1])) + filterFuncs = append(filterFuncs, IDFilter(splitFilter[1])) default: return nil, errors.Errorf("invalid filter %s ", splitFilter[0]) } diff --git a/libpod/image/image.go b/libpod/image/image.go index 60787b826..1101e35dc 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -172,8 +172,6 @@ func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile // LoadFromArchiveReference creates a new image object for images pulled from a tar archive and the like (podman load) // This function is needed because it is possible for a tar archive to have multiple tags for one image func (ir *Runtime) LoadFromArchiveReference(ctx context.Context, srcRef types.ImageReference, signaturePolicyPath string, writer io.Writer) ([]*Image, error) { - var newImages []*Image - if signaturePolicyPath == "" { signaturePolicyPath = ir.SignaturePolicyPath } @@ -182,6 +180,7 @@ func (ir *Runtime) LoadFromArchiveReference(ctx context.Context, srcRef types.Im return nil, errors.Wrapf(err, "unable to pull %s", transports.ImageName(srcRef)) } + newImages := make([]*Image, 0, len(imageNames)) for _, name := range imageNames { newImage, err := ir.NewFromLocal(name) if err != nil { @@ -475,11 +474,11 @@ func (ir *Runtime) GetRWImages() ([]*Image, error) { // getImages retrieves all images present in storage func (ir *Runtime) getImages(rwOnly bool) ([]*Image, error) { - var newImages []*Image images, err := ir.store.Images() if err != nil { return nil, err } + newImages := make([]*Image, 0, len(images)) for _, i := range images { if rwOnly && i.ReadOnly { continue diff --git a/libpod/image/image_test.go b/libpod/image/image_test.go index 3cd368cdc..74067853e 100644 --- a/libpod/image/image_test.go +++ b/libpod/image/image_test.go @@ -44,7 +44,7 @@ func cleanup(workdir string, ir *Runtime) { } } -func makeLocalMatrix(b, bg *Image) ([]localImageTest, error) { +func makeLocalMatrix(b, bg *Image) []localImageTest { var l []localImageTest // busybox busybox := localImageTest{ @@ -65,7 +65,7 @@ func makeLocalMatrix(b, bg *Image) ([]localImageTest, error) { busyboxGlibc.names = bbGlibcNames l = append(l, busybox, busyboxGlibc) - return l, nil + return l } @@ -100,9 +100,7 @@ func TestImage_NewFromLocal(t *testing.T) { bbglibc, err := ir.New(context.Background(), "docker.io/library/busybox:glibc", "", "", writer, nil, SigningOptions{}, nil, util.PullImageMissing) assert.NoError(t, err) - tm, err := makeLocalMatrix(bb, bbglibc) - assert.NoError(t, err) - + tm := makeLocalMatrix(bb, bbglibc) for _, image := range tm { // tag our images err = image.img.TagImage(image.taggedName) diff --git a/libpod/image/prune.go b/libpod/image/prune.go index 3b4ea74c4..518795173 100644 --- a/libpod/image/prune.go +++ b/libpod/image/prune.go @@ -104,10 +104,7 @@ func (ir *Runtime) GetPruneImages(ctx context.Context, all bool, filterFuncs []I // PruneImages prunes dangling and optionally all unused images from the local // image store func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) ([]string, error) { - var ( - prunedCids []string - filterFuncs []ImageFilter - ) + filterFuncs := make([]ImageFilter, 0, len(filter)) for _, f := range filter { filterSplit := strings.SplitN(f, "=", 2) if len(filterSplit) < 2 { @@ -125,6 +122,7 @@ func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) ( if err != nil { return nil, errors.Wrap(err, "unable to get images to prune") } + prunedCids := make([]string, 0, len(pruneImages)) for _, p := range pruneImages { repotags, err := p.RepoTags() if err != nil { diff --git a/libpod/image/pull.go b/libpod/image/pull.go index 6b4c40ba2..24909a59a 100644 --- a/libpod/image/pull.go +++ b/libpod/image/pull.go @@ -366,7 +366,7 @@ func (ir *Runtime) pullGoalFromPossiblyUnqualifiedName(inputName string) (*pullG if err != nil { return nil, err } - var refPairs []pullRefPair + refPairs := make([]pullRefPair, 0, len(searchRegistries)) for _, registry := range searchRegistries { ref, err := decomposedImage.referenceWithRegistry(registry) if err != nil { diff --git a/libpod/image/search.go b/libpod/image/search.go index fd29dac45..f8d45d576 100644 --- a/libpod/image/search.go +++ b/libpod/image/search.go @@ -93,8 +93,8 @@ func SearchImages(term string, options SearchOptions) ([]SearchResult, error) { searchImageInRegistryHelper := func(index int, registry string) { defer sem.Release(1) defer wg.Done() - searchOutput, err := searchImageInRegistry(term, registry, options) - data[index] = searchOutputData{data: searchOutput, err: err} + searchOutput := searchImageInRegistry(term, registry, options) + data[index] = searchOutputData{data: searchOutput} } ctx := context.Background() @@ -131,7 +131,7 @@ func getRegistries(registry string) ([]string, error) { return registries, nil } -func searchImageInRegistry(term string, registry string, options SearchOptions) ([]SearchResult, error) { +func searchImageInRegistry(term string, registry string, options SearchOptions) []SearchResult { // Max number of queries by default is 25 limit := maxQueries if options.Limit > 0 { @@ -147,7 +147,7 @@ func searchImageInRegistry(term string, registry string, options SearchOptions) results, err := docker.SearchRegistry(context.TODO(), sc, registry, term, limit) if err != nil { logrus.Errorf("error searching registry %q: %v", registry, err) - return []SearchResult{}, nil + return []SearchResult{} } index := registry arr := strings.Split(registry, ".") @@ -201,7 +201,7 @@ func searchImageInRegistry(term string, registry string, options SearchOptions) } paramsArr = append(paramsArr, params) } - return paramsArr, nil + return paramsArr } // ParseSearchFilter turns the filter into a SearchFilter that can be used for diff --git a/libpod/kube.go b/libpod/kube.go index a3c5e912f..90acd2541 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -31,8 +31,8 @@ func (c *Container) GenerateForKube() (*v1.Pod, error) { func (p *Pod) GenerateForKube() (*v1.Pod, []v1.ServicePort, error) { // Generate the v1.Pod yaml description var ( - servicePorts []v1.ServicePort - ports []v1.ContainerPort + ports []v1.ContainerPort //nolint + servicePorts []v1.ServicePort //nolint ) allContainers, err := p.allContainers() @@ -99,7 +99,7 @@ func GenerateKubeServiceFromV1Pod(pod *v1.Pod, servicePorts []v1.ServicePort) v1 // containerPortsToServicePorts takes a slice of containerports and generates a // slice of service ports func containerPortsToServicePorts(containerPorts []v1.ContainerPort) []v1.ServicePort { - var sps []v1.ServicePort + sps := make([]v1.ServicePort, 0, len(containerPorts)) for _, cp := range containerPorts { nodePort := 30000 + rand.Intn(32767-30000+1) servicePort := v1.ServicePort{ @@ -116,11 +116,11 @@ func containerPortsToServicePorts(containerPorts []v1.ContainerPort) []v1.Servic // containersToServicePorts takes a slice of v1.Containers and generates an // inclusive list of serviceports to expose func containersToServicePorts(containers []v1.Container) []v1.ServicePort { - var sps []v1.ServicePort // Without the call to rand.Seed, a program will produce the same sequence of pseudo-random numbers // for each execution. Legal nodeport range is 30000-32767 rand.Seed(time.Now().UnixNano()) + sps := make([]v1.ServicePort, 0, len(containers)) for _, ctr := range containers { sps = append(sps, containerPortsToServicePorts(ctr.Ports)...) } @@ -128,11 +128,9 @@ func containersToServicePorts(containers []v1.Container) []v1.ServicePort { } func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPort) (*v1.Pod, error) { - var ( - podContainers []v1.Container - ) deDupPodVolumes := make(map[string]*v1.Volume) first := true + podContainers := make([]v1.Container, 0, len(containers)) for _, ctr := range containers { if !ctr.IsInfra() { ctr, volumes, err := containerToV1Container(ctr) @@ -201,13 +199,11 @@ func addContainersAndVolumesToPodObject(containers []v1.Container, volumes []v1. // simplePodWithV1Container is a function used by inspect when kube yaml needs to be generated // for a single container. we "insert" that container description in a pod. func simplePodWithV1Container(ctr *Container) (*v1.Pod, error) { - var containers []v1.Container kubeCtr, kubeVols, err := containerToV1Container(ctr) if err != nil { return nil, err } - containers = append(containers, kubeCtr) - return addContainersAndVolumesToPodObject(containers, kubeVols, ctr.Name()), nil + return addContainersAndVolumesToPodObject([]v1.Container{kubeCtr}, kubeVols, ctr.Name()), nil } @@ -223,11 +219,7 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, error) { if len(c.config.Spec.Linux.Devices) > 0 { // TODO Enable when we can support devices and their names - devices, err := generateKubeVolumeDeviceFromLinuxDevice(c.Spec().Linux.Devices) - if err != nil { - return kubeContainer, kubeVolumes, err - } - kubeContainer.VolumeDevices = devices + kubeContainer.VolumeDevices = generateKubeVolumeDeviceFromLinuxDevice(c.Spec().Linux.Devices) return kubeContainer, kubeVolumes, errors.Wrapf(define.ErrNotImplemented, "linux devices") } @@ -283,7 +275,7 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, error) { // ocicniPortMappingToContainerPort takes an ocicni portmapping and converts // it to a v1.ContainerPort format for kube output func ocicniPortMappingToContainerPort(portMappings []ocicni.PortMapping) ([]v1.ContainerPort, error) { - var containerPorts []v1.ContainerPort + containerPorts := make([]v1.ContainerPort, 0, len(portMappings)) for _, p := range portMappings { var protocol v1.Protocol switch strings.ToUpper(p.Protocol) { @@ -308,7 +300,7 @@ func ocicniPortMappingToContainerPort(portMappings []ocicni.PortMapping) ([]v1.C // libpodEnvVarsToKubeEnvVars converts a key=value string slice to []v1.EnvVar func libpodEnvVarsToKubeEnvVars(envs []string) ([]v1.EnvVar, error) { - var envVars []v1.EnvVar + envVars := make([]v1.EnvVar, 0, len(envs)) for _, e := range envs { split := strings.SplitN(e, "=", 2) if len(split) != 2 { @@ -325,11 +317,10 @@ func libpodEnvVarsToKubeEnvVars(envs []string) ([]v1.EnvVar, error) { // libpodMountsToKubeVolumeMounts converts the containers mounts to a struct kube understands func libpodMountsToKubeVolumeMounts(c *Container) ([]v1.VolumeMount, []v1.Volume, error) { - var vms []v1.VolumeMount - var vos []v1.Volume - // TjDO when named volumes are supported in play kube, also parse named volumes here _, mounts := c.sortUserVolumes(c.config.Spec) + vms := make([]v1.VolumeMount, 0, len(mounts)) + vos := make([]v1.Volume, 0, len(mounts)) for _, m := range mounts { vm, vo, err := generateKubeVolumeMount(m) if err != nil { @@ -404,8 +395,8 @@ func convertVolumePathToName(hostSourcePath string) (string, error) { func determineCapAddDropFromCapabilities(defaultCaps, containerCaps []string) *v1.Capabilities { var ( - drop []v1.Capability - add []v1.Capability + drop = []v1.Capability{} + add = []v1.Capability{} ) dedupDrop := make(map[string]bool) dedupAdd := make(map[string]bool) @@ -518,8 +509,8 @@ func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, error) { } // generateKubeVolumeDeviceFromLinuxDevice takes a list of devices and makes a VolumeDevice struct for kube -func generateKubeVolumeDeviceFromLinuxDevice(devices []specs.LinuxDevice) ([]v1.VolumeDevice, error) { - var volumeDevices []v1.VolumeDevice +func generateKubeVolumeDeviceFromLinuxDevice(devices []specs.LinuxDevice) []v1.VolumeDevice { + volumeDevices := make([]v1.VolumeDevice, 0, len(devices)) for _, d := range devices { vd := v1.VolumeDevice{ // TBD How are we going to sync up these names @@ -528,7 +519,7 @@ func generateKubeVolumeDeviceFromLinuxDevice(devices []specs.LinuxDevice) ([]v1. } volumeDevices = append(volumeDevices, vd) } - return volumeDevices, nil + return volumeDevices } func removeUnderscores(s string) string { diff --git a/libpod/oci.go b/libpod/oci.go index 684a7ba42..c2f0041b1 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -107,6 +107,13 @@ type OCIRuntime interface { // error. CheckpointContainer(ctr *Container, options ContainerCheckpointOptions) error + // CheckConmonRunning verifies that the given container's Conmon + // instance is still running. Runtimes without Conmon, or systems where + // the PID of conmon is not available, should mock this as True. + // True indicates that Conmon for the instance is running, False + // indicates it is not. + CheckConmonRunning(ctr *Container) (bool, error) + // SupportsCheckpoint returns whether this OCI runtime // implementation supports the CheckpointContainer() operation. SupportsCheckpoint() bool diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index 9c92b036e..0921a532b 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -669,6 +669,31 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, args...) } +func (r *ConmonOCIRuntime) CheckConmonRunning(ctr *Container) (bool, error) { + if ctr.state.ConmonPID == 0 { + // If the container is running or paused, assume Conmon is + // running. We didn't record Conmon PID on some old versions, so + // that is likely what's going on... + // Unusual enough that we should print a warning message though. + if ctr.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) { + logrus.Warnf("Conmon PID is not set, but container is running!") + return true, nil + } + // Container's not running, so conmon PID being unset is + // expected. Conmon is not running. + return false, nil + } + + // We have a conmon PID. Ping it with signal 0. + if err := unix.Kill(ctr.state.ConmonPID, 0); err != nil { + if err == unix.ESRCH { + return false, nil + } + return false, errors.Wrapf(err, "error pinging container %s conmon with signal 0", ctr.ID()) + } + return true, nil +} + // SupportsCheckpoint checks if the OCI runtime supports checkpointing // containers. func (r *ConmonOCIRuntime) SupportsCheckpoint() bool { diff --git a/libpod/oci_missing.go b/libpod/oci_missing.go index 4da16876c..8caf00e6e 100644 --- a/libpod/oci_missing.go +++ b/libpod/oci_missing.go @@ -32,7 +32,7 @@ type MissingRuntime struct { // Get a new MissingRuntime for the given name. // Requires a libpod Runtime so we can make a sane path for the exits dir. -func getMissingRuntime(name string, r *Runtime) (OCIRuntime, error) { +func getMissingRuntime(name string, r *Runtime) OCIRuntime { missingRuntimesLock.Lock() defer missingRuntimesLock.Unlock() @@ -42,7 +42,7 @@ func getMissingRuntime(name string, r *Runtime) (OCIRuntime, error) { runtime, ok := missingRuntimes[name] if ok { - return runtime, nil + return runtime } // Once for each missing runtime, we want to error. @@ -54,7 +54,7 @@ func getMissingRuntime(name string, r *Runtime) (OCIRuntime, error) { missingRuntimes[name] = newRuntime - return newRuntime, nil + return newRuntime } // Name is the name of the missing runtime @@ -163,6 +163,11 @@ func (r *MissingRuntime) CheckpointContainer(ctr *Container, options ContainerCh return r.printError() } +// CheckConmonRunning is not available as the runtime is missing +func (r *MissingRuntime) CheckConmonRunning(ctr *Container) (bool, error) { + return false, r.printError() +} + // SupportsCheckpoint returns false as checkpointing requires a working runtime func (r *MissingRuntime) SupportsCheckpoint() bool { return false diff --git a/libpod/oci_util.go b/libpod/oci_util.go index 53567d2d0..8b40dad81 100644 --- a/libpod/oci_util.go +++ b/libpod/oci_util.go @@ -36,14 +36,30 @@ func bindPorts(ports []ocicni.PortMapping) ([]*os.File, error) { var files []*os.File notifySCTP := false for _, i := range ports { + isV6 := net.ParseIP(i.HostIP).To4() == nil + if i.HostIP == "" { + isV6 = false + } switch i.Protocol { case "udp": - addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", i.HostIP, i.HostPort)) + var ( + addr *net.UDPAddr + err error + ) + if isV6 { + addr, err = net.ResolveUDPAddr("udp6", fmt.Sprintf("[%s]:%d", i.HostIP, i.HostPort)) + } else { + addr, err = net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%d", i.HostIP, i.HostPort)) + } if err != nil { return nil, errors.Wrapf(err, "cannot resolve the UDP address") } - server, err := net.ListenUDP("udp", addr) + proto := "udp4" + if isV6 { + proto = "udp6" + } + server, err := net.ListenUDP(proto, addr) if err != nil { return nil, errors.Wrapf(err, "cannot listen on the UDP port") } @@ -54,12 +70,24 @@ func bindPorts(ports []ocicni.PortMapping) ([]*os.File, error) { files = append(files, f) case "tcp": - addr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", i.HostIP, i.HostPort)) + var ( + addr *net.TCPAddr + err error + ) + if isV6 { + addr, err = net.ResolveTCPAddr("tcp6", fmt.Sprintf("[%s]:%d", i.HostIP, i.HostPort)) + } else { + addr, err = net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", i.HostIP, i.HostPort)) + } if err != nil { return nil, errors.Wrapf(err, "cannot resolve the TCP address") } - server, err := net.ListenTCP("tcp4", addr) + proto := "tcp4" + if isV6 { + proto = "tcp6" + } + server, err := net.ListenTCP(proto, addr) if err != nil { return nil, errors.Wrapf(err, "cannot listen on the TCP port") } diff --git a/libpod/options.go b/libpod/options.go index 8e0d3df86..5a0f60093 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1538,6 +1538,30 @@ func WithPodHostname(hostname string) PodCreateOption { } } +// WithPodCreateCommand adds the full command plus arguments of the current +// process to the pod config. +func WithPodCreateCommand() PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + pod.config.CreateCommand = os.Args + return nil + } +} + +// WithInfraConmonPidFile sets the path to a custom conmon PID file for the +// infra container. +func WithInfraConmonPidFile(path string) PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + pod.config.InfraContainer.ConmonPidFile = path + return nil + } +} + // WithPodLabels sets the labels of a pod. func WithPodLabels(labels map[string]string) PodCreateOption { return func(pod *Pod) error { diff --git a/libpod/pod.go b/libpod/pod.go index 8afaa6052..bf0d7a397 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -64,6 +64,10 @@ type PodConfig struct { // Time pod was created CreatedTime time.Time `json:"created"` + // CreateCommand is the full command plus arguments of the process the + // container has been created with. + CreateCommand []string `json:"CreateCommand,omitempty"` + // ID of the pod's lock LockID uint32 `json:"lockID"` } @@ -79,6 +83,7 @@ type podState struct { // InfraContainerConfig is the configuration for the pod's infra container type InfraContainerConfig struct { + ConmonPidFile string `json:"conmonPidFile"` HasInfraContainer bool `json:"makeInfraContainer"` HostNetwork bool `json:"infraHostNetwork,omitempty"` PortBindings []ocicni.PortMapping `json:"infraPortBindings"` @@ -124,6 +129,12 @@ func (p *Pod) CreatedTime() time.Time { return p.config.CreatedTime } +// CreateCommand returns the os.Args of the process with which the pod has been +// created. +func (p *Pod) CreateCommand() []string { + return p.config.CreateCommand +} + // CgroupParent returns the pod's CGroup parent func (p *Pod) CgroupParent() string { return p.config.CgroupParent @@ -246,6 +257,20 @@ func (p *Pod) InfraContainerID() (string, error) { return p.state.InfraContainerID, nil } +// InfraContainer returns the infra container. +func (p *Pod) InfraContainer() (*Container, error) { + if !p.HasInfraContainer() { + return nil, errors.Wrap(define.ErrNoSuchCtr, "pod has no infra container") + } + + id, err := p.InfraContainerID() + if err != nil { + return nil, err + } + + return p.runtime.state.Container(id) +} + // TODO add pod batching // Lock pod to avoid lock contention // Store and lock all containers (no RemoveContainer in batch guarantees cache will not become stale) diff --git a/libpod/pod_api.go b/libpod/pod_api.go index e2c4b515d..98f4cad73 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -432,10 +432,6 @@ func containerStatusFromContainers(allCtrs []*Container) (map[string]define.Cont // Inspect returns a PodInspect struct to describe the pod func (p *Pod) Inspect() (*define.InspectPodData, error) { - var ( - ctrs []define.InspectPodContainerInfo - ) - p.lock.Lock() defer p.lock.Unlock() if err := p.updatePod(); err != nil { @@ -446,6 +442,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { if err != nil { return nil, err } + ctrs := make([]define.InspectPodContainerInfo, 0, len(containers)) ctrStatuses := make(map[string]define.ContainerStatus, len(containers)) for _, c := range containers { containerStatus := "unknown" @@ -489,6 +486,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { Name: p.Name(), Namespace: p.Namespace(), Created: p.CreatedTime(), + CreateCommand: p.config.CreateCommand, State: podState, Hostname: p.config.Hostname, Labels: p.Labels(), diff --git a/libpod/pod_internal.go b/libpod/pod_internal.go index 851f52a4e..9e60d3c07 100644 --- a/libpod/pod_internal.go +++ b/libpod/pod_internal.go @@ -13,7 +13,7 @@ import ( ) // Creates a new, empty pod -func newPod(runtime *Runtime) (*Pod, error) { +func newPod(runtime *Runtime) *Pod { pod := new(Pod) pod.config = new(PodConfig) pod.config.ID = stringid.GenerateNonCryptoID() @@ -23,7 +23,7 @@ func newPod(runtime *Runtime) (*Pod, error) { pod.state = new(podState) pod.runtime = runtime - return pod, nil + return pod } // Update pod state from database diff --git a/libpod/runtime.go b/libpod/runtime.go index 4744de1a2..b1e48b3b3 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -286,9 +286,7 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { return errors.Wrapf(err, "error retrieving runtime configuration from database") } - if err := runtime.mergeDBConfig(dbConfig); err != nil { - return errors.Wrapf(err, "error merging database config into runtime config") - } + runtime.mergeDBConfig(dbConfig) logrus.Debugf("Using graph driver %s", runtime.storageConfig.GraphDriverName) logrus.Debugf("Using graph root %s", runtime.storageConfig.GraphRoot) @@ -696,11 +694,7 @@ func (r *Runtime) configureStore() error { // Set up a storage service for creating container root filesystems from // images - storageService, err := getStorageService(r.store) - if err != nil { - return err - } - r.storageService = storageService + r.storageService = getStorageService(r.store) ir := image.NewImageRuntimeFromStore(r.store) ir.SignaturePolicyPath = r.config.Engine.SignaturePolicyPath @@ -751,7 +745,7 @@ type DBConfig struct { } // mergeDBConfig merges the configuration from the database. -func (r *Runtime) mergeDBConfig(dbConfig *DBConfig) error { +func (r *Runtime) mergeDBConfig(dbConfig *DBConfig) { c := &r.config.Engine if !r.storageSet.RunRootSet && dbConfig.StorageTmp != "" { @@ -802,7 +796,6 @@ func (r *Runtime) mergeDBConfig(dbConfig *DBConfig) error { } c.VolumePath = dbConfig.VolumePath } - return nil } func (r *Runtime) EnableLabeling() bool { diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 655b42e51..f0beb0941 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -464,9 +464,11 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool, } } - // Check that the container's in a good state to be removed + // Check that the container's in a good state to be removed. if c.state.State == define.ContainerStateRunning { - if err := c.stop(c.StopTimeout()); err != nil { + // Ignore ErrConmonDead - we couldn't retrieve the container's + // exit code properly, but it's still stopped. + if err := c.stop(c.StopTimeout()); err != nil && errors.Cause(err) != define.ErrConmonDead { return errors.Wrapf(err, "cannot remove container %s as it could not be stopped", c.ID()) } } @@ -811,7 +813,7 @@ func (r *Runtime) GetRunningContainers() ([]*Container, error) { // GetContainersByList is a helper function for GetContainers // which takes a []string of container IDs or names func (r *Runtime) GetContainersByList(containers []string) ([]*Container, error) { - var ctrs []*Container + ctrs := make([]*Container, 0, len(containers)) for _, inputContainer := range containers { ctr, err := r.LookupContainer(inputContainer) if err != nil { diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index 06a7b3936..a0dee3aa1 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -130,6 +130,9 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm options = append(options, WithRootFSFromImage(imgID, imgName, rawImageName)) options = append(options, WithName(containerName)) options = append(options, withIsInfra()) + if len(p.config.InfraContainer.ConmonPidFile) > 0 { + options = append(options, WithConmonPidFile(p.config.InfraContainer.ConmonPidFile)) + } return r.newContainer(ctx, g.Config, options...) } diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go index 73b6c5d9b..25584c5ad 100644 --- a/libpod/runtime_pod_linux.go +++ b/libpod/runtime_pod_linux.go @@ -28,10 +28,7 @@ func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (_ *Po return nil, define.ErrRuntimeStopped } - pod, err := newPod(r) - if err != nil { - return nil, errors.Wrapf(err, "error creating pod") - } + pod := newPod(r) // Set default namespace to runtime's namespace // Do so before options run so they can override it diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go index d4b46cc94..f6ecae4ab 100644 --- a/libpod/runtime_volume_linux.go +++ b/libpod/runtime_volume_linux.go @@ -29,11 +29,7 @@ func (r *Runtime) NewVolume(ctx context.Context, options ...VolumeCreateOption) // newVolume creates a new empty volume func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption) (_ *Volume, deferredErr error) { - volume, err := newVolume(r) - if err != nil { - return nil, errors.Wrapf(err, "error creating volume") - } - + volume := newVolume(r) for _, option := range options { if err := option(volume); err != nil { return nil, errors.Wrapf(err, "error running volume create option") diff --git a/libpod/storage.go b/libpod/storage.go index 34e40f699..c90020833 100644 --- a/libpod/storage.go +++ b/libpod/storage.go @@ -21,8 +21,8 @@ type storageService struct { // getStorageService returns a storageService which can create container root // filesystems from images -func getStorageService(store storage.Store) (*storageService, error) { - return &storageService{store: store}, nil +func getStorageService(store storage.Store) *storageService { + return &storageService{store: store} } // ContainerInfo wraps a subset of information about a container: the locations diff --git a/libpod/volume_internal.go b/libpod/volume_internal.go index 781ff77ca..d7d5a2494 100644 --- a/libpod/volume_internal.go +++ b/libpod/volume_internal.go @@ -9,7 +9,7 @@ import ( ) // Creates a new volume -func newVolume(runtime *Runtime) (*Volume, error) { +func newVolume(runtime *Runtime) *Volume { volume := new(Volume) volume.config = new(VolumeConfig) volume.state = new(VolumeState) @@ -17,8 +17,7 @@ func newVolume(runtime *Runtime) (*Volume, error) { volume.config.Labels = make(map[string]string) volume.config.Options = make(map[string]string) volume.state.NeedsCopyUp = true - - return volume, nil + return volume } // teardownStorage deletes the volume from volumePath |