summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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)
}