diff options
-rwxr-xr-x | API.md | 38 | ||||
-rw-r--r-- | cmd/podman/commands_remoteclient.go | 2 | ||||
-rw-r--r-- | cmd/podman/images.go | 6 | ||||
-rw-r--r-- | cmd/podman/port.go | 7 | ||||
-rw-r--r-- | cmd/podman/shared/container.go | 7 | ||||
-rw-r--r-- | cmd/podman/varlink/io.podman.varlink | 17 | ||||
-rw-r--r-- | cmd/podman/volume_inspect.go | 22 | ||||
-rw-r--r-- | cmd/podman/volume_ls.go | 42 | ||||
-rw-r--r-- | libpod/adapter/runtime.go | 54 | ||||
-rw-r--r-- | libpod/adapter/runtime_remote.go | 54 | ||||
-rw-r--r-- | libpod/adapter/volumes_remote.go | 33 | ||||
-rw-r--r-- | libpod/container.go | 12 | ||||
-rw-r--r-- | libpod/image/utils.go | 17 | ||||
-rw-r--r-- | libpod/kube.go | 6 | ||||
-rw-r--r-- | pkg/varlinkapi/volumes.go | 36 |
15 files changed, 304 insertions, 49 deletions
@@ -53,6 +53,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [func GetVersion() string, string, string, string, string, int](#GetVersion) +[func GetVolumes(args: []string, all: bool) Volume](#GetVolumes) + [func HistoryImage(name: string) ImageHistory](#HistoryImage) [func ImageExists(name: string) int](#ImageExists) @@ -175,6 +177,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [type MoreResponse](#MoreResponse) +[type NotImplemented](#NotImplemented) + [type PodContainerErrorData](#PodContainerErrorData) [type PodCreate](#PodCreate) @@ -185,6 +189,10 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [type Sockets](#Sockets) +[type StringResponse](#StringResponse) + +[type Volume](#Volume) + [type VolumeCreateOpts](#VolumeCreateOpts) [type VolumeRemoveOpts](#VolumeRemoveOpts) @@ -516,6 +524,11 @@ $ varlink call unix:/run/podman/io.podman/io.podman.GetPodStats '{"name": "7f62b method GetVersion() [string](https://godoc.org/builtin#string), [string](https://godoc.org/builtin#string), [string](https://godoc.org/builtin#string), [string](https://godoc.org/builtin#string), [string](https://godoc.org/builtin#string), [int](https://godoc.org/builtin#int)</div> GetVersion returns version and build information of the podman service +### <a name="GetVolumes"></a>func GetVolumes +<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> + +method GetVolumes(args: [[]string](#[]string), all: [bool](https://godoc.org/builtin#bool)) [Volume](#Volume)</div> + ### <a name="HistoryImage"></a>func HistoryImage <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> @@ -1480,6 +1493,11 @@ MoreResponse is a struct for when responses from varlink requires longer output logs [[]string](#[]string) id [string](https://godoc.org/builtin#string) +### <a name="NotImplemented"></a>type NotImplemented + + + +comment [string](https://godoc.org/builtin#string) ### <a name="PodContainerErrorData"></a>type PodContainerErrorData @@ -1557,6 +1575,26 @@ container_id [string](https://godoc.org/builtin#string) io_socket [string](https://godoc.org/builtin#string) control_socket [string](https://godoc.org/builtin#string) +### <a name="StringResponse"></a>type StringResponse + + + +message [string](https://godoc.org/builtin#string) +### <a name="Volume"></a>type Volume + + + +name [string](https://godoc.org/builtin#string) + +labels [map[string]](#map[string]) + +mountPoint [string](https://godoc.org/builtin#string) + +driver [string](https://godoc.org/builtin#string) + +options [map[string]](#map[string]) + +scope [string](https://godoc.org/builtin#string) ### <a name="VolumeCreateOpts"></a>type VolumeCreateOpts diff --git a/cmd/podman/commands_remoteclient.go b/cmd/podman/commands_remoteclient.go index a656d5a29..7bdba1c19 100644 --- a/cmd/podman/commands_remoteclient.go +++ b/cmd/podman/commands_remoteclient.go @@ -36,6 +36,8 @@ func getVolumeSubCommands() []*cobra.Command { return []*cobra.Command{ _volumeCreateCommand, _volumeRmCommand, + _volumeLsCommand, + _volumeInspectCommand, } } diff --git a/cmd/podman/images.go b/cmd/podman/images.go index a9e8abbde..b269f6440 100644 --- a/cmd/podman/images.go +++ b/cmd/podman/images.go @@ -247,8 +247,12 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma } // get all specified repo:tag pairs and print them separately + repopairs, err := image.ReposToMap(img.Names()) + if err != nil { + logrus.Errorf("error finding tag/digest for %s", img.ID()) + } outer: - for repo, tags := range image.ReposToMap(img.Names()) { + for repo, tags := range repopairs { for _, tag := range tags { size, err := img.Size(ctx) var sizeStr string diff --git a/cmd/podman/port.go b/cmd/podman/port.go index 5a5b6127b..be84da065 100644 --- a/cmd/podman/port.go +++ b/cmd/podman/port.go @@ -125,8 +125,13 @@ func portCmd(c *cliconfig.PortValues) error { if c.All { fmt.Println(con.ID()) } + + portmappings, err := con.PortMappings() + if err != nil { + return err + } // Iterate mappings - for _, v := range con.Config().PortMappings { + for _, v := range portmappings { hostIP := v.HostIP // Set host IP to 0.0.0.0 if blank if hostIP == "" { diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go index c74d8fdce..81811e0f2 100644 --- a/cmd/podman/shared/container.go +++ b/cmd/podman/shared/container.go @@ -213,11 +213,16 @@ func NewBatchContainer(ctr *libpod.Container, opts PsOptions) (PsContainerOutput } } + ports, err := ctr.PortMappings() + if err != nil { + logrus.Errorf("unable to lookup namespace container for %s", ctr.ID()) + } + pso.ID = cid pso.Image = imageName pso.Command = command pso.Created = created - pso.Ports = portsToString(ctr.PortMappings()) + pso.Ports = portsToString(ports) pso.Names = ctr.Name() pso.IsInfra = ctr.IsInfra() pso.Status = status diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index c3900ca18..03ea06dfc 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -2,6 +2,22 @@ # in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in the upstream libpod repository. interface io.podman +type Volume ( + name: string, + labels: [string]string, + mountPoint: string, + driver: string, + options: [string]string, + scope: string +) + +type NotImplemented ( + comment: string +) + +type StringResponse ( + message: string +) # ContainerChanges describes the return struct for ListContainerChanges type ContainerChanges ( @@ -1044,6 +1060,7 @@ method VolumeCreate(options: VolumeCreateOpts) -> (volumeName: string) method VolumeRemove(options: VolumeRemoveOpts) -> (volumeNames: []string) +method GetVolumes(args: []string, all: bool) -> (volumes: []Volume) # ImageNotFound means the image could not be found by the provided name or ID in local storage. error ImageNotFound (id: string) diff --git a/cmd/podman/volume_inspect.go b/cmd/podman/volume_inspect.go index a8e6f489f..928ef37d0 100644 --- a/cmd/podman/volume_inspect.go +++ b/cmd/podman/volume_inspect.go @@ -2,9 +2,8 @@ package main import ( "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/libpod/adapter" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -39,22 +38,19 @@ func init() { } func volumeInspectCmd(c *cliconfig.VolumeInspectValues) error { - var err error + if (c.All && len(c.InputArgs) > 0) || (!c.All && len(c.InputArgs) < 1) { + return errors.New("provide one or more volume names or use --all") + } - runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) - opts := volumeLsOptions{ - Format: c.Format, - } - - vols, lastError := getVolumesFromContext(&c.PodmanCommand, runtime) - if lastError != nil { - logrus.Errorf("%q", lastError) + vols, err := runtime.InspectVolumes(getContext(), c) + if err != nil { + return err } - - return generateVolLsOutput(vols, opts, runtime) + return generateVolLsOutput(vols, volumeLsOptions{Format: c.Format}) } diff --git a/cmd/podman/volume_ls.go b/cmd/podman/volume_ls.go index 87b14a4b2..0edadc5ac 100644 --- a/cmd/podman/volume_ls.go +++ b/cmd/podman/volume_ls.go @@ -6,8 +6,7 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/formats" - "github.com/containers/libpod/cmd/podman/libpodruntime" - "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/adapter" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -71,7 +70,7 @@ func init() { } func volumeLsCmd(c *cliconfig.VolumeLsValues) error { - runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } @@ -87,7 +86,7 @@ func volumeLsCmd(c *cliconfig.VolumeLsValues) error { opts.Format = genVolLsFormat(c) // Get the filter functions based on any filters set - var filterFuncs []libpod.VolumeFilter + var filterFuncs []adapter.VolumeFilter if c.Filter != "" { filters := strings.Split(c.Filter, ",") for _, f := range filters { @@ -95,7 +94,7 @@ func volumeLsCmd(c *cliconfig.VolumeLsValues) error { if len(filterSplit) < 2 { return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) } - generatedFunc, err := generateVolumeFilterFuncs(filterSplit[0], filterSplit[1], runtime) + generatedFunc, err := generateVolumeFilterFuncs(filterSplit[0], filterSplit[1]) if err != nil { return errors.Wrapf(err, "invalid filter") } @@ -103,13 +102,12 @@ func volumeLsCmd(c *cliconfig.VolumeLsValues) error { } } - volumes, err := runtime.GetAllVolumes() + volumes, err := runtime.Volumes(getContext()) if err != nil { return err } - // Get the volumes that match the filter - volsFiltered := make([]*libpod.Volume, 0, len(volumes)) + volsFiltered := make([]*adapter.Volume, 0, len(volumes)) for _, vol := range volumes { include := true for _, filter := range filterFuncs { @@ -120,7 +118,7 @@ func volumeLsCmd(c *cliconfig.VolumeLsValues) error { volsFiltered = append(volsFiltered, vol) } } - return generateVolLsOutput(volsFiltered, opts, runtime) + return generateVolLsOutput(volsFiltered, opts) } // generate the template based on conditions given @@ -206,7 +204,7 @@ func getVolTemplateOutput(lsParams []volumeLsJSONParams, opts volumeLsOptions) ( } // getVolJSONParams returns the volumes in JSON format -func getVolJSONParams(volumes []*libpod.Volume, opts volumeLsOptions, runtime *libpod.Runtime) ([]volumeLsJSONParams, error) { +func getVolJSONParams(volumes []*adapter.Volume) []volumeLsJSONParams { var lsOutput []volumeLsJSONParams for _, volume := range volumes { @@ -221,25 +219,19 @@ func getVolJSONParams(volumes []*libpod.Volume, opts volumeLsOptions, runtime *l lsOutput = append(lsOutput, params) } - return lsOutput, nil + return lsOutput } // generateVolLsOutput generates the output based on the format, JSON or Go Template, and prints it out -func generateVolLsOutput(volumes []*libpod.Volume, opts volumeLsOptions, runtime *libpod.Runtime) error { +func generateVolLsOutput(volumes []*adapter.Volume, opts volumeLsOptions) error { if len(volumes) == 0 && opts.Format != formats.JSONString { return nil } - lsOutput, err := getVolJSONParams(volumes, opts, runtime) - if err != nil { - return err - } + lsOutput := getVolJSONParams(volumes) var out formats.Writer switch opts.Format { case formats.JSONString: - if err != nil { - return errors.Wrapf(err, "unable to create JSON for volume output") - } out = formats.JSONStructArray{Output: volLsToGeneric([]volumeLsTemplateParams{}, lsOutput)} default: lsOutput, err := getVolTemplateOutput(lsOutput, opts) @@ -252,18 +244,18 @@ func generateVolLsOutput(volumes []*libpod.Volume, opts volumeLsOptions, runtime } // generateVolumeFilterFuncs returns the true if the volume matches the filter set, otherwise it returns false. -func generateVolumeFilterFuncs(filter, filterValue string, runtime *libpod.Runtime) (func(volume *libpod.Volume) bool, error) { +func generateVolumeFilterFuncs(filter, filterValue string) (func(volume *adapter.Volume) bool, error) { switch filter { case "name": - return func(v *libpod.Volume) bool { + return func(v *adapter.Volume) bool { return strings.Contains(v.Name(), filterValue) }, nil case "driver": - return func(v *libpod.Volume) bool { + return func(v *adapter.Volume) bool { return v.Driver() == filterValue }, nil case "scope": - return func(v *libpod.Volume) bool { + return func(v *adapter.Volume) bool { return v.Scope() == filterValue }, nil case "label": @@ -274,7 +266,7 @@ func generateVolumeFilterFuncs(filter, filterValue string, runtime *libpod.Runti } else { filterValue = "" } - return func(v *libpod.Volume) bool { + return func(v *adapter.Volume) bool { for labelKey, labelValue := range v.Labels() { if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { return true @@ -290,7 +282,7 @@ func generateVolumeFilterFuncs(filter, filterValue string, runtime *libpod.Runti } else { filterValue = "" } - return func(v *libpod.Volume) bool { + return func(v *adapter.Volume) bool { for labelKey, labelValue := range v.Options() { if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { return true diff --git a/libpod/adapter/runtime.go b/libpod/adapter/runtime.go index acaecf4b5..7dd845616 100644 --- a/libpod/adapter/runtime.go +++ b/libpod/adapter/runtime.go @@ -35,6 +35,14 @@ type Container struct { *libpod.Container } +// Volume ... +type Volume struct { + *libpod.Volume +} + +// VolumeFilter is for filtering volumes on the client +type VolumeFilter func(*Volume) bool + // GetRuntime returns a LocalRuntime struct with the actual runtime embedded in it func GetRuntime(c *cliconfig.PodmanCommand) (*LocalRuntime, error) { runtime, err := libpodruntime.GetRuntime(c) @@ -200,3 +208,49 @@ func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestM } return newImage.PushImageToHeuristicDestination(ctx, destination, manifestMIMEType, authfile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, nil) } + +// InspectVolumes returns a slice of volumes based on an arg list or --all +func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeInspectValues) ([]*Volume, error) { + var ( + volumes []*libpod.Volume + err error + ) + + if c.All { + volumes, err = r.GetAllVolumes() + } else { + for _, v := range c.InputArgs { + vol, err := r.GetVolume(v) + if err != nil { + return nil, err + } + volumes = append(volumes, vol) + } + } + if err != nil { + return nil, err + } + return libpodVolumeToVolume(volumes), nil +} + +// Volumes returns a slice of localruntime volumes +func (r *LocalRuntime) Volumes(ctx context.Context) ([]*Volume, error) { + vols, err := r.GetAllVolumes() + if err != nil { + return nil, err + } + return libpodVolumeToVolume(vols), nil +} + +// libpodVolumeToVolume converts a slice of libpod volumes to a slice +// of localruntime volumes (same as libpod) +func libpodVolumeToVolume(volumes []*libpod.Volume) []*Volume { + var vols []*Volume + for _, v := range volumes { + newVol := Volume{ + v, + } + vols = append(vols, &newVol) + } + return vols +} diff --git a/libpod/adapter/runtime_remote.go b/libpod/adapter/runtime_remote.go index ab9b4501d..d0d827582 100644 --- a/libpod/adapter/runtime_remote.go +++ b/libpod/adapter/runtime_remote.go @@ -92,6 +92,18 @@ type remoteContainer struct { state *libpod.ContainerState } +type VolumeFilter func(*Volume) bool + +// Volume is embed for libpod volumes +type Volume struct { + remoteVolume +} + +type remoteVolume struct { + Runtime *LocalRuntime + config *libpod.VolumeConfig +} + // GetImages returns a slice of containerimages over a varlink connection func (r *LocalRuntime) GetImages() ([]*ContainerImage, error) { var newImages []*ContainerImage @@ -480,3 +492,45 @@ func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestM return err } + +// InspectVolumes returns a slice of volumes based on an arg list or --all +func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeInspectValues) ([]*Volume, error) { + reply, err := iopodman.GetVolumes().Call(r.Conn, c.InputArgs, c.All) + if err != nil { + return nil, err + } + return varlinkVolumeToVolume(r, reply), nil +} + +//Volumes returns a slice of adapter.volumes based on information about libpod +// volumes over a varlink connection +func (r *LocalRuntime) Volumes(ctx context.Context) ([]*Volume, error) { + reply, err := iopodman.GetVolumes().Call(r.Conn, []string{}, true) + if err != nil { + return nil, err + } + return varlinkVolumeToVolume(r, reply), nil +} + +func varlinkVolumeToVolume(r *LocalRuntime, volumes []iopodman.Volume) []*Volume { + var vols []*Volume + for _, v := range volumes { + volumeConfig := libpod.VolumeConfig{ + Name: v.Name, + Labels: v.Labels, + MountPoint: v.MountPoint, + Driver: v.Driver, + Options: v.Options, + Scope: v.Scope, + } + n := remoteVolume{ + Runtime: r, + config: &volumeConfig, + } + newVol := Volume{ + n, + } + vols = append(vols, &newVol) + } + return vols +} diff --git a/libpod/adapter/volumes_remote.go b/libpod/adapter/volumes_remote.go new file mode 100644 index 000000000..beacd943a --- /dev/null +++ b/libpod/adapter/volumes_remote.go @@ -0,0 +1,33 @@ +// +build remoteclient + +package adapter + +// Name returns the name of the volume +func (v *Volume) Name() string { + return v.config.Name +} + +//Labels returns the labels for a volume +func (v *Volume) Labels() map[string]string { + return v.config.Labels +} + +// Driver returns the driver for the volume +func (v *Volume) Driver() string { + return v.config.Driver +} + +// Options returns the options a volume was created with +func (v *Volume) Options() map[string]string { + return v.config.Options +} + +// MountPath returns the path the volume is mounted to +func (v *Volume) MountPoint() string { + return v.config.MountPoint +} + +// Scope returns the scope for an adapter.volume +func (v *Volume) Scope() string { + return v.config.Scope +} diff --git a/libpod/container.go b/libpod/container.go index b0589be3b..fec61533d 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -557,8 +557,16 @@ func (c *Container) NewNetNS() bool { // PortMappings returns the ports that will be mapped into a container if // a new network namespace is created // If NewNetNS() is false, this value is unused -func (c *Container) PortMappings() []ocicni.PortMapping { - return c.config.PortMappings +func (c *Container) PortMappings() ([]ocicni.PortMapping, error) { + // First check if the container belongs to a network namespace (like a pod) + if len(c.config.NetNsCtr) > 0 { + netNsCtr, err := c.runtime.LookupContainer(c.config.NetNsCtr) + if err != nil { + return nil, errors.Wrapf(err, "unable to lookup network namespace for container %s", c.ID()) + } + return netNsCtr.PortMappings() + } + return c.config.PortMappings, nil } // DNSServers returns DNS servers that will be used in the container's diff --git a/libpod/image/utils.go b/libpod/image/utils.go index ad027f32a..3585428ad 100644 --- a/libpod/image/utils.go +++ b/libpod/image/utils.go @@ -87,22 +87,29 @@ func hasTransport(image string) bool { // ReposToMap parses the specified repotags and returns a map with repositories // as keys and the corresponding arrays of tags as values. -func ReposToMap(repotags []string) map[string][]string { +func ReposToMap(repotags []string) (map[string][]string, error) { // map format is repo -> tag repos := make(map[string][]string) for _, repo := range repotags { var repository, tag string if len(repo) > 0 { - li := strings.LastIndex(repo, ":") - repository = repo[0:li] - tag = repo[li+1:] + named, err := reference.ParseNormalizedNamed(repo) + repository = named.Name() + if err != nil { + return nil, err + } + if ref, ok := named.(reference.NamedTagged); ok { + tag = ref.Tag() + } else if ref, ok := named.(reference.Canonical); ok { + tag = ref.Digest().String() + } } repos[repository] = append(repos[repository], tag) } if len(repos) == 0 { repos["<none>"] = []string{"<none>"} } - return repos + return repos, nil } // GetAdditionalTags returns a list of reference.NamedTagged for the diff --git a/libpod/kube.go b/libpod/kube.go index 16cebf99b..484127870 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -228,7 +228,11 @@ func containerToV1Container(c *Container) (v1.Container, error) { return kubeContainer, nil } - ports, err := ocicniPortMappingToContainerPort(c.PortMappings()) + portmappings, err := c.PortMappings() + if err != nil { + return kubeContainer, err + } + ports, err := ocicniPortMappingToContainerPort(portmappings) if err != nil { return kubeContainer, nil } diff --git a/pkg/varlinkapi/volumes.go b/pkg/varlinkapi/volumes.go index ced394e90..d41b07065 100644 --- a/pkg/varlinkapi/volumes.go +++ b/pkg/varlinkapi/volumes.go @@ -36,3 +36,39 @@ func (i *LibpodAPI) VolumeRemove(call iopodman.VarlinkCall, options iopodman.Vol } return call.ReplyVolumeRemove(deletedVolumes) } + +// GetVolumes returns all the volumes known to the remote system +func (i *LibpodAPI) GetVolumes(call iopodman.VarlinkCall, args []string, all bool) error { + var ( + err error + reply []*libpod.Volume + volumes []iopodman.Volume + ) + if all { + reply, err = i.Runtime.GetAllVolumes() + } else { + for _, v := range args { + vol, err := i.Runtime.GetVolume(v) + if err != nil { + return err + } + reply = append(reply, vol) + } + } + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + // Build the iopodman.volume struct for the return + for _, v := range reply { + newVol := iopodman.Volume{ + Driver: v.Driver(), + Labels: v.Labels(), + MountPoint: v.MountPoint(), + Name: v.Name(), + Options: v.Options(), + Scope: v.Scope(), + } + volumes = append(volumes, newVol) + } + return call.ReplyGetVolumes(volumes) +} |