diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/driver/driver.go | 4 | ||||
-rw-r--r-- | libpod/events/journal_linux.go | 17 | ||||
-rw-r--r-- | libpod/image/image.go | 6 | ||||
-rw-r--r-- | libpod/image/image_test.go | 11 | ||||
-rw-r--r-- | libpod/image/pull.go | 25 | ||||
-rw-r--r-- | libpod/oci.go | 2 | ||||
-rw-r--r-- | libpod/oci_internal_linux.go | 26 | ||||
-rw-r--r-- | libpod/options.go | 71 | ||||
-rw-r--r-- | libpod/pod.go | 2 | ||||
-rw-r--r-- | libpod/runtime.go | 4 | ||||
-rw-r--r-- | libpod/runtime_pod_infra_linux.go | 6 | ||||
-rw-r--r-- | libpod/runtime_pod_linux.go | 4 | ||||
-rw-r--r-- | libpod/runtime_volume_linux.go | 7 | ||||
-rw-r--r-- | libpod/util_linux.go | 11 | ||||
-rw-r--r-- | libpod/volume.go | 46 | ||||
-rw-r--r-- | libpod/volume_inspect.go | 70 |
16 files changed, 253 insertions, 59 deletions
diff --git a/libpod/driver/driver.go b/libpod/driver/driver.go index f9442fa21..85eda5a21 100644 --- a/libpod/driver/driver.go +++ b/libpod/driver/driver.go @@ -38,6 +38,10 @@ func GetDriverData(store cstorage.Store, layerID string) (*Data, error) { if err != nil { return nil, err } + if mountTimes, err := store.Mounted(layerID); mountTimes == 0 || err != nil { + delete(metaData, "MergedDir") + } + return &Data{ Name: name, Data: metaData, diff --git a/libpod/events/journal_linux.go b/libpod/events/journal_linux.go index 7d195dc79..470c76959 100644 --- a/libpod/events/journal_linux.go +++ b/libpod/events/journal_linux.go @@ -4,6 +4,7 @@ package events import ( "fmt" + "strconv" "time" "github.com/coreos/go-systemd/journal" @@ -42,6 +43,9 @@ func (e EventJournalD) Write(ee Event) error { m["PODMAN_IMAGE"] = ee.Image m["PODMAN_NAME"] = ee.Name m["PODMAN_ID"] = ee.ID + if ee.ContainerExitCode != 0 { + m["PODMAN_EXIT_CODE"] = strconv.Itoa(ee.ContainerExitCode) + } case Volume: m["PODMAN_NAME"] = ee.Name } @@ -69,6 +73,11 @@ func (e EventJournalD) Read(options ReadOptions) error { 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") + } } // the api requires a next|prev before getting a cursor if _, err := j.Next(); err != nil { @@ -150,6 +159,14 @@ func newEventFromJournalEntry(entry *sdjournal.JournalEntry) (*Event, error) { / case Container, Pod: newEvent.ID = entry.Fields["PODMAN_ID"] newEvent.Image = entry.Fields["PODMAN_IMAGE"] + if code, ok := entry.Fields["PODMAN_EXIT_CODE"]; ok { + intCode, err := strconv.Atoi(code) + if err != nil { + logrus.Errorf("Error parsing event exit code %s", code) + } else { + newEvent.ContainerExitCode = intCode + } + } case Image: newEvent.ID = entry.Fields["PODMAN_ID"] } diff --git a/libpod/image/image.go b/libpod/image/image.go index 068491f28..cb7c390c6 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -135,7 +135,7 @@ func (ir *Runtime) NewFromLocal(name string) (*Image, error) { // New creates a new image object where the image could be local // or remote -func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *DockerRegistryOptions, signingoptions SigningOptions, forcePull bool, label *string) (*Image, error) { +func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *DockerRegistryOptions, signingoptions SigningOptions, label *string, pullType util.PullType) (*Image, error) { span, _ := opentracing.StartSpanFromContext(ctx, "newImage") span.SetTag("type", "runtime") defer span.Finish() @@ -145,11 +145,13 @@ func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile InputName: name, imageruntime: ir, } - if !forcePull { + if pullType != util.PullImageAlways { localImage, err := newImage.getLocalImage() if err == nil { newImage.image = localImage return &newImage, nil + } else if pullType == util.PullImageNever { + return nil, err } } diff --git a/libpod/image/image_test.go b/libpod/image/image_test.go index e93ebf797..5a6d095f6 100644 --- a/libpod/image/image_test.go +++ b/libpod/image/image_test.go @@ -3,12 +3,13 @@ package image import ( "context" "fmt" - "github.com/containers/libpod/libpod/events" "io" "io/ioutil" "os" "testing" + "github.com/containers/libpod/libpod/events" + "github.com/containers/libpod/pkg/util" "github.com/containers/storage" "github.com/opencontainers/go-digest" "github.com/stretchr/testify/assert" @@ -89,9 +90,9 @@ func TestImage_NewFromLocal(t *testing.T) { ir, err := NewImageRuntimeFromOptions(so) assert.NoError(t, err) ir.Eventer = events.NewNullEventer() - bb, err := ir.New(context.Background(), "docker.io/library/busybox:latest", "", "", writer, nil, SigningOptions{}, false, nil) + bb, err := ir.New(context.Background(), "docker.io/library/busybox:latest", "", "", writer, nil, SigningOptions{}, nil, util.PullImageMissing) assert.NoError(t, err) - bbglibc, err := ir.New(context.Background(), "docker.io/library/busybox:glibc", "", "", writer, nil, SigningOptions{}, false, nil) + bbglibc, err := ir.New(context.Background(), "docker.io/library/busybox:glibc", "", "", writer, nil, SigningOptions{}, nil, util.PullImageMissing) assert.NoError(t, err) tm, err := makeLocalMatrix(bb, bbglibc) @@ -139,7 +140,7 @@ func TestImage_New(t *testing.T) { // Iterate over the names and delete the image // after the pull for _, img := range names { - newImage, err := ir.New(context.Background(), img, "", "", writer, nil, SigningOptions{}, false, nil) + newImage, err := ir.New(context.Background(), img, "", "", writer, nil, SigningOptions{}, nil, util.PullImageMissing) assert.NoError(t, err) assert.NotEqual(t, newImage.ID(), "") err = newImage.Remove(context.Background(), false) @@ -168,7 +169,7 @@ func TestImage_MatchRepoTag(t *testing.T) { ir, err := NewImageRuntimeFromOptions(so) assert.NoError(t, err) ir.Eventer = events.NewNullEventer() - newImage, err := ir.New(context.Background(), "busybox", "", "", os.Stdout, nil, SigningOptions{}, false, nil) + newImage, err := ir.New(context.Background(), "busybox", "", "", os.Stdout, nil, SigningOptions{}, nil, util.PullImageMissing) assert.NoError(t, err) err = newImage.TagImage("foo:latest") assert.NoError(t, err) diff --git a/libpod/image/pull.go b/libpod/image/pull.go index 78cfe3626..dbf3a4ef5 100644 --- a/libpod/image/pull.go +++ b/libpod/image/pull.go @@ -13,6 +13,7 @@ import ( dockerarchive "github.com/containers/image/docker/archive" "github.com/containers/image/docker/tarfile" ociarchive "github.com/containers/image/oci/archive" + oci "github.com/containers/image/oci/layout" is "github.com/containers/image/storage" "github.com/containers/image/transports" "github.com/containers/image/transports/alltransports" @@ -37,6 +38,9 @@ var ( DirTransport = directory.Transport.Name() // DockerTransport is the transport for docker registries DockerTransport = docker.Transport.Name() + // OCIDirTransport is the transport for pushing and pulling + // images to and from a directory containing an OCI image + OCIDirTransport = oci.Transport.Name() // AtomicTransport is the transport for atomic registries AtomicTransport = "atomic" // DefaultTransport is a prefix that we apply to an image name @@ -189,12 +193,12 @@ func (ir *Runtime) pullGoalFromImageReference(ctx context.Context, srcRef types. return ir.getSinglePullRefPairGoal(srcRef, dest) case DirTransport: - path := srcRef.StringWithinTransport() - image := path - if image[:1] == "/" { - // Set localhost as the registry so docker.io isn't prepended, and the path becomes the repository - image = DefaultLocalRegistry + image - } + image := toLocalImageName(srcRef.StringWithinTransport()) + return ir.getSinglePullRefPairGoal(srcRef, image) + + case OCIDirTransport: + split := strings.SplitN(srcRef.StringWithinTransport(), ":", 2) + image := toLocalImageName(split[0]) return ir.getSinglePullRefPairGoal(srcRef, image) default: @@ -202,6 +206,15 @@ func (ir *Runtime) pullGoalFromImageReference(ctx context.Context, srcRef types. } } +// toLocalImageName converts an image name into a 'localhost/' prefixed one +func toLocalImageName(imageName string) string { + return fmt.Sprintf( + "%s/%s", + DefaultLocalRegistry, + strings.TrimLeft(imageName, "/"), + ) +} + // 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) { diff --git a/libpod/oci.go b/libpod/oci.go index 3a0c85987..8a873ca5b 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -60,6 +60,7 @@ type OCIRuntime struct { noPivot bool reservePorts bool supportsJSON bool + sdNotify bool } // ociError is used to parse the OCI runtime JSON log. It is not part of the @@ -87,6 +88,7 @@ func newOCIRuntime(name string, paths []string, conmonPath string, runtimeCfg *R runtime.logSizeMax = runtimeCfg.MaxLogSize runtime.noPivot = runtimeCfg.NoPivotRoot runtime.reservePorts = runtimeCfg.EnablePortReservation + runtime.sdNotify = runtimeCfg.SDNotify // TODO: probe OCI runtime for feature and enable automatically if // available. diff --git a/libpod/oci_internal_linux.go b/libpod/oci_internal_linux.go index d1155cf3c..48b7370e0 100644 --- a/libpod/oci_internal_linux.go +++ b/libpod/oci_internal_linux.go @@ -247,10 +247,14 @@ func (r *OCIRuntime) configureConmonEnv(runtimeDir string) ([]string, []*os.File if notify, ok := os.LookupEnv("NOTIFY_SOCKET"); ok { env = append(env, fmt.Sprintf("NOTIFY_SOCKET=%s", notify)) } - if listenfds, ok := os.LookupEnv("LISTEN_FDS"); ok { - env = append(env, fmt.Sprintf("LISTEN_FDS=%s", listenfds), "LISTEN_PID=1") - fds := activation.Files(false) - extraFiles = append(extraFiles, fds...) + if !r.sdNotify { + if listenfds, ok := os.LookupEnv("LISTEN_FDS"); ok { + env = append(env, fmt.Sprintf("LISTEN_FDS=%s", listenfds), "LISTEN_PID=1") + fds := activation.Files(false) + extraFiles = append(extraFiles, fds...) + } + } else { + logrus.Debug("disabling SD notify") } return env, extraFiles, nil } @@ -445,6 +449,15 @@ func readConmonPipeData(pipe *os.File, ociLog string) (int, error) { select { case ss := <-ch: if ss.err != nil { + if ociLog != "" { + ociLogData, err := ioutil.ReadFile(ociLog) + if err == nil { + var ociErr ociError + if err := json.Unmarshal(ociLogData, &ociErr); err == nil { + return -1, getOCIRuntimeError(ociErr.Msg) + } + } + } return -1, errors.Wrapf(ss.err, "error reading container (probably exited) json message") } logrus.Debugf("Received: %d", ss.si.Data) @@ -472,10 +485,11 @@ func readConmonPipeData(pipe *os.File, ociLog string) (int, error) { } func getOCIRuntimeError(runtimeMsg string) error { - if match, _ := regexp.MatchString(".*permission denied.*", runtimeMsg); match { + r := strings.ToLower(runtimeMsg) + if match, _ := regexp.MatchString(".*permission denied.*|.*operation not permitted.*", r); match { return errors.Wrapf(define.ErrOCIRuntimePermissionDenied, "%s", strings.Trim(runtimeMsg, "\n")) } - if match, _ := regexp.MatchString(".*executable file not found in.*", runtimeMsg); match { + if match, _ := regexp.MatchString(".*executable file not found in.*|.*no such file or directory.*", r); match { return errors.Wrapf(define.ErrOCIRuntimeNotFound, "%s", strings.Trim(runtimeMsg, "\n")) } return errors.Wrapf(define.ErrOCIRuntime, "%s", strings.Trim(runtimeMsg, "\n")) diff --git a/libpod/options.go b/libpod/options.go index 7fbd0016a..a7ddbec34 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -482,6 +482,15 @@ func WithEventsLogger(logger string) RuntimeOption { } } +// WithEnableSDNotify sets a runtime option so we know whether to disable socket/FD +// listening +func WithEnableSDNotify() RuntimeOption { + return func(rt *Runtime) error { + rt.config.SDNotify = true + return nil + } +} + // Container Creation Options // WithShmDir sets the directory that should be mounted on /dev/shm. @@ -1362,6 +1371,17 @@ func WithNamedVolumes(volumes []*ContainerNamedVolume) CtrCreateOption { } } +// WithHealthCheck adds the healthcheck to the container config +func WithHealthCheck(healthCheck *manifest.Schema2HealthConfig) CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return define.ErrCtrFinalized + } + ctr.config.HealthCheckConfig = healthCheck + return nil + } +} + // Volume Creation Options // WithVolumeName sets the name of the volume. @@ -1381,30 +1401,30 @@ func WithVolumeName(name string) VolumeCreateOption { } } -// WithVolumeLabels sets the labels of the volume. -func WithVolumeLabels(labels map[string]string) VolumeCreateOption { +// WithVolumeDriver sets the volume's driver. +// It is presently not implemented, but will be supported in a future Podman +// release. +func WithVolumeDriver(driver string) VolumeCreateOption { return func(volume *Volume) error { if volume.valid { return define.ErrVolumeFinalized } - volume.config.Labels = make(map[string]string) - for key, value := range labels { - volume.config.Labels[key] = value - } - - return nil + return define.ErrNotImplemented } } -// WithVolumeDriver sets the driver of the volume. -func WithVolumeDriver(driver string) VolumeCreateOption { +// WithVolumeLabels sets the labels of the volume. +func WithVolumeLabels(labels map[string]string) VolumeCreateOption { return func(volume *Volume) error { if volume.valid { return define.ErrVolumeFinalized } - volume.config.Driver = driver + volume.config.Labels = make(map[string]string) + for key, value := range labels { + volume.config.Labels[key] = value + } return nil } @@ -1488,6 +1508,24 @@ func WithPodName(name string) PodCreateOption { } } +// WithPodHostname sets the hostname of the pod. +func WithPodHostname(hostname string) PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + + // Check the hostname against a regex + if !nameRegex.MatchString(hostname) { + return regexError + } + + pod.config.Hostname = hostname + + return nil + } +} + // WithPodLabels sets the labels of a pod. func WithPodLabels(labels map[string]string) PodCreateOption { return func(pod *Pod) error { @@ -1673,14 +1711,3 @@ func WithInfraContainerPorts(bindings []ocicni.PortMapping) PodCreateOption { return nil } } - -// WithHealthCheck adds the healthcheck to the container config -func WithHealthCheck(healthCheck *manifest.Schema2HealthConfig) CtrCreateOption { - return func(ctr *Container) error { - if ctr.valid { - return define.ErrCtrFinalized - } - ctr.config.HealthCheckConfig = healthCheck - return nil - } -} diff --git a/libpod/pod.go b/libpod/pod.go index 60626bfd7..3b9bb9c60 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -36,6 +36,8 @@ type PodConfig struct { // Namespace the pod is in Namespace string `json:"namespace,omitempty"` + Hostname string `json:"hostname,omitempty"` + // Labels contains labels applied to the pod Labels map[string]string `json:"labels"` // CgroupParent contains the pod's CGroup parent diff --git a/libpod/runtime.go b/libpod/runtime.go index ca59dc304..cbbf667db 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -252,6 +252,10 @@ type RuntimeConfig struct { EventsLogFilePath string `toml:"-events_logfile_path"` //DetachKeys is the sequence of keys used to detach a container DetachKeys string `toml:"detach_keys"` + + // SDNotify tells Libpod to allow containers to notify the host + // systemd of readiness using the SD_NOTIFY mechanism + SDNotify bool } // runtimeConfiguredFrom is a struct used during early runtime init to help diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index da35b7f93..ad6662f03 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -9,6 +9,7 @@ import ( "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/rootless" + "github.com/containers/libpod/pkg/util" "github.com/opencontainers/image-spec/specs-go/v1" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" @@ -30,6 +31,9 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, imgID return nil, err } + // Set Pod hostname + g.Config.Hostname = p.config.Hostname + isRootless := rootless.IsRootless() entryCmd := []string{r.config.InfraCommand} @@ -108,7 +112,7 @@ func (r *Runtime) createInfraContainer(ctx context.Context, p *Pod) (*Container, return nil, define.ErrRuntimeStopped } - newImage, err := r.ImageRuntime().New(ctx, r.config.InfraImage, "", "", nil, nil, image.SigningOptions{}, false, nil) + newImage, err := r.ImageRuntime().New(ctx, r.config.InfraImage, "", "", nil, nil, image.SigningOptions{}, nil, util.PullImageMissing) if err != nil { return nil, err } diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go index f38e6e7c1..073c5054d 100644 --- a/libpod/runtime_pod_linux.go +++ b/libpod/runtime_pod_linux.go @@ -52,6 +52,10 @@ func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (_ *Po pod.config.Name = name } + if pod.config.Hostname == "" { + pod.config.Hostname = pod.config.Name + } + // Allocate a lock for the pod lock, err := r.lockManager.AllocateLock() if err != nil { diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go index ac6fd02c3..84703787d 100644 --- a/libpod/runtime_volume_linux.go +++ b/libpod/runtime_volume_linux.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "strings" + "time" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/events" @@ -42,14 +43,10 @@ func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption) if volume.config.Name == "" { volume.config.Name = stringid.GenerateNonCryptoID() } - // TODO: support for other volume drivers if volume.config.Driver == "" { volume.config.Driver = "local" } - // TODO: determine when the scope is global and set it to that - if volume.config.Scope == "" { - volume.config.Scope = "local" - } + volume.config.CreatedTime = time.Now() // Create the mountpoint of this volume volPathRoot := filepath.Join(r.config.VolumePath, volume.config.Name) diff --git a/libpod/util_linux.go b/libpod/util_linux.go index 78cbc75a7..d5c113daf 100644 --- a/libpod/util_linux.go +++ b/libpod/util_linux.go @@ -48,6 +48,9 @@ func makeSystemdCgroup(path string) error { return err } + if rootless.IsRootless() { + return controller.CreateSystemdUserUnit(path, rootless.GetRootlessUID()) + } return controller.CreateSystemdUnit(path) } @@ -57,6 +60,14 @@ func deleteSystemdCgroup(path string) error { if err != nil { return err } + if rootless.IsRootless() { + conn, err := cgroups.GetUserConnection(rootless.GetRootlessUID()) + if err != nil { + return err + } + defer conn.Close() + return controller.DeleteByPathConn(path, conn) + } return controller.DeleteByPath(path) } diff --git a/libpod/volume.go b/libpod/volume.go index 9ed2ff087..74126b49b 100644 --- a/libpod/volume.go +++ b/libpod/volume.go @@ -1,5 +1,9 @@ package libpod +import ( + "time" +) + // Volume is the type used to create named volumes // TODO: all volumes should be created using this and the Volume API type Volume struct { @@ -15,10 +19,10 @@ type VolumeConfig struct { Name string `json:"name"` Labels map[string]string `json:"labels"` - MountPoint string `json:"mountPoint"` Driver string `json:"driver"` + MountPoint string `json:"mountPoint"` + CreatedTime time.Time `json:"createdAt,omitempty"` Options map[string]string `json:"options"` - Scope string `json:"scope"` IsCtrSpecific bool `json:"ctrSpecific"` UID int `json:"uid"` GID int `json:"gid"` @@ -29,6 +33,18 @@ func (v *Volume) Name() string { return v.config.Name } +// Driver retrieves the volume's driver. +func (v *Volume) Driver() string { + return v.config.Driver +} + +// Scope retrieves the volume's scope. +// Libpod does not implement volume scoping, and this is provided solely for +// Docker compatibility. It returns only "local". +func (v *Volume) Scope() string { + return "local" +} + // Labels returns the volume's labels func (v *Volume) Labels() map[string]string { labels := make(map[string]string) @@ -43,11 +59,6 @@ func (v *Volume) MountPoint() string { return v.config.MountPoint } -// Driver returns the volume's driver -func (v *Volume) Driver() string { - return v.config.Driver -} - // Options return the volume's options func (v *Volume) Options() map[string]string { options := make(map[string]string) @@ -58,14 +69,25 @@ func (v *Volume) Options() map[string]string { return options } -// Scope returns the scope of the volume -func (v *Volume) Scope() string { - return v.config.Scope -} - // IsCtrSpecific returns whether this volume was created specifically for a // given container. Images with this set to true will be removed when the // container is removed with the Volumes parameter set to true. func (v *Volume) IsCtrSpecific() bool { return v.config.IsCtrSpecific } + +// UID returns the UID the volume will be created as. +func (v *Volume) UID() int { + return v.config.UID +} + +// GID returns the GID the volume will be created as. +func (v *Volume) GID() int { + return v.config.GID +} + +// CreatedTime returns the time the volume was created at. It was not tracked +// for some time, so older volumes may not contain one. +func (v *Volume) CreatedTime() time.Time { + return v.config.CreatedTime +} diff --git a/libpod/volume_inspect.go b/libpod/volume_inspect.go new file mode 100644 index 000000000..87ed9d340 --- /dev/null +++ b/libpod/volume_inspect.go @@ -0,0 +1,70 @@ +package libpod + +import ( + "time" + + "github.com/containers/libpod/libpod/define" +) + +// InspectVolumeData is the output of Inspect() on a volume. It is matched to +// the format of 'docker volume inspect'. +type InspectVolumeData struct { + // Name is the name of the volume. + Name string `json:"Name"` + // Driver is the driver used to create the volume. + // This will be properly implemented in a future version. + Driver string `json:"Driver"` + // Mountpoint is the path on the host where the volume is mounted. + Mountpoint string `json:"Mountpoint"` + // CreatedAt is the date and time the volume was created at. This is not + // stored for older Libpod volumes; if so, it will be omitted. + CreatedAt time.Time `json:"CreatedAt,omitempty"` + // Status is presently unused and provided only for Docker compatibility. + // In the future it will be used to return information on the volume's + // current state. + Status map[string]string `json:"Status,omitempty"` + // Labels includes the volume's configured labels, key:value pairs that + // can be passed during volume creation to provide information for third + // party tools. + Labels map[string]string `json:"Labels"` + // Scope is unused and provided solely for Docker compatibility. It is + // unconditionally set to "local". + Scope string `json:"Scope"` + // Options is a set of options that were used when creating the volume. + // It is presently not used. + Options map[string]string `json:"Options"` + // UID is the UID that the volume was created with. + UID int `json:"UID,omitempty"` + // GID is the GID that the volume was created with. + GID int `json:"GID,omitempty"` + // ContainerSpecific indicates that the volume was created as part of a + // specific container, and will be removed when that container is + // removed. + ContainerSpecific bool `json:"ContainerSpecific,omitempty"` +} + +// Inspect provides detailed information about the configuration of the given +// volume. +func (v *Volume) Inspect() (*InspectVolumeData, error) { + if !v.valid { + return nil, define.ErrVolumeRemoved + } + + data := new(InspectVolumeData) + + data.Name = v.config.Name + data.Driver = v.config.Driver + data.Mountpoint = v.config.MountPoint + data.CreatedAt = v.config.CreatedTime + data.Labels = make(map[string]string) + for k, v := range v.config.Labels { + data.Labels[k] = v + } + data.Scope = v.Scope() + data.Options = make(map[string]string) + data.UID = v.config.UID + data.GID = v.config.GID + data.ContainerSpecific = v.config.IsCtrSpecific + + return data, nil +} |