diff options
author | baude <bbaude@redhat.com> | 2019-04-02 10:26:43 -0500 |
---|---|---|
committer | baude <bbaude@redhat.com> | 2019-04-04 14:41:50 -0500 |
commit | dc94dbd3c17c9011fc1e851694a2190dcf6c2b3c (patch) | |
tree | b887cd75144e555acfb4a346f43b89cdf5470d8c | |
parent | 1759eb09e1c13bc8392d515d69ca93226d067c73 (diff) | |
download | podman-dc94dbd3c17c9011fc1e851694a2190dcf6c2b3c.tar.gz podman-dc94dbd3c17c9011fc1e851694a2190dcf6c2b3c.tar.bz2 podman-dc94dbd3c17c9011fc1e851694a2190dcf6c2b3c.zip |
podman-remote image tree
add the ability for the podman-remote client to be able to print an
image tree.
Signed-off-by: baude <bbaude@redhat.com>
-rwxr-xr-x | API.md | 18 | ||||
-rw-r--r-- | cmd/podman/tree.go | 77 | ||||
-rw-r--r-- | cmd/podman/varlink/io.podman.varlink | 10 | ||||
-rw-r--r-- | libpod/image/image.go | 28 | ||||
-rw-r--r-- | pkg/adapter/images.go | 34 | ||||
-rw-r--r-- | pkg/adapter/images_remote.go | 32 | ||||
-rw-r--r-- | pkg/adapter/runtime_remote.go | 7 | ||||
-rw-r--r-- | pkg/varlinkapi/images.go | 38 |
8 files changed, 181 insertions, 63 deletions
@@ -5,6 +5,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [func BuildImage(build: BuildInfo) MoreResponse](#BuildImage) +[func BuildImageHierarchyMap(name: string) string](#BuildImageHierarchyMap) + [func Commit(name: string, image_name: string, changes: []string, author: string, message: string, pause: bool, manifestType: string) string](#Commit) [func ContainerArtifacts(name: string, artifactName: string) string](#ContainerArtifacts) @@ -57,6 +59,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [func GetInfo() PodmanInfo](#GetInfo) +[func GetLayersMapWithImageInfo() string](#GetLayersMapWithImageInfo) + [func GetPod(name: string) ListPodData](#GetPod) [func GetPodStats(name: string) string, ContainerStats](#GetPodStats) @@ -259,6 +263,11 @@ method BuildImage(build: [BuildInfo](#BuildInfo)) [MoreResponse](#MoreResponse)< BuildImage takes a [BuildInfo](#BuildInfo) structure and builds an image. At a minimum, you must provide the 'dockerfile' and 'tags' options in the BuildInfo structure. It will return a [MoreResponse](#MoreResponse) structure that contains the build logs and resulting image ID. +### <a name="BuildImageHierarchyMap"></a>func BuildImageHierarchyMap +<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> + +method BuildImageHierarchyMap(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> +BuildImageHierarchyMap is for the development of Podman and should not be used. ### <a name="Commit"></a>func Commit <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> @@ -396,7 +405,7 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.DeleteUnusedImages <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> method Diff(name: [string](https://godoc.org/builtin#string)) [DiffInfo](#DiffInfo)</div> - +Diff returns a diff between libpod objects ### <a name="ExportContainer"></a>func ExportContainer <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> @@ -520,6 +529,11 @@ If the image caGetImage returns be found, [ImageNotFound](#ImageNotFound) will b method GetInfo() [PodmanInfo](#PodmanInfo)</div> GetInfo returns a [PodmanInfo](#PodmanInfo) struct that describes podman and its host such as storage stats, build information of Podman, and system-wide registries. +### <a name="GetLayersMapWithImageInfo"></a>func GetLayersMapWithImageInfo +<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> + +method GetLayersMapWithImageInfo() [string](https://godoc.org/builtin#string)</div> +GetLayersMapWithImageInfo is for the development of Podman and should not be used. ### <a name="GetPod"></a>func GetPod <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> @@ -1507,6 +1521,8 @@ containers [int](https://godoc.org/builtin#int) labels [map[string]](#map[string]) isParent [bool](https://godoc.org/builtin#bool) + +topLayer [string](https://godoc.org/builtin#string) ### <a name="ImageHistory"></a>type ImageHistory ImageHistory describes the returned structure from ImageHistory. diff --git a/cmd/podman/tree.go b/cmd/podman/tree.go index c56e35aef..371e88495 100644 --- a/cmd/podman/tree.go +++ b/cmd/podman/tree.go @@ -5,9 +5,9 @@ import ( "fmt" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod/image" - units "github.com/docker/go-units" + "github.com/containers/libpod/pkg/adapter" + "github.com/docker/go-units" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -41,16 +41,6 @@ func init() { treeCommand.Flags().BoolVar(&treeCommand.WhatRequires, "whatrequires", false, "Show all child images and layers of the specified image") } -// infoImage keep information of Image along with all associated layers -type infoImage struct { - // id of image - id string - // tags of image - tags []string - // layers stores all layers of image. - layers []image.LayerInfo -} - func treeCmd(c *cliconfig.TreeValues) error { args := c.InputArgs if len(args) == 0 { @@ -60,46 +50,33 @@ func treeCmd(c *cliconfig.TreeValues) error { return errors.Errorf("you must provide at most 1 argument") } - 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) - - img, err := runtime.ImageRuntime().NewFromLocal(args[0]) + imageInfo, layerInfoMap, img, err := runtime.Tree(c) if err != nil { return err } + return printTree(imageInfo, layerInfoMap, img, c.WhatRequires) +} - // Fetch map of image-layers, which is used for printing output. - layerInfoMap, err := image.GetLayersMapWithImageInfo(runtime.ImageRuntime()) - if err != nil { - return errors.Wrapf(err, "error while retriving layers of image %q", img.InputName) - } - - // Create an imageInfo and fill the image and layer info - imageInfo := &infoImage{ - id: img.ID(), - tags: img.Names(), - } - +func printTree(imageInfo *image.InfoImage, layerInfoMap map[string]*image.LayerInfo, img *adapter.ContainerImage, whatRequires bool) error { size, err := img.Size(context.Background()) if err != nil { - return errors.Wrapf(err, "error while retriving image size") + return err } - fmt.Printf("Image ID: %s\n", imageInfo.id[:12]) - fmt.Printf("Tags:\t %s\n", imageInfo.tags) + + fmt.Printf("Image ID: %s\n", imageInfo.ID[:12]) + fmt.Printf("Tags:\t %s\n", imageInfo.Tags) fmt.Printf("Size:\t %v\n", units.HumanSizeWithPrecision(float64(*size), 4)) fmt.Printf(fmt.Sprintf("Image Layers\n")) - if !c.WhatRequires { + if !whatRequires { // fill imageInfo with layers associated with image. // the layers will be filled such that // (Start)RootLayer->...intermediate Parent Layer(s)-> TopLayer(End) - err := buildImageHierarchyMap(imageInfo, layerInfoMap, img.TopLayer()) - if err != nil { - return err - } // Build output from imageInfo into buffer printImageHierarchy(imageInfo) @@ -108,30 +85,8 @@ func treeCmd(c *cliconfig.TreeValues) error { // the layers will be filled such that // (Start)TopLayer->...intermediate Child Layer(s)-> Child TopLayer(End) // (Forks)... intermediate Child Layer(s) -> Child Top Layer(End) - err := printImageChildren(layerInfoMap, img.TopLayer(), "", true) - if err != nil { - return err - } - } - - return nil -} - -// Stores hierarchy of images such that all parent layers using which image is built are stored in imageInfo -// Layers are added such that (Start)RootLayer->...intermediate Parent Layer(s)-> TopLayer(End) -func buildImageHierarchyMap(imageInfo *infoImage, layerMap map[string]*image.LayerInfo, layerID string) error { - if layerID == "" { - return nil - } - ll, ok := layerMap[layerID] - if !ok { - return fmt.Errorf("lookup error: layerid %s not found", layerID) + return printImageChildren(layerInfoMap, img.TopLayer(), "", true) } - if err := buildImageHierarchyMap(imageInfo, layerMap, ll.ParentID); err != nil { - return err - } - - imageInfo.layers = append(imageInfo.layers, *ll) return nil } @@ -175,14 +130,14 @@ func printImageChildren(layerMap map[string]*image.LayerInfo, layerID string, pr } // prints the layers info of image -func printImageHierarchy(imageInfo *infoImage) { - for count, l := range imageInfo.layers { +func printImageHierarchy(imageInfo *image.InfoImage) { + for count, l := range imageInfo.Layers { var tags string intend := middleItem if len(l.RepoTags) > 0 { tags = fmt.Sprintf(" Top Layer of: %s", l.RepoTags) } - if count == len(imageInfo.layers)-1 { + if count == len(imageInfo.Layers)-1 { intend = lastItem } fmt.Printf("%s ID: %s Size: %7v%s\n", intend, l.ID[:12], units.HumanSizeWithPrecision(float64(l.Size), 4), tags) diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index 2ff06a6f6..92fdcd20f 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -68,7 +68,8 @@ type Image ( virtualSize: int, containers: int, labels: [string]string, - isParent: bool + isParent: bool, + topLayer: string ) # ImageHistory describes the returned structure from ImageHistory. @@ -1161,8 +1162,15 @@ method LoadImage(name: string, inputFile: string, quiet: bool, deleteFile: bool) # GetEvents returns known libpod events filtered by the options provided. method GetEvents(filter: []string, since: string, until: string) -> (events: Event) +# Diff returns a diff between libpod objects method Diff(name: string) -> (diffs: []DiffInfo) +# GetLayersMapWithImageInfo is for the development of Podman and should not be used. +method GetLayersMapWithImageInfo() -> (layerMap: string) + +# BuildImageHierarchyMap is for the development of Podman and should not be used. +method BuildImageHierarchyMap(name: string) -> (imageInfo: string) + # ImageNotFound means the image could not be found by the provided name or ID in local storage. error ImageNotFound (id: string, reason: string) diff --git a/libpod/image/image.go b/libpod/image/image.go index 4862bf1d6..cc056b816 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -68,6 +68,16 @@ type Runtime struct { EventsLogFilePath string } +// InfoImage keep information of Image along with all associated layers +type InfoImage struct { + // ID of image + ID string + // Tags of image + Tags []string + // Layers stores all layers of image. + Layers []LayerInfo +} + // ErrRepoTagNotFound is the error returned when the image id given doesn't match a rep tag in store var ErrRepoTagNotFound = errors.New("unable to match user input to any specific repotag") @@ -1277,3 +1287,21 @@ func GetLayersMapWithImageInfo(imageruntime *Runtime) (map[string]*LayerInfo, er } return layerInfoMap, nil } + +// BuildImageHierarchyMap stores hierarchy of images such that all parent layers using which image is built are stored in imageInfo +// Layers are added such that (Start)RootLayer->...intermediate Parent Layer(s)-> TopLayer(End) +func BuildImageHierarchyMap(imageInfo *InfoImage, layerMap map[string]*LayerInfo, layerID string) error { + if layerID == "" { + return nil + } + ll, ok := layerMap[layerID] + if !ok { + return fmt.Errorf("lookup error: layerid %s not found", layerID) + } + if err := BuildImageHierarchyMap(imageInfo, layerMap, ll.ParentID); err != nil { + return err + } + + imageInfo.Layers = append(imageInfo.Layers, *ll) + return nil +} diff --git a/pkg/adapter/images.go b/pkg/adapter/images.go new file mode 100644 index 000000000..c8ea1cdea --- /dev/null +++ b/pkg/adapter/images.go @@ -0,0 +1,34 @@ +// +build !remoteclient + +package adapter + +import ( + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/libpod/image" + "github.com/pkg/errors" +) + +// Tree ... +func (r *LocalRuntime) Tree(c *cliconfig.TreeValues) (*image.InfoImage, map[string]*image.LayerInfo, *ContainerImage, error) { + img, err := r.NewImageFromLocal(c.InputArgs[0]) + if err != nil { + return nil, nil, nil, err + } + + // Fetch map of image-layers, which is used for printing output. + layerInfoMap, err := image.GetLayersMapWithImageInfo(r.Runtime.ImageRuntime()) + if err != nil { + return nil, nil, nil, errors.Wrapf(err, "error while retrieving layers of image %q", img.InputName) + } + + // Create an imageInfo and fill the image and layer info + imageInfo := &image.InfoImage{ + ID: img.ID(), + Tags: img.Names(), + } + + if err := image.BuildImageHierarchyMap(imageInfo, layerInfoMap, img.TopLayer()); err != nil { + return nil, nil, nil, err + } + return imageInfo, layerInfoMap, img, nil +} diff --git a/pkg/adapter/images_remote.go b/pkg/adapter/images_remote.go index e7b38dccc..722058d4a 100644 --- a/pkg/adapter/images_remote.go +++ b/pkg/adapter/images_remote.go @@ -6,8 +6,11 @@ import ( "context" "encoding/json" + "github.com/containers/libpod/cmd/podman/cliconfig" iopodman "github.com/containers/libpod/cmd/podman/varlink" + "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/inspect" + "github.com/pkg/errors" ) // Inspect returns returns an ImageData struct from over a varlink connection @@ -22,3 +25,32 @@ func (i *ContainerImage) Inspect(ctx context.Context) (*inspect.ImageData, error } return &data, nil } + +// Tree ... +func (r *LocalRuntime) Tree(c *cliconfig.TreeValues) (*image.InfoImage, map[string]*image.LayerInfo, *ContainerImage, error) { + layerInfoMap := make(map[string]*image.LayerInfo) + imageInfo := &image.InfoImage{} + + img, err := r.NewImageFromLocal(c.InputArgs[0]) + if err != nil { + return nil, nil, nil, err + } + + reply, err := iopodman.GetLayersMapWithImageInfo().Call(r.Conn) + if err != nil { + return nil, nil, nil, errors.Wrap(err, "failed to obtain image layers") + } + if err := json.Unmarshal([]byte(reply), &layerInfoMap); err != nil { + return nil, nil, nil, errors.Wrap(err, "failed to unmarshal image layers") + } + + reply, err = iopodman.BuildImageHierarchyMap().Call(r.Conn, c.InputArgs[0]) + if err != nil { + return nil, nil, nil, errors.Wrap(err, "failed to get build image map") + } + if err := json.Unmarshal([]byte(reply), imageInfo); err != nil { + return nil, nil, nil, errors.Wrap(err, "failed to unmarshal build image map") + } + + return imageInfo, layerInfoMap, img, nil +} diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go index 978c9ffd8..807a9ad8f 100644 --- a/pkg/adapter/runtime_remote.go +++ b/pkg/adapter/runtime_remote.go @@ -82,6 +82,7 @@ type remoteImage struct { Digest digest.Digest isParent bool Runtime *LocalRuntime + TopLayer string } // Container ... @@ -147,6 +148,7 @@ func imageInListToContainerImage(i iopodman.Image, name string, runtime *LocalRu Names: i.RepoTags, isParent: i.IsParent, Runtime: runtime, + TopLayer: i.TopLayer, } return &ContainerImage{ri}, nil } @@ -280,6 +282,11 @@ func (ci *ContainerImage) Dangling() bool { return len(ci.Names()) == 0 } +// TopLayer returns an images top layer as a string +func (ci *ContainerImage) TopLayer() string { + return ci.remoteImage.TopLayer +} + // TagImage ... func (ci *ContainerImage) TagImage(tag string) error { _, err := iopodman.TagImage().Call(ci.Runtime.Conn, ci.ID(), tag) diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go index 63d500204..8cd13e251 100644 --- a/pkg/varlinkapi/images.go +++ b/pkg/varlinkapi/images.go @@ -103,6 +103,7 @@ func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, id string) error { VirtualSize: newImage.VirtualSize, Containers: int64(len(containers)), Labels: labels, + TopLayer: newImage.TopLayer(), } return call.ReplyGetImage(il) } @@ -923,3 +924,40 @@ func (i *LibpodAPI) Diff(call iopodman.VarlinkCall, name string) error { } return call.ReplyDiff(response) } + +// GetLayersMapWithImageInfo is a development only endpoint to obtain layer information for an image. +func (i *LibpodAPI) GetLayersMapWithImageInfo(call iopodman.VarlinkCall) error { + layerInfo, err := image.GetLayersMapWithImageInfo(i.Runtime.ImageRuntime()) + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + b, err := json.Marshal(layerInfo) + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + return call.ReplyGetLayersMapWithImageInfo(string(b)) +} + +// BuildImageHierarchyMap ... +func (i *LibpodAPI) BuildImageHierarchyMap(call iopodman.VarlinkCall, name string) error { + img, err := i.Runtime.ImageRuntime().NewFromLocal(name) + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + imageInfo := &image.InfoImage{ + ID: img.ID(), + Tags: img.Names(), + } + layerInfo, err := image.GetLayersMapWithImageInfo(i.Runtime.ImageRuntime()) + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + if err := image.BuildImageHierarchyMap(imageInfo, layerInfo, img.TopLayer()); err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + b, err := json.Marshal(imageInfo) + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + return call.ReplyBuildImageHierarchyMap(string(b)) +} |