summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorumohnani8 <umohnani@redhat.com>2018-06-21 16:11:59 -0400
committerAtomic Bot <atomic-devel@projectatomic.io>2018-06-22 15:19:34 +0000
commit088d5af87984e8e8348f3b70004d8107df645f73 (patch)
tree6a2400e1660620f8f6291c8aa7822a29543bbc12
parent89af35175d97cf90e7336d3c817612fafc68dbdb (diff)
downloadpodman-088d5af87984e8e8348f3b70004d8107df645f73.tar.gz
podman-088d5af87984e8e8348f3b70004d8107df645f73.tar.bz2
podman-088d5af87984e8e8348f3b70004d8107df645f73.zip
Podman history now prints out intermediate image IDs
If the intermediate image exists in the store, podman history will show the IDs of the intermediate image of each layer. Signed-off-by: umohnani8 <umohnani@redhat.com> Closes: #982 Approved by: mheon
-rw-r--r--cmd/podman/history.go81
-rw-r--r--libpod/image/image.go78
-rw-r--r--pkg/varlinkapi/images.go26
3 files changed, 101 insertions, 84 deletions
diff --git a/cmd/podman/history.go b/cmd/podman/history.go
index 4f3867d34..2570dcc7d 100644
--- a/cmd/podman/history.go
+++ b/cmd/podman/history.go
@@ -6,12 +6,11 @@ import (
"strings"
"time"
- "github.com/containers/image/types"
units "github.com/docker/go-units"
- "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/projectatomic/libpod/cmd/podman/formats"
"github.com/projectatomic/libpod/cmd/podman/libpodruntime"
+ "github.com/projectatomic/libpod/libpod/image"
"github.com/urfave/cli"
)
@@ -26,18 +25,6 @@ type historyTemplateParams struct {
Comment string
}
-// historyJSONParams is only used when the JSON format is specified,
-// and is better for data processing from JSON.
-// historyJSONParams will be populated by data from v1.History and types.BlobInfo,
-// the members of the struct are the sama data types as their sources.
-type historyJSONParams struct {
- ID string `json:"id"`
- Created *time.Time `json:"created"`
- CreatedBy string `json:"createdBy"`
- Size int64 `json:"size"`
- Comment string `json:"comment"`
-}
-
// historyOptions stores cli flag values
type historyOptions struct {
human bool
@@ -111,12 +98,12 @@ func historyCmd(c *cli.Context) error {
format: format,
}
- history, layers, err := image.History(getContext())
+ history, err := image.History(getContext())
if err != nil {
return errors.Wrapf(err, "error getting history of image %q", image.InputName)
}
- return generateHistoryOutput(history, layers, image.ID(), opts)
+ return generateHistoryOutput(history, opts)
}
func genHistoryFormat(format string, quiet bool) string {
@@ -132,7 +119,7 @@ func genHistoryFormat(format string, quiet bool) string {
}
// historyToGeneric makes an empty array of interfaces for output
-func historyToGeneric(templParams []historyTemplateParams, JSONParams []historyJSONParams) (genericParams []interface{}) {
+func historyToGeneric(templParams []historyTemplateParams, JSONParams []*image.History) (genericParams []interface{}) {
if len(templParams) > 0 {
for _, v := range templParams {
genericParams = append(genericParams, interface{}(v))
@@ -158,36 +145,27 @@ func (h *historyTemplateParams) headerMap() map[string]string {
}
// getHistorytemplateOutput gets the modified history information to be printed in human readable format
-func getHistoryTemplateOutput(history []v1.History, layers []types.BlobInfo, imageID string, opts historyOptions) (historyOutput []historyTemplateParams) {
+func getHistoryTemplateOutput(history []*image.History, opts historyOptions) (historyOutput []historyTemplateParams) {
var (
outputSize string
createdTime string
createdBy string
- count = 1
)
- for i := len(history) - 1; i >= 0; i-- {
- if i != len(history)-1 {
- imageID = "<missing>"
- }
- if !opts.noTrunc && i == len(history)-1 {
+ for _, hist := range history {
+ imageID := hist.ID
+ if !opts.noTrunc && imageID != "<missing>" {
imageID = shortID(imageID)
}
- var size int64
- if !history[i].EmptyLayer {
- size = layers[len(layers)-count].Size
- count++
- }
-
if opts.human {
- createdTime = units.HumanDuration(time.Since((*history[i].Created))) + " ago"
- outputSize = units.HumanSize(float64(size))
+ createdTime = units.HumanDuration(time.Since((*hist.Created))) + " ago"
+ outputSize = units.HumanSize(float64(hist.Size))
} else {
- createdTime = (history[i].Created).Format(time.RFC3339)
- outputSize = strconv.FormatInt(size, 10)
+ createdTime = (hist.Created).Format(time.RFC3339)
+ outputSize = strconv.FormatInt(hist.Size, 10)
}
- createdBy = strings.Join(strings.Fields(history[i].CreatedBy), " ")
+ createdBy = strings.Join(strings.Fields(hist.CreatedBy), " ")
if !opts.noTrunc && len(createdBy) > createdByTruncLength {
createdBy = createdBy[:createdByTruncLength-3] + "..."
}
@@ -197,29 +175,7 @@ func getHistoryTemplateOutput(history []v1.History, layers []types.BlobInfo, ima
Created: createdTime,
CreatedBy: createdBy,
Size: outputSize,
- Comment: history[i].Comment,
- }
- historyOutput = append(historyOutput, params)
- }
- return
-}
-
-// getHistoryJSONOutput returns the history information in its raw form
-func getHistoryJSONOutput(history []v1.History, layers []types.BlobInfo, imageID string) (historyOutput []historyJSONParams) {
- count := 1
- for i := len(history) - 1; i >= 0; i-- {
- var size int64
- if !history[i].EmptyLayer {
- size = layers[len(layers)-count].Size
- count++
- }
-
- params := historyJSONParams{
- ID: imageID,
- Created: history[i].Created,
- CreatedBy: history[i].CreatedBy,
- Size: size,
- Comment: history[i].Comment,
+ Comment: hist.Comment,
}
historyOutput = append(historyOutput, params)
}
@@ -227,7 +183,7 @@ func getHistoryJSONOutput(history []v1.History, layers []types.BlobInfo, imageID
}
// generateHistoryOutput generates the history based on the format given
-func generateHistoryOutput(history []v1.History, layers []types.BlobInfo, imageID string, opts historyOptions) error {
+func generateHistoryOutput(history []*image.History, opts historyOptions) error {
if len(history) == 0 {
return nil
}
@@ -236,11 +192,10 @@ func generateHistoryOutput(history []v1.History, layers []types.BlobInfo, imageI
switch opts.format {
case formats.JSONString:
- historyOutput := getHistoryJSONOutput(history, layers, imageID)
- out = formats.JSONStructArray{Output: historyToGeneric([]historyTemplateParams{}, historyOutput)}
+ out = formats.JSONStructArray{Output: historyToGeneric([]historyTemplateParams{}, history)}
default:
- historyOutput := getHistoryTemplateOutput(history, layers, imageID, opts)
- out = formats.StdoutTemplateArray{Output: historyToGeneric(historyOutput, []historyJSONParams{}), Template: opts.format, Fields: historyOutput[0].headerMap()}
+ historyOutput := getHistoryTemplateOutput(history, opts)
+ out = formats.StdoutTemplateArray{Output: historyToGeneric(historyOutput, []*image.History{}), Template: opts.format, Fields: historyOutput[0].headerMap()}
}
return formats.Writer(out).Out()
diff --git a/libpod/image/image.go b/libpod/image/image.go
index c8a929074..57eabe2c8 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -608,17 +608,87 @@ func (i *Image) Layer() (*storage.Layer, error) {
return i.imageruntime.store.Layer(i.image.TopLayer)
}
+// History contains the history information of an image
+type History struct {
+ ID string `json:"id"`
+ Created *time.Time `json:"created"`
+ CreatedBy string `json:"createdBy"`
+ Size int64 `json:"size"`
+ Comment string `json:"comment"`
+}
+
// History gets the history of an image and information about its layers
-func (i *Image) History(ctx context.Context) ([]ociv1.History, []types.BlobInfo, error) {
+func (i *Image) History(ctx context.Context) ([]*History, error) {
img, err := i.toImageRef(ctx)
if err != nil {
- return nil, nil, err
+ return nil, err
}
oci, err := img.OCIConfig(ctx)
if err != nil {
- return nil, nil, err
+ return nil, err
+ }
+
+ // Get the IDs of the images making up the history layers
+ // if the images exist locally in the store
+ images, err := i.imageruntime.GetImages()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting images from store")
+ }
+ imageIDs := []string{i.ID()}
+ if err := i.historyLayerIDs(i.TopLayer(), images, &imageIDs); err != nil {
+ return nil, errors.Wrap(err, "error getting image IDs for layers in history")
}
- return oci.History, img.LayerInfos(), nil
+
+ var (
+ imageID string
+ imgIDCount = 0
+ size int64
+ sizeCount = 1
+ allHistory []*History
+ )
+
+ for i := len(oci.History) - 1; i >= 0; i-- {
+ if imgIDCount < len(imageIDs) {
+ imageID = imageIDs[imgIDCount]
+ imgIDCount++
+ } else {
+ imageID = "<missing>"
+ }
+ if !oci.History[i].EmptyLayer {
+ size = img.LayerInfos()[len(img.LayerInfos())-sizeCount].Size
+ sizeCount++
+ }
+ allHistory = append(allHistory, &History{
+ ID: imageID,
+ Created: oci.History[i].Created,
+ CreatedBy: oci.History[i].CreatedBy,
+ Size: size,
+ Comment: oci.History[i].Comment,
+ })
+ }
+
+ return allHistory, nil
+}
+
+// historyLayerIDs goes through the images in store and checks if the top layer of an image
+// is the same as the parent of topLayerID
+func (i *Image) historyLayerIDs(topLayerID string, images []*Image, IDs *[]string) error {
+ for _, image := range images {
+ // Get the layer info of topLayerID
+ layer, err := i.imageruntime.store.Layer(topLayerID)
+ if err != nil {
+ return errors.Wrapf(err, "error getting layer info %q", topLayerID)
+ }
+ // Check if the parent of layer is equal to the image's top layer
+ // If so add the image ID to the list of IDs and find the parent of
+ // the top layer of the image ID added to the list
+ // Since we are checking for parent, each top layer can only have one parent
+ if layer.Parent == image.TopLayer() {
+ *IDs = append(*IDs, image.ID())
+ return i.historyLayerIDs(image.TopLayer(), images, IDs)
+ }
+ }
+ return nil
}
// Dangling returns a bool if the image is "dangling"
diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go
index 420962973..8e2669660 100644
--- a/pkg/varlinkapi/images.go
+++ b/pkg/varlinkapi/images.go
@@ -1,6 +1,7 @@
package varlinkapi
import (
+ "bytes"
"encoding/json"
"fmt"
"io"
@@ -8,7 +9,6 @@ import (
"strings"
"time"
- "bytes"
"github.com/containers/image/docker"
"github.com/containers/image/types"
"github.com/docker/go-units"
@@ -307,27 +307,19 @@ func (i *LibpodAPI) HistoryImage(call ioprojectatomicpodman.VarlinkCall, name st
if err != nil {
return call.ReplyImageNotFound(name)
}
- history, layerInfos, err := newImage.History(getContext())
+ history, err := newImage.History(getContext())
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
- var (
- histories []ioprojectatomicpodman.ImageHistory
- count = 1
- )
- for i := len(history) - 1; i >= 0; i-- {
- var size int64
- if !history[i].EmptyLayer {
- size = layerInfos[len(layerInfos)-count].Size
- count++
- }
+ var histories []ioprojectatomicpodman.ImageHistory
+ for _, hist := range history {
imageHistory := ioprojectatomicpodman.ImageHistory{
- Id: newImage.ID(),
- Created: history[i].Created.String(),
- CreatedBy: history[i].CreatedBy,
+ Id: hist.ID,
+ Created: hist.Created.String(),
+ CreatedBy: hist.CreatedBy,
Tags: newImage.Names(),
- Size: size,
- Comment: history[i].Comment,
+ Size: hist.Size,
+ Comment: hist.Comment,
}
histories = append(histories, imageHistory)
}