diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/boltdb_state_internal.go | 9 | ||||
-rw-r--r-- | libpod/container.go | 13 | ||||
-rw-r--r-- | libpod/container_api.go | 2 | ||||
-rw-r--r-- | libpod/container_config.go | 2 | ||||
-rw-r--r-- | libpod/container_inspect.go | 15 | ||||
-rw-r--r-- | libpod/container_internal.go | 19 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 43 | ||||
-rw-r--r-- | libpod/container_validate.go | 10 | ||||
-rw-r--r-- | libpod/define/errors.go | 3 | ||||
-rw-r--r-- | libpod/define/podstate.go | 7 | ||||
-rw-r--r-- | libpod/events.go | 6 | ||||
-rw-r--r-- | libpod/events/config.go | 12 | ||||
-rw-r--r-- | libpod/events/events.go | 9 | ||||
-rw-r--r-- | libpod/events/filters.go | 20 | ||||
-rw-r--r-- | libpod/events/journal_linux.go | 71 | ||||
-rw-r--r-- | libpod/image/filters.go | 4 | ||||
-rw-r--r-- | libpod/image/image.go | 10 | ||||
-rw-r--r-- | libpod/image/search.go | 2 | ||||
-rw-r--r-- | libpod/image/utils.go | 3 | ||||
-rw-r--r-- | libpod/networking_linux.go | 6 | ||||
-rw-r--r-- | libpod/options.go | 19 | ||||
-rw-r--r-- | libpod/pod_api.go | 2 | ||||
-rw-r--r-- | libpod/pod_status.go | 8 |
23 files changed, 242 insertions, 53 deletions
diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go index e195ca314..2f485318c 100644 --- a/libpod/boltdb_state_internal.go +++ b/libpod/boltdb_state_internal.go @@ -3,6 +3,7 @@ package libpod import ( "bytes" "os" + "path/filepath" "runtime" "strings" @@ -104,25 +105,25 @@ func checkRuntimeConfig(db *bolt.DB, rt *Runtime) error { }, { "libpod root directory (staticdir)", - rt.config.Engine.StaticDir, + filepath.Clean(rt.config.Engine.StaticDir), staticDirKey, "", }, { "libpod temporary files directory (tmpdir)", - rt.config.Engine.TmpDir, + filepath.Clean(rt.config.Engine.TmpDir), tmpDirKey, "", }, { "storage temporary directory (runroot)", - rt.StorageConfig().RunRoot, + filepath.Clean(rt.StorageConfig().RunRoot), runRootKey, storeOpts.RunRoot, }, { "storage graph root directory (graphroot)", - rt.StorageConfig().GraphRoot, + filepath.Clean(rt.StorageConfig().GraphRoot), graphRootKey, storeOpts.GraphRoot, }, diff --git a/libpod/container.go b/libpod/container.go index 01419500e..ea5a6e09c 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -235,6 +235,19 @@ type ContainerOverlayVolume struct { Source string `json:"source,omitempty"` } +// ContainerImageVolume is a volume based on a container image. The container +// image is first mounted on the host and is then bind-mounted into the +// container. +type ContainerImageVolume struct { + // Source is the source of the image volume. The image can be referred + // to by name and by ID. + Source string `json:"source"` + // Dest is the absolute path of the mount in the container. + Dest string `json:"dest"` + // ReadWrite sets the volume writable. + ReadWrite bool `json:"rw"` +} + // Config accessors // Unlocked diff --git a/libpod/container_api.go b/libpod/container_api.go index aef37dd59..a9808a30e 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -249,7 +249,7 @@ func (c *Container) Attach(streams *define.AttachStreams, keys string, resize <- // attaching, and I really do not want to do that right now. // Send a SIGWINCH after attach succeeds so that most programs will // redraw the screen for the new attach session. - attachRdy := make(chan bool) + attachRdy := make(chan bool, 1) if c.config.Spec.Process != nil && c.config.Spec.Process.Terminal { go func() { <-attachRdy diff --git a/libpod/container_config.go b/libpod/container_config.go index e264da4da..0006613bc 100644 --- a/libpod/container_config.go +++ b/libpod/container_config.go @@ -134,6 +134,8 @@ type ContainerRootFSConfig struct { NamedVolumes []*ContainerNamedVolume `json:"namedVolumes,omitempty"` // OverlayVolumes lists the overlay volumes to mount into the container. OverlayVolumes []*ContainerOverlayVolume `json:"overlayVolumes,omitempty"` + // ImageVolumes lists the image volumes to mount into the container. + ImageVolumes []*ContainerImageVolume `json:"imageVolumes,omitempty"` // CreateWorkingDir indicates that Libpod should create the container's // working directory if it does not exist. Some OCI runtimes do this by // default, but others do not. diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index b8bce1272..162f70326 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(namedVolumes, mounts) + inspectMounts, err := c.getInspectMounts(namedVolumes, c.config.ImageVolumes, mounts) if err != nil { return nil, err } @@ -192,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(namedVolumes []*ContainerNamedVolume, mounts []spec.Mount) ([]define.InspectMount, error) { +func (c *Container) getInspectMounts(namedVolumes []*ContainerNamedVolume, imageVolumes []*ContainerImageVolume, mounts []spec.Mount) ([]define.InspectMount, error) { inspectMounts := []define.InspectMount{} // No mounts, return early @@ -219,6 +219,17 @@ func (c *Container) getInspectMounts(namedVolumes []*ContainerNamedVolume, mount inspectMounts = append(inspectMounts, mountStruct) } + + for _, volume := range imageVolumes { + mountStruct := define.InspectMount{} + mountStruct.Type = "image" + mountStruct.Destination = volume.Dest + mountStruct.Source = volume.Source + mountStruct.RW = volume.ReadWrite + + inspectMounts = append(inspectMounts, mountStruct) + } + for _, mount := range mounts { // It's a mount. // Is it a tmpfs? If so, discard. diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 4ae571de6..cafe70b80 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -1734,6 +1734,25 @@ func (c *Container) cleanup(ctx context.Context) error { } } + // Unmount image volumes + for _, v := range c.config.ImageVolumes { + img, err := c.runtime.ImageRuntime().NewFromLocal(v.Source) + if err != nil { + if lastError == nil { + lastError = err + continue + } + logrus.Errorf("error unmounting image volume %q:%q :%v", v.Source, v.Dest, err) + } + if err := img.Unmount(false); err != nil { + if lastError == nil { + lastError = err + continue + } + logrus.Errorf("error unmounting image volume %q:%q :%v", v.Source, v.Dest, err) + } + } + return lastError } diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index ffb2f5b73..57d5100cf 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -39,6 +39,7 @@ import ( "github.com/containers/storage/pkg/idtools" securejoin "github.com/cyphar/filepath-securejoin" runcuser "github.com/opencontainers/runc/libcontainer/user" + "github.com/opencontainers/runtime-spec/specs-go" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/selinux/go-selinux/label" @@ -368,6 +369,35 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { g.AddMount(overlayMount) } + // Add image volumes as overlay mounts + for _, volume := range c.config.ImageVolumes { + // Mount the specified image. + img, err := c.runtime.ImageRuntime().NewFromLocal(volume.Source) + if err != nil { + return nil, errors.Wrapf(err, "error creating image volume %q:%q", volume.Source, volume.Dest) + } + mountPoint, err := img.Mount(nil, "") + if err != nil { + return nil, errors.Wrapf(err, "error mounting image volume %q:%q", volume.Source, volume.Dest) + } + + contentDir, err := overlay.TempDir(c.config.StaticDir, c.RootUID(), c.RootGID()) + if err != nil { + return nil, errors.Wrapf(err, "failed to create TempDir in the %s directory", c.config.StaticDir) + } + + var overlayMount specs.Mount + if volume.ReadWrite { + overlayMount, err = overlay.Mount(contentDir, mountPoint, volume.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions()) + } else { + overlayMount, err = overlay.MountReadOnly(contentDir, mountPoint, volume.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions()) + } + if err != nil { + return nil, errors.Wrapf(err, "creating overlay mount for image %q failed", volume.Source) + } + g.AddMount(overlayMount) + } + hasHomeSet := false for _, s := range c.config.Spec.Process.Env { if strings.HasPrefix(s, "HOME=") { @@ -1412,7 +1442,8 @@ func (c *Container) generateResolvConf() (string, error) { // Determine the endpoint for resolv.conf in case it is a symlink resolvPath, err := filepath.EvalSymlinks(resolvConf) - if err != nil { + // resolv.conf doesn't have to exists + if err != nil && !os.IsNotExist(err) { return "", err } @@ -1422,7 +1453,8 @@ func (c *Container) generateResolvConf() (string, error) { } contents, err := ioutil.ReadFile(resolvPath) - if err != nil { + // resolv.conf doesn't have to exists + if err != nil && !os.IsNotExist(err) { return "", errors.Wrapf(err, "unable to read %s", resolvPath) } @@ -1550,9 +1582,13 @@ func (c *Container) getHosts() string { hosts += fmt.Sprintf("# used by slirp4netns\n%s\t%s %s\n", "10.0.2.100", c.Hostname(), c.config.Name) } else { hasNetNS := false + netNone := false for _, ns := range c.config.Spec.Linux.Namespaces { if ns.Type == spec.NetworkNamespace { hasNetNS = true + if ns.Path == "" && !c.config.CreateNetNS { + netNone = true + } break } } @@ -1564,6 +1600,9 @@ func (c *Container) getHosts() string { } hosts += fmt.Sprintf("127.0.1.1 %s\n", osHostname) } + if netNone { + hosts += fmt.Sprintf("127.0.1.1 %s\n", c.Hostname()) + } } } return hosts diff --git a/libpod/container_validate.go b/libpod/container_validate.go index b78168cd1..68cc095b7 100644 --- a/libpod/container_validate.go +++ b/libpod/container_validate.go @@ -88,7 +88,7 @@ func (c *Container) validate() error { return errors.Wrapf(define.ErrInvalidArg, "cannot add to /etc/hosts if using image's /etc/hosts") } - // Check named volume and overlay volumes destination conflits + // Check named volume, overlay volume and image volume destination conflits destinations := make(map[string]bool) for _, vol := range c.config.NamedVolumes { // Don't check if they already exist. @@ -106,6 +106,14 @@ func (c *Container) validate() error { } destinations[vol.Dest] = true } + for _, vol := range c.config.ImageVolumes { + // Don't check if they already exist. + // If they don't we will automatically create them. + if _, ok := destinations[vol.Dest]; ok { + return errors.Wrapf(define.ErrInvalidArg, "two volumes found with destination %s", vol.Dest) + } + destinations[vol.Dest] = true + } return nil } diff --git a/libpod/define/errors.go b/libpod/define/errors.go index 627928ef7..300e0d7ca 100644 --- a/libpod/define/errors.go +++ b/libpod/define/errors.go @@ -14,6 +14,9 @@ var ( // ErrNoSuchImage indicates the requested image does not exist ErrNoSuchImage = errors.New("no such image") + // ErrMultipleImages found multiple name and tag matches + ErrMultipleImages = errors.New("found multiple name and tag matches") + // ErrNoSuchTag indicates the requested image tag does not exist ErrNoSuchTag = errors.New("no such tag") diff --git a/libpod/define/podstate.go b/libpod/define/podstate.go index 2b59aabfb..e02671972 100644 --- a/libpod/define/podstate.go +++ b/libpod/define/podstate.go @@ -10,9 +10,12 @@ const ( PodStateExited = "Exited" // PodStatePaused indicates the pod has been paused PodStatePaused = "Paused" - // PodStateRunning indicates that one or more of the containers in - // the pod is running + // PodStateRunning indicates that all of the containers in the pod are + // running. PodStateRunning = "Running" + // PodStateDegraded indicates that at least one, but not all, of the + // containers in the pod are running. + PodStateDegraded = "Degraded" // PodStateStopped indicates all of the containers belonging to the pod // are stopped. PodStateStopped = "Stopped" diff --git a/libpod/events.go b/libpod/events.go index b519fe324..95317eb01 100644 --- a/libpod/events.go +++ b/libpod/events.go @@ -26,6 +26,12 @@ func (c *Container) newContainerEvent(status events.Status) { e.Name = c.Name() e.Image = c.config.RootfsImageName e.Type = events.Container + + e.Details = events.Details{ + ID: e.ID, + Attributes: c.Labels(), + } + if err := c.runtime.eventer.Write(e); err != nil { logrus.Errorf("unable to write pod event: %q", err) } diff --git a/libpod/events/config.go b/libpod/events/config.go index bb35c03c0..2ec3111fe 100644 --- a/libpod/events/config.go +++ b/libpod/events/config.go @@ -36,6 +36,18 @@ type Event struct { Time time.Time // Type of event that occurred Type Type + + Details +} + +// Details describes specifics about certain events, specifically around +// container events +type Details struct { + // ID is the event ID + ID string + // Attributes can be used to describe specifics about the event + // in the case of a container event, labels for example + Attributes map[string]string } // EventerOptions describe options that need to be passed to create diff --git a/libpod/events/events.go b/libpod/events/events.go index 722c9595e..42939d64c 100644 --- a/libpod/events/events.go +++ b/libpod/events/events.go @@ -69,7 +69,14 @@ func (e *Event) ToHumanReadable() string { var humanFormat string switch e.Type { case Container, Pod: - humanFormat = fmt.Sprintf("%s %s %s %s (image=%s, name=%s)", e.Time, e.Type, e.Status, e.ID, e.Image, e.Name) + humanFormat = fmt.Sprintf("%s %s %s %s (image=%s, name=%s", e.Time, e.Type, e.Status, e.ID, e.Image, e.Name) + // check if the container has labels and add it to the output + if len(e.Attributes) > 0 { + for k, v := range e.Attributes { + humanFormat += fmt.Sprintf(", %s=%s", k, v) + } + } + humanFormat += ")" case Image: humanFormat = fmt.Sprintf("%s %s %s %s %s", e.Time, e.Type, e.Status, e.ID, e.Name) case System: diff --git a/libpod/events/filters.go b/libpod/events/filters.go index c50474007..62891d32c 100644 --- a/libpod/events/filters.go +++ b/libpod/events/filters.go @@ -55,6 +55,24 @@ func generateEventFilter(filter, filterValue string) (func(e *Event) bool, error return func(e *Event) bool { return string(e.Type) == filterValue }, nil + + case "LABEL": + return func(e *Event) bool { + var found bool + // iterate labels and see if we match a key and value + for eventKey, eventValue := range e.Attributes { + filterValueSplit := strings.SplitN(filterValue, "=", 2) + // if the filter isn't right, just return false + if len(filterValueSplit) < 2 { + return false + } + if eventKey == filterValueSplit[0] && eventValue == filterValueSplit[1] { + found = true + break + } + } + return found + }, nil } return nil, errors.Errorf("%s is an invalid filter", filter) } @@ -73,7 +91,7 @@ func generateEventUntilOption(timeUntil time.Time) func(e *Event) bool { } func parseFilter(filter string) (string, string, error) { - filterSplit := strings.Split(filter, "=") + filterSplit := strings.SplitN(filter, "=", 2) if len(filterSplit) != 2 { return "", "", errors.Errorf("%s is an invalid filter", filter) } diff --git a/libpod/events/journal_linux.go b/libpod/events/journal_linux.go index dc55dbc77..5e3be8009 100644 --- a/libpod/events/journal_linux.go +++ b/libpod/events/journal_linux.go @@ -4,6 +4,7 @@ package events import ( "context" + "encoding/json" "strconv" "time" @@ -46,6 +47,15 @@ func (e EventJournalD) Write(ee Event) error { if ee.ContainerExitCode != 0 { m["PODMAN_EXIT_CODE"] = strconv.Itoa(ee.ContainerExitCode) } + // If we have container labels, we need to convert them to a string so they + // can be recorded with the event + if len(ee.Details.Attributes) > 0 { + b, err := json.Marshal(ee.Details.Attributes) + if err != nil { + return err + } + m["PODMAN_LABELS"] = string(b) + } case Volume: m["PODMAN_NAME"] = ee.Name } @@ -59,35 +69,39 @@ func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error { if err != nil { return errors.Wrapf(err, "failed to generate event options") } - j, err := sdjournal.NewJournal() //nolint + j, err := sdjournal.NewJournal() if err != nil { return err } - // TODO AddMatch and Seek seem to conflict - // Issue filed upstream -> https://github.com/coreos/go-systemd/issues/315 - // Leaving commented code in case upstream fixes things - //podmanJournal := sdjournal.Match{Field: "SYSLOG_IDENTIFIER", Value: "podman"} //nolint - //if err := j.AddMatch(podmanJournal.String()); err != nil { - // return errors.Wrap(err, "failed to add filter for event log") - //} + + // match only podman journal entries + podmanJournal := sdjournal.Match{Field: "SYSLOG_IDENTIFIER", Value: "podman"} + if err := j.AddMatch(podmanJournal.String()); err != nil { + return errors.Wrap(err, "failed to add journal filter for event log") + } + if len(options.Since) == 0 && len(options.Until) == 0 && options.Stream { if err := j.SeekTail(); err != nil { return errors.Wrap(err, "failed to seek end of journal") } - } else { - podmanJournal := sdjournal.Match{Field: "SYSLOG_IDENTIFIER", Value: "podman"} //nolint - if err := j.AddMatch(podmanJournal.String()); err != nil { - return errors.Wrap(err, "failed to add filter for event log") + // After SeekTail calling Next moves to a random entry. + // To prevent this we have to call Previous first. + // see: https://bugs.freedesktop.org/show_bug.cgi?id=64614 + if _, err := j.Previous(); err != nil { + return errors.Wrap(err, "failed to move journal cursor to previous entry") } } + // the api requires a next|prev before getting a cursor if _, err := j.Next(); err != nil { - return err + return errors.Wrap(err, "failed to move journal cursor to next entry") } + prevCursor, err := j.GetCursor() if err != nil { - return err + return errors.Wrap(err, "failed to get journal cursor") } + for { select { case <-ctx.Done(): @@ -96,30 +110,26 @@ func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error { default: // fallthrough } + if _, err := j.Next(); err != nil { - return err + return errors.Wrap(err, "failed to move journal cursor to next entry") } newCursor, err := j.GetCursor() if err != nil { - return err + return errors.Wrap(err, "failed to get journal cursor") } if prevCursor == newCursor { if len(options.Until) > 0 || !options.Stream { break } - _ = j.Wait(sdjournal.IndefiniteWait) //nolint + _ = j.Wait(sdjournal.IndefiniteWait) continue } prevCursor = newCursor + entry, err := j.GetEntry() if err != nil { - return err - } - // TODO this keeps us from feeding the podman event parser with - // with regular journal content; it can be removed if the above - // problem with AddMatch is resolved. - if entry.Fields["PODMAN_EVENT"] == "" { - continue + return errors.Wrap(err, "failed to read journal entry") } newEvent, err := newEventFromJournalEntry(entry) if err != nil { @@ -174,6 +184,19 @@ func newEventFromJournalEntry(entry *sdjournal.JournalEntry) (*Event, error) { / newEvent.ContainerExitCode = intCode } } + + // we need to check for the presence of labels recorded to a container event + if stringLabels, ok := entry.Fields["PODMAN_LABELS"]; ok && len(stringLabels) > 0 { + labels := make(map[string]string, 0) + if err := json.Unmarshal([]byte(stringLabels), &labels); err != nil { + return nil, err + } + + // if we have labels, add them to the event + if len(labels) > 0 { + newEvent.Details = Details{Attributes: labels} + } + } case Image: newEvent.ID = entry.Fields["PODMAN_ID"] } diff --git a/libpod/image/filters.go b/libpod/image/filters.go index db647954f..4aff0a7b5 100644 --- a/libpod/image/filters.go +++ b/libpod/image/filters.go @@ -82,7 +82,7 @@ func LabelFilter(ctx context.Context, labelfilter string) ResultFilter { // We need to handle both label=key and label=key=value return func(i *Image) bool { var value string - splitFilter := strings.Split(labelfilter, "=") + splitFilter := strings.SplitN(labelfilter, "=", 2) key := splitFilter[0] if len(splitFilter) > 1 { value = splitFilter[1] @@ -157,7 +157,7 @@ func (ir *Runtime) createFilterFuncs(filters []string, img *Image) ([]ResultFilt var filterFuncs []ResultFilter ctx := context.Background() for _, filter := range filters { - splitFilter := strings.Split(filter, "=") + splitFilter := strings.SplitN(filter, "=", 2) if len(splitFilter) < 2 { return nil, errors.Errorf("invalid filter syntax %s", filter) } diff --git a/libpod/image/image.go b/libpod/image/image.go index 0900944eb..301954703 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -177,7 +177,7 @@ func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile // SaveImages stores one more images in a multi-image archive. // Note that only `docker-archive` supports storing multiple // image. -func (ir *Runtime) SaveImages(ctx context.Context, namesOrIDs []string, format string, outputFile string, quiet bool) (finalErr error) { +func (ir *Runtime) SaveImages(ctx context.Context, namesOrIDs []string, format string, outputFile string, quiet, removeSignatures bool) (finalErr error) { if format != DockerArchive { return errors.Errorf("multi-image archives are only supported in in the %q format", DockerArchive) } @@ -264,7 +264,7 @@ func (ir *Runtime) SaveImages(ctx context.Context, namesOrIDs []string, format s } img := imageMap[id] - copyOptions := getCopyOptions(sys, writer, nil, nil, SigningOptions{}, "", img.tags) + copyOptions := getCopyOptions(sys, writer, nil, nil, SigningOptions{RemoveSignatures: removeSignatures}, "", img.tags) copyOptions.DestinationCtx.SystemRegistriesConfPath = registries.SystemRegistriesConfPath() // For copying, we need a source reference that we can create @@ -469,7 +469,7 @@ func (ir *Runtime) getLocalImage(inputName string) (string, *storage.Image, erro if err != nil { return "", nil, err } - img, err := ir.store.Image(ref.String()) + img, err := ir.store.Image(reference.TagNameOnly(ref).String()) if err == nil { return ref.String(), img, nil } @@ -1584,7 +1584,7 @@ func (i *Image) Comment(ctx context.Context, manifestType string) (string, error } // Save writes a container image to the filesystem -func (i *Image) Save(ctx context.Context, source, format, output string, moreTags []string, quiet, compress bool) error { +func (i *Image) Save(ctx context.Context, source, format, output string, moreTags []string, quiet, compress, removeSignatures bool) error { var ( writer io.Writer destRef types.ImageReference @@ -1636,7 +1636,7 @@ func (i *Image) Save(ctx context.Context, source, format, output string, moreTag return err } } - if err := i.PushImageToReference(ctx, destRef, manifestType, "", "", "", writer, compress, SigningOptions{}, &DockerRegistryOptions{}, additionaltags); err != nil { + if err := i.PushImageToReference(ctx, destRef, manifestType, "", "", "", writer, compress, SigningOptions{RemoveSignatures: removeSignatures}, &DockerRegistryOptions{}, additionaltags); err != nil { return errors.Wrapf(err, "unable to save %q", source) } i.newImageEvent(events.Save) diff --git a/libpod/image/search.go b/libpod/image/search.go index 5f5845989..b9acf4a20 100644 --- a/libpod/image/search.go +++ b/libpod/image/search.go @@ -263,7 +263,7 @@ func searchRepositoryTags(registry, term string, sc *types.SystemContext, option func ParseSearchFilter(filter []string) (*SearchFilter, error) { sFilter := new(SearchFilter) for _, f := range filter { - arr := strings.Split(f, "=") + arr := strings.SplitN(f, "=", 2) switch arr[0] { case "stars": if len(arr) < 2 { diff --git a/libpod/image/utils.go b/libpod/image/utils.go index 2538f429b..7429a7f10 100644 --- a/libpod/image/utils.go +++ b/libpod/image/utils.go @@ -11,6 +11,7 @@ import ( "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/signature" "github.com/containers/image/v5/types" + "github.com/containers/podman/v2/libpod/define" "github.com/containers/storage" "github.com/pkg/errors" ) @@ -42,7 +43,7 @@ func findImageInRepotags(search imageParts, images []*Image) (*storage.Image, er if len(results) == 0 { return &storage.Image{}, errors.Errorf("unable to find a name and tag match for %s in repotags", searchName) } else if len(results) > 1 { - return &storage.Image{}, errors.Errorf("found multiple name and tag matches for %s in repotags", searchName) + return &storage.Image{}, errors.Wrapf(define.ErrMultipleImages, searchName) } return results[0], nil } diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index df0ff6c32..9ff6e40b7 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -254,9 +254,11 @@ func (r *Runtime) setupSlirp4netns(ctr *Container) error { if ctr.config.NetworkOptions != nil { slirpOptions := ctr.config.NetworkOptions["slirp4netns"] for _, o := range slirpOptions { - parts := strings.Split(o, "=") + parts := strings.SplitN(o, "=", 2) + if len(parts) < 2 { + return errors.Errorf("unknown option for slirp4netns: %q", o) + } option, value := parts[0], parts[1] - switch option { case "cidr": ipv4, _, err := net.ParseCIDR(value) diff --git a/libpod/options.go b/libpod/options.go index 1ffb78da9..5d1ce8755 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1439,6 +1439,25 @@ func WithOverlayVolumes(volumes []*ContainerOverlayVolume) CtrCreateOption { } } +// WithImageVolumes adds the given image volumes to the container. +func WithImageVolumes(volumes []*ContainerImageVolume) CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return define.ErrCtrFinalized + } + + for _, vol := range volumes { + ctr.config.ImageVolumes = append(ctr.config.ImageVolumes, &ContainerImageVolume{ + Dest: vol.Dest, + Source: vol.Source, + ReadWrite: vol.ReadWrite, + }) + } + + return nil + } +} + // WithHealthCheck adds the healthcheck to the container config func WithHealthCheck(healthCheck *manifest.Schema2HealthConfig) CtrCreateOption { return func(ctr *Container) error { diff --git a/libpod/pod_api.go b/libpod/pod_api.go index f2ddba9c9..87ac5c07a 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -506,7 +506,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { }) ctrStatuses[c.ID()] = c.state.State } - podState, err := CreatePodStatusResults(ctrStatuses) + podState, err := createPodStatusResults(ctrStatuses) if err != nil { return nil, err } diff --git a/libpod/pod_status.go b/libpod/pod_status.go index f4ccf308a..668d45ec7 100644 --- a/libpod/pod_status.go +++ b/libpod/pod_status.go @@ -10,10 +10,10 @@ func (p *Pod) GetPodStatus() (string, error) { if err != nil { return define.PodStateErrored, err } - return CreatePodStatusResults(ctrStatuses) + return createPodStatusResults(ctrStatuses) } -func CreatePodStatusResults(ctrStatuses map[string]define.ContainerStatus) (string, error) { +func createPodStatusResults(ctrStatuses map[string]define.ContainerStatus) (string, error) { ctrNum := len(ctrStatuses) if ctrNum == 0 { return define.PodStateCreated, nil @@ -43,8 +43,10 @@ func CreatePodStatusResults(ctrStatuses map[string]define.ContainerStatus) (stri } switch { - case statuses[define.PodStateRunning] > 0: + case statuses[define.PodStateRunning] == ctrNum: return define.PodStateRunning, nil + case statuses[define.PodStateRunning] > 0: + return define.PodStateDegraded, nil case statuses[define.PodStatePaused] == ctrNum: return define.PodStatePaused, nil case statuses[define.PodStateStopped] == ctrNum: |