summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/boltdb_state.go12
-rw-r--r--libpod/boltdb_state_internal.go12
-rw-r--r--libpod/container_config.go125
-rw-r--r--libpod/container_internal_linux.go27
-rw-r--r--libpod/define/errors.go16
-rw-r--r--libpod/events/journal_linux.go3
-rw-r--r--libpod/image/errors.go11
-rw-r--r--libpod/image/image.go7
-rw-r--r--libpod/image/pull.go19
-rw-r--r--libpod/options.go13
10 files changed, 190 insertions, 55 deletions
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go
index e98a6e907..2575f0e86 100644
--- a/libpod/boltdb_state.go
+++ b/libpod/boltdb_state.go
@@ -2347,11 +2347,19 @@ func (s *BoltState) AddPod(pod *Pod) error {
// Check if we already have something with the given ID and name
idExist := idsBkt.Get(podID)
if idExist != nil {
- return errors.Wrapf(define.ErrPodExists, "ID %s is in use", pod.ID())
+ err = define.ErrPodExists
+ if allPodsBkt.Get(idExist) == nil {
+ err = define.ErrCtrExists
+ }
+ return errors.Wrapf(err, "ID \"%s\" is in use", pod.ID())
}
nameExist := namesBkt.Get(podName)
if nameExist != nil {
- return errors.Wrapf(define.ErrPodExists, "name %s is in use", pod.Name())
+ err = define.ErrPodExists
+ if allPodsBkt.Get(nameExist) == nil {
+ err = define.ErrCtrExists
+ }
+ return errors.Wrapf(err, "name \"%s\" is in use", pod.Name())
}
// We are good to add the pod
diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go
index ddbd40da8..9be753d26 100644
--- a/libpod/boltdb_state_internal.go
+++ b/libpod/boltdb_state_internal.go
@@ -586,11 +586,19 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
// Check if we already have a container with the given ID and name
idExist := idsBucket.Get(ctrID)
if idExist != nil {
- return errors.Wrapf(define.ErrCtrExists, "ID %s is in use", ctr.ID())
+ err = define.ErrCtrExists
+ if allCtrsBucket.Get(idExist) == nil {
+ err = define.ErrPodExists
+ }
+ return errors.Wrapf(err, "ID \"%s\" is in use", ctr.ID())
}
nameExist := namesBucket.Get(ctrName)
if nameExist != nil {
- return errors.Wrapf(define.ErrCtrExists, "name %s is in use", ctr.Name())
+ err = define.ErrCtrExists
+ if allCtrsBucket.Get(nameExist) == nil {
+ err = define.ErrPodExists
+ }
+ return errors.Wrapf(err, "name \"%s\" is in use", ctr.Name())
}
// No overlapping containers
diff --git a/libpod/container_config.go b/libpod/container_config.go
index 301b867fc..5f89395c1 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -13,25 +13,52 @@ import (
// ContainerConfig contains all information that was used to create the
// container. It may not be changed once created.
-// It is stored, read-only, on disk
+// It is stored, read-only, on disk in Libpod's State.
+// Any changes will not be written back to the database, and will cause
+// inconsistencies with other Libpod instances.
type ContainerConfig struct {
+ // Spec is OCI runtime spec used to create the container. This is passed
+ // in when the container is created, but it is not the final spec used
+ // to run the container - it will be modified by Libpod to add things we
+ // manage (e.g. bind mounts for /etc/resolv.conf, named volumes, a
+ // network namespace prepared by CNI or slirp4netns) in the
+ // generateSpec() function.
Spec *spec.Spec `json:"spec"`
+ // ID is a hex-encoded 256-bit pseudorandom integer used as a unique
+ // identifier for the container. IDs are globally unique in Libpod -
+ // once an ID is in use, no other container or pod will be created with
+ // the same one until the holder of the ID has been removed.
+ // ID is generated by Libpod, and cannot be chosen or influenced by the
+ // user (except when restoring a checkpointed container).
+ // ID is guaranteed to be 64 characters long.
ID string `json:"id"`
+ // Name is a human-readable name for the container. All containers must
+ // have a non-empty name. Name may be provided when the container is
+ // created; if no name is chosen, a name will be auto-generated.
Name string `json:"name"`
- // Full ID of the pood the container belongs to
+ // Pod is the full ID of the pod the container belongs to. If the
+ // container does not belong to a pod, this will be empty.
+ // If this is not empty, a pod with this ID is guaranteed to exist in
+ // the state for the duration of this container's existence.
Pod string `json:"pod,omitempty"`
- // Namespace the container is in
+ // Namespace is the libpod Namespace the container is in.
+ // Namespaces are used to divide containers in the state.
Namespace string `json:"namespace,omitempty"`
- // ID of this container's lock
+ // LockID is the ID of this container's lock. Each container, pod, and
+ // volume is assigned a unique Lock (from one of several backends) by
+ // the libpod Runtime. This lock will belong only to this container for
+ // the duration of the container's lifetime.
LockID uint32 `json:"lockID"`
- // CreateCommand is the full command plus arguments of the process the
- // container has been created with.
+ // CreateCommand is the full command plus arguments that were used to
+ // create the container. It is shown in the output of Inspect, and may
+ // be used to recreate an identical container for automatic updates or
+ // portable systemd unit files.
CreateCommand []string `json:"CreateCommand,omitempty"`
// RawImageName is the raw and unprocessed name of the image when creating
@@ -40,10 +67,13 @@ type ContainerConfig struct {
// name and not some normalized instance of it.
RawImageName string `json:"RawImageName,omitempty"`
- // UID/GID mappings used by the storage
+ // IDMappings are UID/GID mappings used by the container's user
+ // namespace. They are used by the OCI runtime when creating the
+ // container, and by c/storage to ensure that the container's files have
+ // the appropriate owner.
IDMappings storage.IDMappingOptions `json:"idMappingsOptions,omitempty"`
- // IDs of dependency containers.
+ // Dependencies are the IDs of dependency containers.
// These containers must be started before this container is started.
Dependencies []string
@@ -59,45 +89,92 @@ type ContainerConfig struct {
// ContainerRootFSConfig is an embedded sub-config providing config info
// about the container's root fs.
type ContainerRootFSConfig struct {
- RootfsImageID string `json:"rootfsImageID,omitempty"`
+ // RootfsImageID is the ID of the image used to create the container.
+ // If the container was created from a Rootfs, this will be empty.
+ // If non-empty, Podman will create a root filesystem for the container
+ // based on an image with this ID.
+ // This conflicts with Rootfs.
+ RootfsImageID string `json:"rootfsImageID,omitempty"`
+ // RootfsImageName is the (normalized) name of the image used to create
+ // the container. If the container was created from a Rootfs, this will
+ // be empty.
RootfsImageName string `json:"rootfsImageName,omitempty"`
- // Rootfs to use for the container, this conflicts with RootfsImageID
+ // Rootfs is a directory to use as the container's root filesystem.
+ // If RootfsImageID is set, this will be empty.
+ // If this is set, Podman will not create a root filesystem for the
+ // container based on an image, and will instead use the given directory
+ // as the container's root.
+ // Conflicts with RootfsImageID.
Rootfs string `json:"rootfs,omitempty"`
- // Src path to be mounted on /dev/shm in container.
+ // ShmDir is the path to be mounted on /dev/shm in container.
+ // If not set manually at creation time, Libpod will create a tmpfs
+ // with the size specified in ShmSize and populate this with the path of
+ // said tmpfs.
ShmDir string `json:"ShmDir,omitempty"`
- // Size of the container's SHM.
+ // ShmSize is the size of the container's SHM. Only used if ShmDir was
+ // not set manually at time of creation.
ShmSize int64 `json:"shmSize"`
// Static directory for container content that will persist across
// reboot.
+ // StaticDir is a persistent directory for Libpod files that will
+ // survive system reboot. It is not part of the container's rootfs and
+ // is not mounted into the container. It will be removed when the
+ // container is removed.
+ // Usually used to store container log files, files that will be bind
+ // mounted into the container (e.g. the resolv.conf we made for the
+ // container), and other per-container content.
StaticDir string `json:"staticDir"`
- // Mounts list contains all additional mounts into the container rootfs.
- // These include the SHM mount.
+ // Mounts contains all additional mounts into the container rootfs.
+ // It is presently only used for the container's SHM directory.
// These must be unmounted before the container's rootfs is unmounted.
Mounts []string `json:"mounts,omitempty"`
- // NamedVolumes lists the named volumes to mount into the container.
+ // NamedVolumes lists the Libpod named volumes to mount into the
+ // container. Each named volume is guaranteed to exist so long as this
+ // container exists.
NamedVolumes []*ContainerNamedVolume `json:"namedVolumes,omitempty"`
// OverlayVolumes lists the overlay volumes to mount into the container.
OverlayVolumes []*ContainerOverlayVolume `json:"overlayVolumes,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.
+ CreateWorkingDir bool `json:"createWorkingDir,omitempty"`
}
// ContainerSecurityConfig is an embedded sub-config providing security configuration
// to the container.
type ContainerSecurityConfig struct {
- // Whether the container is privileged
+ // Pirivileged is whether the container is privileged. Privileged
+ // containers have lessened security and increased access to the system.
+ // Note that this does NOT directly correspond to Podman's --privileged
+ // flag - most of the work of that flag is done in creating the OCI spec
+ // given to Libpod. This only enables a small subset of the overall
+ // operation, mostly around mounting the container image with reduced
+ // security.
Privileged bool `json:"privileged"`
- // SELinux process label for container
+ // ProcessLabel is the SELinux process label for the container.
ProcessLabel string `json:"ProcessLabel,omitempty"`
- // SELinux mount label for root filesystem
+ // MountLabel is the SELinux mount label for the container's root
+ // filesystem. Only used if the container was created from an image.
+ // If not explicitly set, an unused random MLS label will be assigned by
+ // containers/storage (but only if SELinux is enabled).
MountLabel string `json:"MountLabel,omitempty"`
- // LabelOpts are options passed in by the user to setup SELinux labels
+ // LabelOpts are options passed in by the user to setup SELinux labels.
+ // These are used by the containers/storage library.
LabelOpts []string `json:"labelopts,omitempty"`
- // User and group to use in the container
- // Can be specified by name or UID/GID
+ // User and group to use in the container. Can be specified as only user
+ // (in which case we will attempt to look up the user in the container
+ // to determine the appropriate group) or user and group separated by a
+ // colon.
+ // Can be specified by name or UID/GID.
+ // If unset, this will default to UID and GID 0 (root).
User string `json:"user,omitempty"`
- // Additional groups to add
+ // Groups are additional groups to add the container's user to. These
+ // are resolved within the container using the container's /etc/passwd.
Groups []string `json:"groups,omitempty"`
- // AddCurrentUserPasswdEntry indicates that the current user passwd entry
- // should be added to the /etc/passwd within the container
+ // AddCurrentUserPasswdEntry indicates that Libpod should ensure that
+ // the container's /etc/passwd contains an entry for the user running
+ // Libpod - mostly used in rootless containers where the user running
+ // Libpod wants to retain their UID inside the container.
AddCurrentUserPasswdEntry bool `json:"addCurrentUserPasswdEntry,omitempty"`
}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 4cfe992ea..9fb9738dc 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -159,7 +159,32 @@ func (c *Container) prepare() error {
}
// Save changes to container state
- return c.save()
+ if err := c.save(); err != nil {
+ return err
+ }
+
+ // Ensure container entrypoint is created (if required)
+ if c.config.CreateWorkingDir {
+ workdir, err := securejoin.SecureJoin(c.state.Mountpoint, c.WorkingDir())
+ if err != nil {
+ return errors.Wrapf(err, "error creating path to container %s working dir", c.ID())
+ }
+ rootUID := c.RootUID()
+ rootGID := c.RootGID()
+
+ if err := os.MkdirAll(workdir, 0755); err != nil {
+ if os.IsExist(err) {
+ return nil
+ }
+ return errors.Wrapf(err, "error creating container %s working dir", c.ID())
+ }
+
+ if err := os.Chown(workdir, rootUID, rootGID); err != nil {
+ return errors.Wrapf(err, "error chowning container %s working directory to container root", c.ID())
+ }
+ }
+
+ return nil
}
// cleanupNetwork unmounts and cleans up the container's network
diff --git a/libpod/define/errors.go b/libpod/define/errors.go
index 23d10f527..6e372eb5e 100644
--- a/libpod/define/errors.go
+++ b/libpod/define/errors.go
@@ -2,27 +2,27 @@ package define
import (
"errors"
-
- "github.com/containers/podman/v2/libpod/image"
- "github.com/containers/podman/v2/utils"
)
var (
// ErrNoSuchCtr indicates the requested container does not exist
- ErrNoSuchCtr = image.ErrNoSuchCtr
+ ErrNoSuchCtr = errors.New("no such container")
// ErrNoSuchPod indicates the requested pod does not exist
- ErrNoSuchPod = image.ErrNoSuchPod
+ ErrNoSuchPod = errors.New("no such pod")
// ErrNoSuchImage indicates the requested image does not exist
- ErrNoSuchImage = image.ErrNoSuchImage
+ ErrNoSuchImage = errors.New("no such image")
// ErrNoSuchTag indicates the requested image tag does not exist
- ErrNoSuchTag = image.ErrNoSuchTag
+ ErrNoSuchTag = errors.New("no such tag")
// ErrNoSuchVolume indicates the requested volume does not exist
ErrNoSuchVolume = errors.New("no such volume")
+ // ErrNoSuchNetwork indicates the requested network does not exist
+ ErrNoSuchNetwork = errors.New("network not found")
+
// ErrNoSuchExecSession indicates that the requested exec session does
// not exist.
ErrNoSuchExecSession = errors.New("no such exec session")
@@ -76,7 +76,7 @@ var (
// ErrDetach indicates that an attach session was manually detached by
// the user.
- ErrDetach = utils.ErrDetach
+ ErrDetach = errors.New("detached from container")
// ErrWillDeadlock indicates that the requested operation will cause a
// deadlock. This is usually caused by upgrade issues, and is resolved
diff --git a/libpod/events/journal_linux.go b/libpod/events/journal_linux.go
index 7c2a3e0f2..dc55dbc77 100644
--- a/libpod/events/journal_linux.go
+++ b/libpod/events/journal_linux.go
@@ -4,7 +4,6 @@ package events
import (
"context"
- "fmt"
"strconv"
"time"
@@ -50,7 +49,7 @@ func (e EventJournalD) Write(ee Event) error {
case Volume:
m["PODMAN_NAME"] = ee.Name
}
- return journal.Send(fmt.Sprintf("%s", ee.ToHumanReadable()), journal.PriInfo, m)
+ return journal.Send(string(ee.ToHumanReadable()), journal.PriInfo, m)
}
// Read reads events from the journal and sends qualified events to the event channel
diff --git a/libpod/image/errors.go b/libpod/image/errors.go
index ddbf7be4b..3f58b1c6a 100644
--- a/libpod/image/errors.go
+++ b/libpod/image/errors.go
@@ -1,17 +1,16 @@
package image
import (
- "errors"
+ "github.com/containers/podman/v2/libpod/define"
)
-// Copied directly from libpod errors to avoid circular imports
var (
// ErrNoSuchCtr indicates the requested container does not exist
- ErrNoSuchCtr = errors.New("no such container")
+ ErrNoSuchCtr = define.ErrNoSuchCtr
// ErrNoSuchPod indicates the requested pod does not exist
- ErrNoSuchPod = errors.New("no such pod")
+ ErrNoSuchPod = define.ErrNoSuchPod
// ErrNoSuchImage indicates the requested image does not exist
- ErrNoSuchImage = errors.New("no such image")
+ ErrNoSuchImage = define.ErrNoSuchImage
// ErrNoSuchTag indicates the requested image tag does not exist
- ErrNoSuchTag = errors.New("no such tag")
+ ErrNoSuchTag = define.ErrNoSuchTag
)
diff --git a/libpod/image/image.go b/libpod/image/image.go
index 8b2aa318f..4664da63e 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -14,6 +14,7 @@ import (
"syscall"
"time"
+ "github.com/containers/common/pkg/retry"
cp "github.com/containers/image/v5/copy"
"github.com/containers/image/v5/directory"
dockerarchive "github.com/containers/image/v5/docker/archive"
@@ -75,6 +76,8 @@ type InfoImage struct {
Layers []LayerInfo
}
+const maxRetry = 3
+
// ImageFilter is a function to determine whether a image is included
// in command output. Images to be outputted are tested using the function.
// A true return will include the image, a false return will exclude it.
@@ -158,7 +161,7 @@ func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile
if signaturePolicyPath == "" {
signaturePolicyPath = ir.SignaturePolicyPath
}
- imageName, err := ir.pullImageFromHeuristicSource(ctx, name, writer, authfile, signaturePolicyPath, signingoptions, dockeroptions, label)
+ imageName, err := ir.pullImageFromHeuristicSource(ctx, name, writer, authfile, signaturePolicyPath, signingoptions, dockeroptions, &retry.RetryOptions{MaxRetry: maxRetry}, label)
if err != nil {
return nil, errors.Wrapf(err, "unable to pull %s", name)
}
@@ -176,7 +179,7 @@ func (ir *Runtime) LoadFromArchiveReference(ctx context.Context, srcRef types.Im
if signaturePolicyPath == "" {
signaturePolicyPath = ir.SignaturePolicyPath
}
- imageNames, err := ir.pullImageFromReference(ctx, srcRef, writer, "", signaturePolicyPath, SigningOptions{}, &DockerRegistryOptions{})
+ imageNames, err := ir.pullImageFromReference(ctx, srcRef, writer, "", signaturePolicyPath, SigningOptions{}, &DockerRegistryOptions{}, &retry.RetryOptions{MaxRetry: maxRetry})
if err != nil {
return nil, errors.Wrapf(err, "unable to pull %s", transports.ImageName(srcRef))
}
diff --git a/libpod/image/pull.go b/libpod/image/pull.go
index d31f0dbdc..641698d03 100644
--- a/libpod/image/pull.go
+++ b/libpod/image/pull.go
@@ -7,6 +7,7 @@ import (
"path/filepath"
"strings"
+ "github.com/containers/common/pkg/retry"
cp "github.com/containers/image/v5/copy"
"github.com/containers/image/v5/directory"
"github.com/containers/image/v5/docker"
@@ -218,7 +219,7 @@ func toLocalImageName(imageName string) string {
// pullImageFromHeuristicSource pulls an image based on inputName, which is heuristically parsed and may involve configured registries.
// Use pullImageFromReference if the source is known precisely.
-func (ir *Runtime) pullImageFromHeuristicSource(ctx context.Context, inputName string, writer io.Writer, authfile, signaturePolicyPath string, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions, label *string) ([]string, error) {
+func (ir *Runtime) pullImageFromHeuristicSource(ctx context.Context, inputName string, writer io.Writer, authfile, signaturePolicyPath string, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions, retryOptions *retry.RetryOptions, label *string) ([]string, error) {
span, _ := opentracing.StartSpanFromContext(ctx, "pullImageFromHeuristicSource")
defer span.Finish()
@@ -247,11 +248,11 @@ func (ir *Runtime) pullImageFromHeuristicSource(ctx context.Context, inputName s
return nil, errors.Wrapf(err, "error determining pull goal for image %q", inputName)
}
}
- return ir.doPullImage(ctx, sc, *goal, writer, signingOptions, dockerOptions, label)
+ return ir.doPullImage(ctx, sc, *goal, writer, signingOptions, dockerOptions, retryOptions, label)
}
// pullImageFromReference pulls an image from a types.imageReference.
-func (ir *Runtime) pullImageFromReference(ctx context.Context, srcRef types.ImageReference, writer io.Writer, authfile, signaturePolicyPath string, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions) ([]string, error) {
+func (ir *Runtime) pullImageFromReference(ctx context.Context, srcRef types.ImageReference, writer io.Writer, authfile, signaturePolicyPath string, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions, retryOptions *retry.RetryOptions) ([]string, error) {
span, _ := opentracing.StartSpanFromContext(ctx, "pullImageFromReference")
defer span.Finish()
@@ -264,7 +265,7 @@ func (ir *Runtime) pullImageFromReference(ctx context.Context, srcRef types.Imag
if err != nil {
return nil, errors.Wrapf(err, "error determining pull goal for image %q", transports.ImageName(srcRef))
}
- return ir.doPullImage(ctx, sc, *goal, writer, signingOptions, dockerOptions, nil)
+ return ir.doPullImage(ctx, sc, *goal, writer, signingOptions, dockerOptions, retryOptions, nil)
}
func cleanErrorMessage(err error) string {
@@ -274,7 +275,7 @@ func cleanErrorMessage(err error) string {
}
// doPullImage is an internal helper interpreting pullGoal. Almost everyone should call one of the callers of doPullImage instead.
-func (ir *Runtime) doPullImage(ctx context.Context, sc *types.SystemContext, goal pullGoal, writer io.Writer, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions, label *string) ([]string, error) {
+func (ir *Runtime) doPullImage(ctx context.Context, sc *types.SystemContext, goal pullGoal, writer io.Writer, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions, retryOptions *retry.RetryOptions, label *string) ([]string, error) {
span, _ := opentracing.StartSpanFromContext(ctx, "doPullImage")
defer span.Finish()
@@ -310,9 +311,11 @@ func (ir *Runtime) doPullImage(ctx context.Context, sc *types.SystemContext, goa
return nil, err
}
}
-
- _, err = cp.Image(ctx, policyContext, imageInfo.dstRef, imageInfo.srcRef, copyOptions)
- if err != nil {
+ imageInfo := imageInfo
+ if err = retry.RetryIfNecessary(ctx, func() error {
+ _, err = cp.Image(ctx, policyContext, imageInfo.dstRef, imageInfo.srcRef, copyOptions)
+ return err
+ }, retryOptions); err != nil {
pullErrors = multierror.Append(pullErrors, err)
logrus.Debugf("Error pulling image ref %s: %v", imageInfo.srcRef.StringWithinTransport(), err)
if writer != nil {
diff --git a/libpod/options.go b/libpod/options.go
index b98ef2221..16b05d9b6 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1451,6 +1451,19 @@ func WithCreateCommand(cmd []string) CtrCreateOption {
}
}
+// WithCreateWorkingDir tells Podman to create the container's working directory
+// if it does not exist.
+func WithCreateWorkingDir() CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return define.ErrCtrFinalized
+ }
+
+ ctr.config.CreateWorkingDir = true
+ return nil
+ }
+}
+
// Volume Creation Options
// WithVolumeName sets the name of the volume.