aboutsummaryrefslogtreecommitdiff
path: root/pkg/specgen
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/specgen')
-rw-r--r--pkg/specgen/generate/container.go14
-rw-r--r--pkg/specgen/generate/container_create.go12
-rw-r--r--pkg/specgen/generate/kube/kube.go40
-rw-r--r--pkg/specgen/generate/kube/volume.go124
-rw-r--r--pkg/specgen/generate/storage.go61
-rw-r--r--pkg/specgen/namespaces.go16
-rw-r--r--pkg/specgen/specgen.go38
-rw-r--r--pkg/specgen/volumes.go149
8 files changed, 381 insertions, 73 deletions
diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go
index 2ee8f2441..c7e62d185 100644
--- a/pkg/specgen/generate/container.go
+++ b/pkg/specgen/generate/container.go
@@ -257,7 +257,19 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
}
}
- return verifyContainerResources(s)
+ warnings, err := verifyContainerResources(s)
+ if err != nil {
+ return warnings, err
+ }
+
+ // Warn on net=host/container/pod/none and port mappings.
+ if (s.NetNS.NSMode == specgen.Host || s.NetNS.NSMode == specgen.FromContainer ||
+ s.NetNS.NSMode == specgen.FromPod || s.NetNS.NSMode == specgen.NoNetwork) &&
+ len(s.PortMappings) > 0 {
+ warnings = append(warnings, "Port mappings have been discarded as one of the Host, Container, Pod, and None network modes are in use")
+ }
+
+ return warnings, nil
}
// finishThrottleDevices takes the temporary representation of the throttle
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index c049e64cf..45a374216 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -111,7 +111,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
return nil, errors.Wrap(err, "invalid config provided")
}
- finalMounts, finalVolumes, err := finalizeMounts(ctx, s, rt, rtc, newImage)
+ finalMounts, finalVolumes, finalOverlays, err := finalizeMounts(ctx, s, rt, rtc, newImage)
if err != nil {
return nil, err
}
@@ -121,7 +121,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
return nil, err
}
- opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, newImage, command)
+ opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, finalOverlays, newImage, command)
if err != nil {
return nil, err
}
@@ -144,7 +144,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
return rt.NewContainer(ctx, runtimeSpec, options...)
}
-func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator, pod *libpod.Pod, volumes []*specgen.NamedVolume, img *image.Image, command []string) ([]libpod.CtrCreateOption, error) {
+func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator, pod *libpod.Pod, volumes []*specgen.NamedVolume, overlays []*specgen.OverlayVolume, img *image.Image, command []string) ([]libpod.CtrCreateOption, error) {
var options []libpod.CtrCreateOption
var err error
@@ -224,7 +224,7 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
for _, volume := range volumes {
destinations = append(destinations, volume.Dest)
}
- for _, overlayVolume := range s.OverlayVolumes {
+ for _, overlayVolume := range overlays {
destinations = append(destinations, overlayVolume.Destination)
}
for _, imageVolume := range s.ImageVolumes {
@@ -244,9 +244,9 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
options = append(options, libpod.WithNamedVolumes(vols))
}
- if len(s.OverlayVolumes) != 0 {
+ if len(overlays) != 0 {
var vols []*libpod.ContainerOverlayVolume
- for _, v := range s.OverlayVolumes {
+ for _, v := range overlays {
vols = append(vols, &libpod.ContainerOverlayVolume{
Dest: v.Destination,
Source: v.Source,
diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go
index e1202956c..5f72d28bb 100644
--- a/pkg/specgen/generate/kube/kube.go
+++ b/pkg/specgen/generate/kube/kube.go
@@ -47,7 +47,7 @@ func ToPodGen(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec)
return p, nil
}
-func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newImage *image.Image, volumes map[string]string, podID, podName, infraID string, configMaps []v1.ConfigMap, seccompPaths *KubeSeccompPaths, restartPolicy string) (*specgen.SpecGenerator, error) {
+func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newImage *image.Image, volumes map[string]*KubeVolume, podID, podName, infraID string, configMaps []v1.ConfigMap, seccompPaths *KubeSeccompPaths, restartPolicy string) (*specgen.SpecGenerator, error) {
s := specgen.NewSpecGenerator(iid, false)
// podName should be non-empty for Deployment objects to be able to create
@@ -163,22 +163,36 @@ func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newI
s.Env = envs
for _, volume := range containerYAML.VolumeMounts {
- hostPath, exists := volumes[volume.Name]
+ volumeSource, exists := volumes[volume.Name]
if !exists {
return nil, errors.Errorf("Volume mount %s specified for container but not configured in volumes", volume.Name)
}
- if err := parse.ValidateVolumeCtrDir(volume.MountPath); err != nil {
- return nil, errors.Wrapf(err, "error in parsing MountPath")
- }
- mount := spec.Mount{
- Destination: volume.MountPath,
- Source: hostPath,
- Type: "bind",
- }
- if volume.ReadOnly {
- mount.Options = []string{"ro"}
+ switch volumeSource.Type {
+ case KubeVolumeTypeBindMount:
+ if err := parse.ValidateVolumeCtrDir(volume.MountPath); err != nil {
+ return nil, errors.Wrapf(err, "error in parsing MountPath")
+ }
+ mount := spec.Mount{
+ Destination: volume.MountPath,
+ Source: volumeSource.Source,
+ Type: "bind",
+ }
+ if volume.ReadOnly {
+ mount.Options = []string{"ro"}
+ }
+ s.Mounts = append(s.Mounts, mount)
+ case KubeVolumeTypeNamed:
+ namedVolume := specgen.NamedVolume{
+ Dest: volume.MountPath,
+ Name: volumeSource.Source,
+ }
+ if volume.ReadOnly {
+ namedVolume.Options = []string{"ro"}
+ }
+ s.Volumes = append(s.Volumes, &namedVolume)
+ default:
+ return nil, errors.Errorf("Unsupported volume source type")
}
- s.Mounts = append(s.Mounts, mount)
}
s.RestartPolicy = restartPolicy
diff --git a/pkg/specgen/generate/kube/volume.go b/pkg/specgen/generate/kube/volume.go
new file mode 100644
index 000000000..2ef0f4c23
--- /dev/null
+++ b/pkg/specgen/generate/kube/volume.go
@@ -0,0 +1,124 @@
+package kube
+
+import (
+ "os"
+
+ "github.com/containers/buildah/pkg/parse"
+ "github.com/containers/podman/v2/libpod"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ v1 "k8s.io/api/core/v1"
+)
+
+const (
+ // https://kubernetes.io/docs/concepts/storage/volumes/#hostpath
+ kubeDirectoryPermission = 0755
+ // https://kubernetes.io/docs/concepts/storage/volumes/#hostpath
+ kubeFilePermission = 0644
+)
+
+type KubeVolumeType int
+
+const (
+ KubeVolumeTypeBindMount KubeVolumeType = iota
+ KubeVolumeTypeNamed KubeVolumeType = iota
+)
+
+type KubeVolume struct {
+ // Type of volume to create
+ Type KubeVolumeType
+ // Path for bind mount or volume name for named volume
+ Source string
+}
+
+// Create a KubeVolume from an HostPathVolumeSource
+func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) {
+ if hostPath.Type != nil {
+ switch *hostPath.Type {
+ case v1.HostPathDirectoryOrCreate:
+ if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) {
+ if err := os.Mkdir(hostPath.Path, kubeDirectoryPermission); err != nil {
+ return nil, err
+ }
+ }
+ // Label a newly created volume
+ if err := libpod.LabelVolumePath(hostPath.Path); err != nil {
+ return nil, errors.Wrapf(err, "error giving %s a label", hostPath.Path)
+ }
+ case v1.HostPathFileOrCreate:
+ if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) {
+ f, err := os.OpenFile(hostPath.Path, os.O_RDONLY|os.O_CREATE, kubeFilePermission)
+ if err != nil {
+ return nil, errors.Wrap(err, "error creating HostPath")
+ }
+ if err := f.Close(); err != nil {
+ logrus.Warnf("Error in closing newly created HostPath file: %v", err)
+ }
+ }
+ // unconditionally label a newly created volume
+ if err := libpod.LabelVolumePath(hostPath.Path); err != nil {
+ return nil, errors.Wrapf(err, "error giving %s a label", hostPath.Path)
+ }
+ case v1.HostPathSocket:
+ st, err := os.Stat(hostPath.Path)
+ if err != nil {
+ return nil, errors.Wrap(err, "error checking HostPathSocket")
+ }
+ if st.Mode()&os.ModeSocket != os.ModeSocket {
+ return nil, errors.Errorf("error checking HostPathSocket: path %s is not a socket", hostPath.Path)
+ }
+
+ case v1.HostPathDirectory:
+ case v1.HostPathFile:
+ case v1.HostPathUnset:
+ // do nothing here because we will verify the path exists in validateVolumeHostDir
+ break
+ default:
+ return nil, errors.Errorf("Invalid HostPath type %v", hostPath.Type)
+ }
+ }
+
+ if err := parse.ValidateVolumeHostDir(hostPath.Path); err != nil {
+ return nil, errors.Wrapf(err, "error in parsing HostPath in YAML")
+ }
+
+ return &KubeVolume{
+ Type: KubeVolumeTypeBindMount,
+ Source: hostPath.Path,
+ }, nil
+}
+
+// Create a KubeVolume from a PersistentVolumeClaimVolumeSource
+func VolumeFromPersistentVolumeClaim(claim *v1.PersistentVolumeClaimVolumeSource) (*KubeVolume, error) {
+ return &KubeVolume{
+ Type: KubeVolumeTypeNamed,
+ Source: claim.ClaimName,
+ }, nil
+}
+
+// Create a KubeVolume from one of the supported VolumeSource
+func VolumeFromSource(volumeSource v1.VolumeSource) (*KubeVolume, error) {
+ if volumeSource.HostPath != nil {
+ return VolumeFromHostPath(volumeSource.HostPath)
+ } else if volumeSource.PersistentVolumeClaim != nil {
+ return VolumeFromPersistentVolumeClaim(volumeSource.PersistentVolumeClaim)
+ } else {
+ return nil, errors.Errorf("HostPath and PersistentVolumeClaim are currently the conly supported VolumeSource")
+ }
+}
+
+// Create a map of volume name to KubeVolume
+func InitializeVolumes(specVolumes []v1.Volume) (map[string]*KubeVolume, error) {
+ volumes := make(map[string]*KubeVolume)
+
+ for _, specVolume := range specVolumes {
+ volume, err := VolumeFromSource(specVolume.VolumeSource)
+ if err != nil {
+ return nil, err
+ }
+
+ volumes[specVolume.Name] = volume
+ }
+
+ return volumes, nil
+}
diff --git a/pkg/specgen/generate/storage.go b/pkg/specgen/generate/storage.go
index b225f79ee..331a5c5bf 100644
--- a/pkg/specgen/generate/storage.go
+++ b/pkg/specgen/generate/storage.go
@@ -33,17 +33,17 @@ var (
)
// Produce final mounts and named volumes for a container
-func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, img *image.Image) ([]spec.Mount, []*specgen.NamedVolume, error) {
+func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, img *image.Image) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, error) {
// Get image volumes
baseMounts, baseVolumes, err := getImageVolumes(ctx, img, s)
if err != nil {
- return nil, nil, err
+ return nil, nil, nil, err
}
// Get volumes-from mounts
volFromMounts, volFromVolumes, err := getVolumesFrom(s.VolumesFrom, rt)
if err != nil {
- return nil, nil, err
+ return nil, nil, nil, err
}
// Supersede from --volumes-from.
@@ -57,19 +57,53 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru
// Need to make map forms of specgen mounts/volumes.
unifiedMounts := map[string]spec.Mount{}
unifiedVolumes := map[string]*specgen.NamedVolume{}
+ unifiedOverlays := map[string]*specgen.OverlayVolume{}
+
+ // Need to make map forms of specgen mounts/volumes.
+ commonMounts, commonVolumes, commonOverlayVolumes, err := specgen.GenVolumeMounts(rtc.Volumes())
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
for _, m := range s.Mounts {
if _, ok := unifiedMounts[m.Destination]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified mounts - multiple mounts at %q", m.Destination)
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified mounts - multiple mounts at %q", m.Destination)
}
unifiedMounts[m.Destination] = m
}
+
+ for _, m := range commonMounts {
+ if _, ok := unifiedMounts[m.Destination]; !ok {
+ unifiedMounts[m.Destination] = m
+ }
+ }
+
for _, v := range s.Volumes {
if _, ok := unifiedVolumes[v.Dest]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", v.Dest)
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", v.Dest)
}
unifiedVolumes[v.Dest] = v
}
+ for _, v := range commonVolumes {
+ if _, ok := unifiedVolumes[v.Dest]; !ok {
+ unifiedVolumes[v.Dest] = v
+ }
+ }
+
+ for _, v := range s.OverlayVolumes {
+ if _, ok := unifiedOverlays[v.Destination]; ok {
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", v.Destination)
+ }
+ unifiedOverlays[v.Destination] = v
+ }
+
+ for _, v := range commonOverlayVolumes {
+ if _, ok := unifiedOverlays[v.Destination]; ok {
+ unifiedOverlays[v.Destination] = v
+ }
+ }
+
// If requested, add container init binary
if s.Init {
initPath := s.InitPath
@@ -78,10 +112,10 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru
}
initMount, err := addContainerInitBinary(s, initPath)
if err != nil {
- return nil, nil, err
+ return nil, nil, nil, err
}
if _, ok := unifiedMounts[initMount.Destination]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, "conflict with mount added by --init to %q", initMount.Destination)
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict with mount added by --init to %q", initMount.Destination)
}
unifiedMounts[initMount.Destination] = initMount
}
@@ -115,12 +149,12 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru
// Check for conflicts between named volumes and mounts
for dest := range baseMounts {
if _, ok := baseVolumes[dest]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
}
}
for dest := range baseVolumes {
if _, ok := baseMounts[dest]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
}
}
// Final step: maps to arrays
@@ -129,7 +163,7 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru
if mount.Type == TypeBind {
absSrc, err := filepath.Abs(mount.Source)
if err != nil {
- return nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source)
+ return nil, nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source)
}
mount.Source = absSrc
}
@@ -140,7 +174,12 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru
finalVolumes = append(finalVolumes, volume)
}
- return finalMounts, finalVolumes, nil
+ finalOverlays := make([]*specgen.OverlayVolume, 0, len(unifiedOverlays))
+ for _, volume := range unifiedOverlays {
+ finalOverlays = append(finalOverlays, volume)
+ }
+
+ return finalMounts, finalVolumes, finalOverlays, nil
}
// Get image volumes from the given image
diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go
index 90c56d366..d15745fa0 100644
--- a/pkg/specgen/namespaces.go
+++ b/pkg/specgen/namespaces.go
@@ -27,19 +27,25 @@ const (
// Private indicates the namespace is private
Private NamespaceMode = "private"
// NoNetwork indicates no network namespace should
- // be joined. loopback should still exists
+ // be joined. loopback should still exists.
+ // Only used with the network namespace, invalid otherwise.
NoNetwork NamespaceMode = "none"
// Bridge indicates that a CNI network stack
- // should be used
+ // should be used.
+ // Only used with the network namespace, invalid otherwise.
Bridge NamespaceMode = "bridge"
// Slirp indicates that a slirp4netns network stack should
- // be used
+ // be used.
+ // Only used with the network namespace, invalid otherwise.
Slirp NamespaceMode = "slirp4netns"
// KeepId indicates a user namespace to keep the owner uid inside
- // of the namespace itself
+ // of the namespace itself.
+ // Only used with the user namespace, invalid otherwise.
KeepID NamespaceMode = "keep-id"
- // KeepId indicates to automatically create a user namespace
+ // Auto indicates to automatically create a user namespace.
+ // Only used with the user namespace, invalid otherwise.
Auto NamespaceMode = "auto"
+
// DefaultKernelNamespaces is a comma-separated list of default kernel
// namespaces.
DefaultKernelNamespaces = "cgroup,ipc,net,uts"
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index 0a9a16ea7..fad2406e5 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -1,13 +1,13 @@
package specgen
import (
- "errors"
"net"
"syscall"
"github.com/containers/image/v5/manifest"
"github.com/containers/storage"
spec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
)
// LogConfig describes the logging characteristics for a container
@@ -459,42 +459,6 @@ type SpecGenerator struct {
ContainerHealthCheckConfig
}
-// NamedVolume holds information about a named volume that will be mounted into
-// the container.
-type NamedVolume struct {
- // Name is the name of the named volume to be mounted. May be empty.
- // If empty, a new named volume with a pseudorandomly generated name
- // will be mounted at the given destination.
- Name string
- // Destination to mount the named volume within the container. Must be
- // an absolute path. Path will be created if it does not exist.
- Dest string
- // Options are options that the named volume will be mounted with.
- Options []string
-}
-
-// OverlayVolume holds information about a overlay volume that will be mounted into
-// the container.
-type OverlayVolume struct {
- // Destination is the absolute path where the mount will be placed in the container.
- Destination string `json:"destination"`
- // Source specifies the source path of the mount.
- Source string `json:"source,omitempty"`
-}
-
-// ImageVolume 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. An
-// ImageVolume is always mounted read only.
-type ImageVolume struct {
- // Source is the source of the image volume. The image can be referred
- // to by name and by ID.
- Source string
- // Destination is the absolute path of the mount in the container.
- Destination string
- // ReadWrite sets the volume writable.
- ReadWrite bool
-}
-
// PortMapping is one or more ports that will be mapped into the container.
type PortMapping struct {
// HostIP is the IP that we will bind to on the host.
diff --git a/pkg/specgen/volumes.go b/pkg/specgen/volumes.go
new file mode 100644
index 000000000..a4f42d715
--- /dev/null
+++ b/pkg/specgen/volumes.go
@@ -0,0 +1,149 @@
+package specgen
+
+import (
+ "path/filepath"
+ "strings"
+
+ "github.com/containers/buildah/pkg/parse"
+ spec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+// NamedVolume holds information about a named volume that will be mounted into
+// the container.
+type NamedVolume struct {
+ // Name is the name of the named volume to be mounted. May be empty.
+ // If empty, a new named volume with a pseudorandomly generated name
+ // will be mounted at the given destination.
+ Name string
+ // Destination to mount the named volume within the container. Must be
+ // an absolute path. Path will be created if it does not exist.
+ Dest string
+ // Options are options that the named volume will be mounted with.
+ Options []string
+}
+
+// OverlayVolume holds information about a overlay volume that will be mounted into
+// the container.
+type OverlayVolume struct {
+ // Destination is the absolute path where the mount will be placed in the container.
+ Destination string `json:"destination"`
+ // Source specifies the source path of the mount.
+ Source string `json:"source,omitempty"`
+}
+
+// ImageVolume 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. An
+// ImageVolume is always mounted read only.
+type ImageVolume struct {
+ // Source is the source of the image volume. The image can be referred
+ // to by name and by ID.
+ Source string
+ // Destination is the absolute path of the mount in the container.
+ Destination string
+ // ReadWrite sets the volume writable.
+ ReadWrite bool
+}
+
+// GenVolumeMounts parses user input into mounts, volumes and overlay volumes
+func GenVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*NamedVolume, map[string]*OverlayVolume, error) {
+ errDuplicateDest := errors.Errorf("duplicate mount destination")
+
+ mounts := make(map[string]spec.Mount)
+ volumes := make(map[string]*NamedVolume)
+ overlayVolumes := make(map[string]*OverlayVolume)
+
+ volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]")
+
+ for _, vol := range volumeFlag {
+ var (
+ options []string
+ src string
+ dest string
+ err error
+ )
+
+ splitVol := strings.Split(vol, ":")
+ if len(splitVol) > 3 {
+ return nil, nil, nil, errors.Wrapf(volumeFormatErr, vol)
+ }
+
+ src = splitVol[0]
+ if len(splitVol) == 1 {
+ // This is an anonymous named volume. Only thing given
+ // is destination.
+ // Name/source will be blank, and populated by libpod.
+ src = ""
+ dest = splitVol[0]
+ } else if len(splitVol) > 1 {
+ dest = splitVol[1]
+ }
+ if len(splitVol) > 2 {
+ if options, err = parse.ValidateVolumeOpts(strings.Split(splitVol[2], ",")); err != nil {
+ return nil, nil, nil, err
+ }
+ }
+
+ // Do not check source dir for anonymous volumes
+ if len(splitVol) > 1 {
+ if len(src) == 0 {
+ return nil, nil, nil, errors.New("host directory cannot be empty")
+ }
+ }
+ if err := parse.ValidateVolumeCtrDir(dest); err != nil {
+ return nil, nil, nil, err
+ }
+
+ cleanDest := filepath.Clean(dest)
+
+ if strings.HasPrefix(src, "/") || strings.HasPrefix(src, ".") {
+ // This is not a named volume
+ overlayFlag := false
+ for _, o := range options {
+ if o == "O" {
+ overlayFlag = true
+ if len(options) > 1 {
+ return nil, nil, nil, errors.New("can't use 'O' with other options")
+ }
+ }
+ }
+ if overlayFlag {
+ // This is a overlay volume
+ newOverlayVol := new(OverlayVolume)
+ newOverlayVol.Destination = cleanDest
+ newOverlayVol.Source = src
+ if _, ok := overlayVolumes[newOverlayVol.Destination]; ok {
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, newOverlayVol.Destination)
+ }
+ overlayVolumes[newOverlayVol.Destination] = newOverlayVol
+ } else {
+ newMount := spec.Mount{
+ Destination: cleanDest,
+ Type: "bind",
+ Source: src,
+ Options: options,
+ }
+ if _, ok := mounts[newMount.Destination]; ok {
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, newMount.Destination)
+ }
+ mounts[newMount.Destination] = newMount
+ }
+ } else {
+ // This is a named volume
+ newNamedVol := new(NamedVolume)
+ newNamedVol.Name = src
+ newNamedVol.Dest = cleanDest
+ newNamedVol.Options = options
+
+ if _, ok := volumes[newNamedVol.Dest]; ok {
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, newNamedVol.Dest)
+ }
+ volumes[newNamedVol.Dest] = newNamedVol
+ }
+
+ logrus.Debugf("User mount %s:%s options %v", src, dest, options)
+ }
+
+ return mounts, volumes, overlayVolumes, nil
+}