Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion API.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;">

Expand Down Expand Up @@ -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;">

Expand Down Expand Up @@ -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;">

Expand Down Expand Up @@ -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.
Expand Down
77 changes: 16 additions & 61 deletions cmd/podman/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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 {
Expand All @@ -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)

Expand All @@ -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
}

Expand Down Expand Up @@ -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)
Expand Down
10 changes: 9 additions & 1 deletion cmd/podman/varlink/io.podman.varlink
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)

Expand Down
28 changes: 28 additions & 0 deletions libpod/image/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand Down Expand Up @@ -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
}
34 changes: 34 additions & 0 deletions pkg/adapter/images.go
Original file line number Diff line number Diff line change
@@ -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
}
32 changes: 32 additions & 0 deletions pkg/adapter/images_remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
7 changes: 7 additions & 0 deletions pkg/adapter/runtime_remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ type remoteImage struct {
Digest digest.Digest
isParent bool
Runtime *LocalRuntime
TopLayer string
}

// Container ...
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
Expand Down
Loading