summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/boltdb_state_internal.go9
-rw-r--r--libpod/container.go13
-rw-r--r--libpod/container_api.go2
-rw-r--r--libpod/container_config.go2
-rw-r--r--libpod/container_inspect.go15
-rw-r--r--libpod/container_internal.go19
-rw-r--r--libpod/container_internal_linux.go43
-rw-r--r--libpod/container_validate.go10
-rw-r--r--libpod/define/errors.go3
-rw-r--r--libpod/define/podstate.go7
-rw-r--r--libpod/events.go6
-rw-r--r--libpod/events/config.go12
-rw-r--r--libpod/events/events.go9
-rw-r--r--libpod/events/filters.go20
-rw-r--r--libpod/events/journal_linux.go71
-rw-r--r--libpod/image/filters.go4
-rw-r--r--libpod/image/image.go10
-rw-r--r--libpod/image/search.go2
-rw-r--r--libpod/image/utils.go3
-rw-r--r--libpod/networking_linux.go6
-rw-r--r--libpod/options.go19
-rw-r--r--libpod/pod_api.go2
-rw-r--r--libpod/pod_status.go8
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: