mirror of
https://github.com/containers/podman.git
synced 2025-07-03 09:17:15 +08:00
Refactor image tree for API usage
Signed-off-by: Sascha Grunert <sgrunert@suse.com>
This commit is contained in:
14
API.md
14
API.md
@ -95,6 +95,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
|
|||||||
|
|
||||||
[func ImageSave(options: ImageSaveOptions) MoreResponse](#ImageSave)
|
[func ImageSave(options: ImageSaveOptions) MoreResponse](#ImageSave)
|
||||||
|
|
||||||
|
[func ImageTree(name: string, whatRequires: bool) string](#ImageTree)
|
||||||
|
|
||||||
[func ImagesPrune(all: bool, filter: []string) []string](#ImagesPrune)
|
[func ImagesPrune(all: bool, filter: []string) []string](#ImagesPrune)
|
||||||
|
|
||||||
[func ImportImage(source: string, reference: string, message: string, changes: []string, delete: bool) string](#ImportImage)
|
[func ImportImage(source: string, reference: string, message: string, changes: []string, delete: bool) string](#ImportImage)
|
||||||
@ -775,6 +777,18 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.ImageExists '{"name": "im
|
|||||||
|
|
||||||
method ImageSave(options: [ImageSaveOptions](#ImageSaveOptions)) [MoreResponse](#MoreResponse)</div>
|
method ImageSave(options: [ImageSaveOptions](#ImageSaveOptions)) [MoreResponse](#MoreResponse)</div>
|
||||||
ImageSave allows you to save an image from the local image storage to a tarball
|
ImageSave allows you to save an image from the local image storage to a tarball
|
||||||
|
### <a name="ImageTree"></a>func ImageTree
|
||||||
|
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||||
|
|
||||||
|
method ImageTree(name: [string](https://godoc.org/builtin#string), whatRequires: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div>
|
||||||
|
ImageTree returns the image tree for the provided image name or ID
|
||||||
|
#### Example
|
||||||
|
~~~
|
||||||
|
$ varlink call -m unix:/run/podman/io.podman/io.podman.ImageTree '{"name": "alpine"}'
|
||||||
|
{
|
||||||
|
"tree": "Image ID: e7d92cdc71fe\nTags: [docker.io/library/alpine:latest]\nSize: 5.861MB\nImage Layers\n└── ID: 5216338b40a7 Size: 5.857MB Top Layer of: [docker.io/library/alpine:latest]\n"
|
||||||
|
}
|
||||||
|
~~~
|
||||||
### <a name="ImagesPrune"></a>func ImagesPrune
|
### <a name="ImagesPrune"></a>func ImagesPrune
|
||||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||||
|
|
||||||
|
@ -1,23 +1,14 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/containers/libpod/cmd/podman/cliconfig"
|
"github.com/containers/libpod/cmd/podman/cliconfig"
|
||||||
"github.com/containers/libpod/libpod/image"
|
|
||||||
"github.com/containers/libpod/pkg/adapter"
|
"github.com/containers/libpod/pkg/adapter"
|
||||||
"github.com/docker/go-units"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
middleItem = "├── "
|
|
||||||
continueItem = "│ "
|
|
||||||
lastItem = "└── "
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
treeCommand cliconfig.TreeValues
|
treeCommand cliconfig.TreeValues
|
||||||
|
|
||||||
@ -56,95 +47,11 @@ func treeCmd(c *cliconfig.TreeValues) error {
|
|||||||
return errors.Wrapf(err, "error creating libpod runtime")
|
return errors.Wrapf(err, "error creating libpod runtime")
|
||||||
}
|
}
|
||||||
defer runtime.DeferredShutdown(false)
|
defer runtime.DeferredShutdown(false)
|
||||||
imageInfo, layerInfoMap, img, err := runtime.Tree(c.InputArgs[0])
|
|
||||||
|
tree, err := runtime.ImageTree(c.InputArgs[0], c.WhatRequires)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return printTree(imageInfo, layerInfoMap, img, c.WhatRequires)
|
fmt.Print(tree)
|
||||||
}
|
|
||||||
|
|
||||||
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 err
|
|
||||||
}
|
|
||||||
|
|
||||||
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))
|
|
||||||
if img.TopLayer() != "" {
|
|
||||||
fmt.Printf("Image Layers\n")
|
|
||||||
} else {
|
|
||||||
fmt.Printf("No Image Layers\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !whatRequires {
|
|
||||||
// fill imageInfo with layers associated with image.
|
|
||||||
// the layers will be filled such that
|
|
||||||
// (Start)RootLayer->...intermediate Parent Layer(s)-> TopLayer(End)
|
|
||||||
// Build output from imageInfo into buffer
|
|
||||||
printImageHierarchy(imageInfo)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// fill imageInfo with layers associated with image.
|
|
||||||
// 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)
|
|
||||||
return printImageChildren(layerInfoMap, img.TopLayer(), "", true)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stores all children layers which are created using given Image.
|
|
||||||
// Layers are stored as follows
|
|
||||||
// (Start)TopLayer->...intermediate Child Layer(s)-> Child TopLayer(End)
|
|
||||||
// (Forks)... intermediate Child Layer(s) -> Child Top Layer(End)
|
|
||||||
func printImageChildren(layerMap map[string]*image.LayerInfo, layerID string, prefix string, last bool) error {
|
|
||||||
if layerID == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ll, ok := layerMap[layerID]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("lookup error: layerid %s, not found", layerID)
|
|
||||||
}
|
|
||||||
fmt.Print(prefix)
|
|
||||||
|
|
||||||
//initialize intend with middleItem to reduce middleItem checks.
|
|
||||||
intend := middleItem
|
|
||||||
if !last {
|
|
||||||
// add continueItem i.e. '|' for next iteration prefix
|
|
||||||
prefix += continueItem
|
|
||||||
} else if len(ll.ChildID) > 1 || len(ll.ChildID) == 0 {
|
|
||||||
// The above condition ensure, alignment happens for node, which has more then 1 children.
|
|
||||||
// If node is last in printing hierarchy, it should not be printed as middleItem i.e. ├──
|
|
||||||
intend = lastItem
|
|
||||||
prefix += " "
|
|
||||||
}
|
|
||||||
|
|
||||||
var tags string
|
|
||||||
if len(ll.RepoTags) > 0 {
|
|
||||||
tags = fmt.Sprintf(" Top Layer of: %s", ll.RepoTags)
|
|
||||||
}
|
|
||||||
fmt.Printf("%sID: %s Size: %7v%s\n", intend, ll.ID[:12], units.HumanSizeWithPrecision(float64(ll.Size), 4), tags)
|
|
||||||
for count, childID := range ll.ChildID {
|
|
||||||
if err := printImageChildren(layerMap, childID, prefix, count == len(ll.ChildID)-1); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// prints the layers info of image
|
|
||||||
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 {
|
|
||||||
intend = lastItem
|
|
||||||
}
|
|
||||||
fmt.Printf("%s ID: %s Size: %7v%s\n", intend, l.ID[:12], units.HumanSizeWithPrecision(float64(l.Size), 4), tags)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1188,6 +1188,16 @@ method GetPodsByStatus(statuses: []string) -> (pods: []string)
|
|||||||
# ~~~
|
# ~~~
|
||||||
method ImageExists(name: string) -> (exists: int)
|
method ImageExists(name: string) -> (exists: int)
|
||||||
|
|
||||||
|
# ImageTree returns the image tree for the provided image name or ID
|
||||||
|
# #### Example
|
||||||
|
# ~~~
|
||||||
|
# $ varlink call -m unix:/run/podman/io.podman/io.podman.ImageTree '{"name": "alpine"}'
|
||||||
|
# {
|
||||||
|
# "tree": "Image ID: e7d92cdc71fe\nTags: [docker.io/library/alpine:latest]\nSize: 5.861MB\nImage Layers\n└── ID: 5216338b40a7 Size: 5.857MB Top Layer of: [docker.io/library/alpine:latest]\n"
|
||||||
|
# }
|
||||||
|
# ~~~
|
||||||
|
method ImageTree(name: string, whatRequires: bool) -> (tree: string)
|
||||||
|
|
||||||
# ContainerExists takes a full or partial container ID or name and returns an int as to
|
# ContainerExists takes a full or partial container ID or name and returns an int as to
|
||||||
# whether the container exists in local storage. A result of 0 means the container does
|
# whether the container exists in local storage. A result of 0 means the container does
|
||||||
# exists; whereas a result of 1 means it could not be found.
|
# exists; whereas a result of 1 means it could not be found.
|
||||||
|
138
libpod/image/tree.go
Normal file
138
libpod/image/tree.go
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
package image
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/go-units"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
middleItem = "├── "
|
||||||
|
continueItem = "│ "
|
||||||
|
lastItem = "└── "
|
||||||
|
)
|
||||||
|
|
||||||
|
type tree struct {
|
||||||
|
img *Image
|
||||||
|
imageInfo *InfoImage
|
||||||
|
layerInfo map[string]*LayerInfo
|
||||||
|
sb *strings.Builder
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateTree creates an image tree string representation for displaying it
|
||||||
|
// to the user.
|
||||||
|
func (i *Image) GenerateTree(whatRequires bool) (string, error) {
|
||||||
|
// Fetch map of image-layers, which is used for printing output.
|
||||||
|
layerInfo, err := GetLayersMapWithImageInfo(i.imageruntime)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "error while retrieving layers of image %q", i.InputName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an imageInfo and fill the image and layer info
|
||||||
|
imageInfo := &InfoImage{
|
||||||
|
ID: i.ID(),
|
||||||
|
Tags: i.Names(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := BuildImageHierarchyMap(imageInfo, layerInfo, i.TopLayer()); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
sb := &strings.Builder{}
|
||||||
|
tree := &tree{i, imageInfo, layerInfo, sb}
|
||||||
|
if err := tree.print(whatRequires); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return tree.string(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tree) string() string {
|
||||||
|
return t.sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tree) print(whatRequires bool) error {
|
||||||
|
size, err := t.img.Size(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(t.sb, "Image ID: %s\n", t.imageInfo.ID[:12])
|
||||||
|
fmt.Fprintf(t.sb, "Tags: %s\n", t.imageInfo.Tags)
|
||||||
|
fmt.Fprintf(t.sb, "Size: %v\n", units.HumanSizeWithPrecision(float64(*size), 4))
|
||||||
|
if t.img.TopLayer() != "" {
|
||||||
|
fmt.Fprintf(t.sb, "Image Layers\n")
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(t.sb, "No Image Layers\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !whatRequires {
|
||||||
|
// fill imageInfo with layers associated with image.
|
||||||
|
// the layers will be filled such that
|
||||||
|
// (Start)RootLayer->...intermediate Parent Layer(s)-> TopLayer(End)
|
||||||
|
// Build output from imageInfo into buffer
|
||||||
|
t.printImageHierarchy(t.imageInfo)
|
||||||
|
} else {
|
||||||
|
// fill imageInfo with layers associated with image.
|
||||||
|
// 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)
|
||||||
|
return t.printImageChildren(t.layerInfo, t.img.TopLayer(), "", true)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stores all children layers which are created using given Image.
|
||||||
|
// Layers are stored as follows
|
||||||
|
// (Start)TopLayer->...intermediate Child Layer(s)-> Child TopLayer(End)
|
||||||
|
// (Forks)... intermediate Child Layer(s) -> Child Top Layer(End)
|
||||||
|
func (t *tree) printImageChildren(layerMap map[string]*LayerInfo, layerID string, prefix string, last bool) error {
|
||||||
|
if layerID == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ll, ok := layerMap[layerID]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("lookup error: layerid %s, not found", layerID)
|
||||||
|
}
|
||||||
|
fmt.Fprint(t.sb, prefix)
|
||||||
|
|
||||||
|
//initialize intend with middleItem to reduce middleItem checks.
|
||||||
|
intend := middleItem
|
||||||
|
if !last {
|
||||||
|
// add continueItem i.e. '|' for next iteration prefix
|
||||||
|
prefix += continueItem
|
||||||
|
} else if len(ll.ChildID) > 1 || len(ll.ChildID) == 0 {
|
||||||
|
// The above condition ensure, alignment happens for node, which has more then 1 children.
|
||||||
|
// If node is last in printing hierarchy, it should not be printed as middleItem i.e. ├──
|
||||||
|
intend = lastItem
|
||||||
|
prefix += " "
|
||||||
|
}
|
||||||
|
|
||||||
|
var tags string
|
||||||
|
if len(ll.RepoTags) > 0 {
|
||||||
|
tags = fmt.Sprintf(" Top Layer of: %s", ll.RepoTags)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(t.sb, "%sID: %s Size: %7v%s\n", intend, ll.ID[:12], units.HumanSizeWithPrecision(float64(ll.Size), 4), tags)
|
||||||
|
for count, childID := range ll.ChildID {
|
||||||
|
if err := t.printImageChildren(layerMap, childID, prefix, count == len(ll.ChildID)-1); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// prints the layers info of image
|
||||||
|
func (t *tree) printImageHierarchy(imageInfo *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 {
|
||||||
|
intend = lastItem
|
||||||
|
}
|
||||||
|
fmt.Fprintf(t.sb, "%s ID: %s Size: %7v%s\n", intend, l.ID[:12], units.HumanSizeWithPrecision(float64(l.Size), 4), tags)
|
||||||
|
}
|
||||||
|
}
|
@ -1,33 +0,0 @@
|
|||||||
// +build !remoteclient
|
|
||||||
|
|
||||||
package adapter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/containers/libpod/libpod/image"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Tree ...
|
|
||||||
func (r *LocalRuntime) Tree(imageOrID string) (*image.InfoImage, map[string]*image.LayerInfo, *ContainerImage, error) {
|
|
||||||
img, err := r.NewImageFromLocal(imageOrID)
|
|
||||||
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
|
|
||||||
}
|
|
@ -7,9 +7,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
iopodman "github.com/containers/libpod/cmd/podman/varlink"
|
iopodman "github.com/containers/libpod/cmd/podman/varlink"
|
||||||
"github.com/containers/libpod/libpod/image"
|
|
||||||
"github.com/containers/libpod/pkg/inspect"
|
"github.com/containers/libpod/pkg/inspect"
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Inspect returns returns an ImageData struct from over a varlink connection
|
// Inspect returns returns an ImageData struct from over a varlink connection
|
||||||
@ -24,32 +22,3 @@ func (i *ContainerImage) Inspect(ctx context.Context) (*inspect.ImageData, error
|
|||||||
}
|
}
|
||||||
return &data, nil
|
return &data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tree ...
|
|
||||||
func (r *LocalRuntime) Tree(imageOrID string) (*image.InfoImage, map[string]*image.LayerInfo, *ContainerImage, error) {
|
|
||||||
layerInfoMap := make(map[string]*image.LayerInfo)
|
|
||||||
imageInfo := &image.InfoImage{}
|
|
||||||
|
|
||||||
img, err := r.NewImageFromLocal(imageOrID)
|
|
||||||
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, imageOrID)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
@ -133,6 +133,15 @@ func (r *LocalRuntime) NewImageFromLocal(name string) (*ContainerImage, error) {
|
|||||||
return &ContainerImage{img}, nil
|
return &ContainerImage{img}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImageTree reutnrs an new image.Tree for the provided `imageOrID` and `whatrequires` flag
|
||||||
|
func (r *LocalRuntime) ImageTree(imageOrID string, whatRequires bool) (string, error) {
|
||||||
|
img, err := r.Runtime.ImageRuntime().NewFromLocal(imageOrID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return img.GenerateTree(whatRequires)
|
||||||
|
}
|
||||||
|
|
||||||
// LoadFromArchiveReference calls into local storage to load an image from an archive
|
// LoadFromArchiveReference calls into local storage to load an image from an archive
|
||||||
func (r *LocalRuntime) LoadFromArchiveReference(ctx context.Context, srcRef types.ImageReference, signaturePolicyPath string, writer io.Writer) ([]*ContainerImage, error) {
|
func (r *LocalRuntime) LoadFromArchiveReference(ctx context.Context, srcRef types.ImageReference, signaturePolicyPath string, writer io.Writer) ([]*ContainerImage, error) {
|
||||||
var containerImages []*ContainerImage
|
var containerImages []*ContainerImage
|
||||||
|
@ -344,6 +344,10 @@ func (r *LocalRuntime) New(ctx context.Context, name, signaturePolicyPath, authf
|
|||||||
return newImage, nil
|
return newImage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *LocalRuntime) ImageTree(imageOrID string, whatRequires bool) (string, error) {
|
||||||
|
return iopodman.ImageTree().Call(r.Conn, imageOrID, whatRequires)
|
||||||
|
}
|
||||||
|
|
||||||
// IsParent goes through the layers in the store and checks if i.TopLayer is
|
// IsParent goes through the layers in the store and checks if i.TopLayer is
|
||||||
// the parent of any other layer in store. Double check that image with that
|
// the parent of any other layer in store. Double check that image with that
|
||||||
// layer exists as well.
|
// layer exists as well.
|
||||||
|
@ -46,17 +46,34 @@ func ImageExists(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ImageTree(w http.ResponseWriter, r *http.Request) {
|
func ImageTree(w http.ResponseWriter, r *http.Request) {
|
||||||
// tree is a bit of a mess ... logic is in adapter and therefore not callable from here. needs rework
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||||
|
name := utils.GetName(r)
|
||||||
|
|
||||||
// name := utils.GetName(r)
|
img, err := runtime.ImageRuntime().NewFromLocal(name)
|
||||||
// _, layerInfoMap, _, err := s.Runtime.Tree(name)
|
if err != nil {
|
||||||
// if err != nil {
|
utils.Error(w, "Something went wrong.", http.StatusNotFound, errors.Wrapf(err, "Failed to find image %s", name))
|
||||||
// Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "Failed to find image information for %q", name))
|
return
|
||||||
// return
|
}
|
||||||
// }
|
|
||||||
// it is not clear to me how to deal with this given all the processing of the image
|
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||||
// is in main. we need to discuss how that really should be and return something useful.
|
query := struct {
|
||||||
handlers.UnsupportedHandler(w, r)
|
WhatRequires bool `schema:"whatrequires"`
|
||||||
|
}{
|
||||||
|
WhatRequires: false,
|
||||||
|
}
|
||||||
|
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
||||||
|
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
|
||||||
|
errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tree, err := img.GenerateTree(query.WhatRequires)
|
||||||
|
if err != nil {
|
||||||
|
utils.Error(w, "Server error", http.StatusInternalServerError, errors.Wrapf(err, "failed to generate image tree for %s", name))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.WriteResponse(w, http.StatusOK, tree)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetImage(w http.ResponseWriter, r *http.Request) {
|
func GetImage(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -72,8 +89,8 @@ func GetImage(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
utils.WriteResponse(w, http.StatusOK, inspect)
|
utils.WriteResponse(w, http.StatusOK, inspect)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetImages(w http.ResponseWriter, r *http.Request) {
|
func GetImages(w http.ResponseWriter, r *http.Request) {
|
||||||
images, err := utils.GetImages(w, r)
|
images, err := utils.GetImages(w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -136,3 +136,12 @@ type swagInspectVolumeResponse struct {
|
|||||||
libpod.InspectVolumeData
|
libpod.InspectVolumeData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Image tree response
|
||||||
|
// swagger:response LibpodImageTreeResponse
|
||||||
|
type swagImageTreeResponse struct {
|
||||||
|
// in:body
|
||||||
|
Body struct {
|
||||||
|
ImageTreeResponse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -578,6 +578,31 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
|
|||||||
// 500:
|
// 500:
|
||||||
// $ref: '#/responses/InternalError'
|
// $ref: '#/responses/InternalError'
|
||||||
r.Handle(VersionedPath("/libpod/images/{name}/exists"), APIHandler(s.Context, libpod.ImageExists))
|
r.Handle(VersionedPath("/libpod/images/{name}/exists"), APIHandler(s.Context, libpod.ImageExists))
|
||||||
|
// swagger:operation POST /libpod/images/{name}/tree libpod libpodImageTree
|
||||||
|
// ---
|
||||||
|
// tags:
|
||||||
|
// - images
|
||||||
|
// summary: Image tree
|
||||||
|
// description: Retrieve the image tree for the provided image name or ID
|
||||||
|
// parameters:
|
||||||
|
// - in: path
|
||||||
|
// name: name
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// description: the name or ID of the container
|
||||||
|
// - in: query
|
||||||
|
// name: whatrequires
|
||||||
|
// type: boolean
|
||||||
|
// description: show all child images and layers of the specified image
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// responses:
|
||||||
|
// 200:
|
||||||
|
// $ref: '#/responses/LibpodImageTreeResponse'
|
||||||
|
// 401:
|
||||||
|
// $ref: '#/responses/NoSuchImage'
|
||||||
|
// 500:
|
||||||
|
// $ref: '#/responses/InternalError'
|
||||||
r.Handle(VersionedPath("/libpod/images/{name}/tree"), APIHandler(s.Context, libpod.ImageTree))
|
r.Handle(VersionedPath("/libpod/images/{name}/tree"), APIHandler(s.Context, libpod.ImageTree))
|
||||||
// swagger:operation GET /libpod/images/{name}/history libpod libpodImageHistory
|
// swagger:operation GET /libpod/images/{name}/history libpod libpodImageHistory
|
||||||
// ---
|
// ---
|
||||||
|
@ -1016,3 +1016,17 @@ func (i *LibpodAPI) BuildImageHierarchyMap(call iopodman.VarlinkCall, name strin
|
|||||||
}
|
}
|
||||||
return call.ReplyBuildImageHierarchyMap(string(b))
|
return call.ReplyBuildImageHierarchyMap(string(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImageTree returns the image tree string for the provided image name or ID
|
||||||
|
func (i *LibpodAPI) ImageTree(call iopodman.VarlinkCall, nameOrID string, whatRequires bool) error {
|
||||||
|
img, err := i.Runtime.ImageRuntime().NewFromLocal(nameOrID)
|
||||||
|
if err != nil {
|
||||||
|
return call.ReplyErrorOccurred(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
tree, err := img.GenerateTree(whatRequires)
|
||||||
|
if err != nil {
|
||||||
|
return call.ReplyErrorOccurred(err.Error())
|
||||||
|
}
|
||||||
|
return call.ReplyImageTree(tree)
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user