package daemon import ( "fmt" "runtime" "time" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types/image" "github.com/docker/docker/layer" ) // ImageHistory returns a slice of ImageHistory structures for the specified image // name by walking the image lineage. func (daemon *Daemon) ImageHistory(name string) ([]*image.HistoryResponseItem, error) { start := time.Now() img, err := daemon.GetImage(name) if err != nil { return nil, err } // If the image OS isn't set, assume it's the host OS platform := img.OS if platform == "" { platform = runtime.GOOS } history := []*image.HistoryResponseItem{} layerCounter := 0 rootFS := *img.RootFS rootFS.DiffIDs = nil for _, h := range img.History { var layerSize int64 if !h.EmptyLayer { if len(img.RootFS.DiffIDs) <= layerCounter { return nil, fmt.Errorf("too many non-empty layers in History section") } rootFS.Append(img.RootFS.DiffIDs[layerCounter]) l, err := daemon.stores[platform].layerStore.Get(rootFS.ChainID()) if err != nil { return nil, err } layerSize, err = l.DiffSize() layer.ReleaseAndLog(daemon.stores[platform].layerStore, l) if err != nil { return nil, err } layerCounter++ } history = append([]*image.HistoryResponseItem{{ ID: "", Created: h.Created.Unix(), CreatedBy: h.CreatedBy, Comment: h.Comment, Size: layerSize, }}, history...) } // Fill in image IDs and tags histImg := img id := img.ID() for _, h := range history { h.ID = id.String() var tags []string for _, r := range daemon.stores[platform].referenceStore.References(id.Digest()) { if _, ok := r.(reference.NamedTagged); ok { tags = append(tags, reference.FamiliarString(r)) } } h.Tags = tags id = histImg.Parent if id == "" { break } histImg, err = daemon.GetImage(id.String()) if err != nil { break } } imageActions.WithValues("history").UpdateSince(start) return history, nil }