浏览代码

Fixing Image struct to no longer use Graph.

Signed-off-by:  John Howard <jhoward@microsoft.com>
John Howard 10 年之前
父节点
当前提交
9001ea26e7

+ 3 - 2
builder/internals.go

@@ -24,6 +24,7 @@ import (
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/daemon"
 	"github.com/docker/docker/daemon"
 	"github.com/docker/docker/graph"
 	"github.com/docker/docker/graph"
+	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/chrootarchive"
 	"github.com/docker/docker/pkg/chrootarchive"
 	"github.com/docker/docker/pkg/httputils"
 	"github.com/docker/docker/pkg/httputils"
@@ -486,7 +487,7 @@ func ContainsWildcards(name string) bool {
 	return false
 	return false
 }
 }
 
 
-func (b *Builder) pullImage(name string) (*graph.Image, error) {
+func (b *Builder) pullImage(name string) (*image.Image, error) {
 	remote, tag := parsers.ParseRepositoryTag(name)
 	remote, tag := parsers.ParseRepositoryTag(name)
 	if tag == "" {
 	if tag == "" {
 		tag = "latest"
 		tag = "latest"
@@ -524,7 +525,7 @@ func (b *Builder) pullImage(name string) (*graph.Image, error) {
 	return image, nil
 	return image, nil
 }
 }
 
 
-func (b *Builder) processImageFrom(img *graph.Image) error {
+func (b *Builder) processImageFrom(img *image.Image) error {
 	b.image = img.ID
 	b.image = img.ID
 
 
 	if img.Config != nil {
 	if img.Config != nil {

+ 2 - 2
daemon/commit.go

@@ -1,7 +1,7 @@
 package daemon
 package daemon
 
 
 import (
 import (
-	"github.com/docker/docker/graph"
+	"github.com/docker/docker/image"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/runconfig"
 )
 )
 
 
@@ -16,7 +16,7 @@ type ContainerCommitConfig struct {
 
 
 // Commit creates a new filesystem image from the current state of a container.
 // Commit creates a new filesystem image from the current state of a container.
 // The image can optionally be tagged into a repository
 // The image can optionally be tagged into a repository
-func (daemon *Daemon) Commit(container *Container, c *ContainerCommitConfig) (*graph.Image, error) {
+func (daemon *Daemon) Commit(container *Container, c *ContainerCommitConfig) (*image.Image, error) {
 	if c.Pause && !container.IsPaused() {
 	if c.Pause && !container.IsPaused() {
 		container.Pause()
 		container.Pause()
 		defer container.Unpause()
 		defer container.Unpause()

+ 2 - 2
daemon/container.go

@@ -20,7 +20,7 @@ import (
 	"github.com/docker/docker/daemon/logger"
 	"github.com/docker/docker/daemon/logger"
 	"github.com/docker/docker/daemon/logger/jsonfilelog"
 	"github.com/docker/docker/daemon/logger/jsonfilelog"
 	"github.com/docker/docker/daemon/network"
 	"github.com/docker/docker/daemon/network"
-	"github.com/docker/docker/graph"
+	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/broadcastwriter"
 	"github.com/docker/docker/pkg/broadcastwriter"
 	"github.com/docker/docker/pkg/fileutils"
 	"github.com/docker/docker/pkg/fileutils"
@@ -584,7 +584,7 @@ func (container *Container) Changes() ([]archive.Change, error) {
 	return container.changes()
 	return container.changes()
 }
 }
 
 
-func (container *Container) GetImage() (*graph.Image, error) {
+func (container *Container) GetImage() (*image.Image, error) {
 	if container.daemon == nil {
 	if container.daemon == nil {
 		return nil, fmt.Errorf("Can't get image of unregistered container")
 		return nil, fmt.Errorf("Can't get image of unregistered container")
 	}
 	}

+ 2 - 2
daemon/container_windows.go

@@ -9,7 +9,7 @@ import (
 
 
 	"github.com/docker/docker/daemon/execdriver"
 	"github.com/docker/docker/daemon/execdriver"
 	"github.com/docker/docker/daemon/graphdriver/windows"
 	"github.com/docker/docker/daemon/graphdriver/windows"
-	"github.com/docker/docker/graph"
+	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/microsoft/hcsshim"
 	"github.com/microsoft/hcsshim"
 )
 )
@@ -114,7 +114,7 @@ func populateCommand(c *Container, env []string) error {
 	// enable VFS to continue operating for development purposes.
 	// enable VFS to continue operating for development purposes.
 	if wd, ok := c.daemon.driver.(*windows.WindowsGraphDriver); ok {
 	if wd, ok := c.daemon.driver.(*windows.WindowsGraphDriver); ok {
 		var err error
 		var err error
-		var img *graph.Image
+		var img *image.Image
 		var ids []string
 		var ids []string
 
 
 		if img, err = c.daemon.graph.Get(c.ImageID); err != nil {
 		if img, err = c.daemon.graph.Get(c.ImageID); err != nil {

+ 2 - 1
daemon/create.go

@@ -8,6 +8,7 @@ import (
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/graph"
 	"github.com/docker/docker/graph"
+	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/runconfig"
@@ -46,7 +47,7 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
 	var (
 	var (
 		container *Container
 		container *Container
 		warnings  []string
 		warnings  []string
-		img       *graph.Image
+		img       *image.Image
 		imgID     string
 		imgID     string
 		err       error
 		err       error
 	)
 	)

+ 4 - 3
daemon/daemon.go

@@ -23,6 +23,7 @@ import (
 	"github.com/docker/docker/daemon/logger"
 	"github.com/docker/docker/daemon/logger"
 	"github.com/docker/docker/daemon/network"
 	"github.com/docker/docker/daemon/network"
 	"github.com/docker/docker/graph"
 	"github.com/docker/docker/graph"
+	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/broadcastwriter"
 	"github.com/docker/docker/pkg/broadcastwriter"
 	"github.com/docker/docker/pkg/fileutils"
 	"github.com/docker/docker/pkg/fileutils"
 	"github.com/docker/docker/pkg/graphdb"
 	"github.com/docker/docker/pkg/graphdb"
@@ -336,7 +337,7 @@ func (daemon *Daemon) restore() error {
 	return nil
 	return nil
 }
 }
 
 
-func (daemon *Daemon) mergeAndVerifyConfig(config *runconfig.Config, img *graph.Image) error {
+func (daemon *Daemon) mergeAndVerifyConfig(config *runconfig.Config, img *image.Image) error {
 	if img != nil && img.Config != nil {
 	if img != nil && img.Config != nil {
 		if err := runconfig.Merge(config, img.Config); err != nil {
 		if err := runconfig.Merge(config, img.Config); err != nil {
 			return err
 			return err
@@ -882,7 +883,7 @@ func (daemon *Daemon) ContainerGraph() *graphdb.Database {
 	return daemon.containerGraph
 	return daemon.containerGraph
 }
 }
 
 
-func (daemon *Daemon) ImageGetCached(imgID string, config *runconfig.Config) (*graph.Image, error) {
+func (daemon *Daemon) ImageGetCached(imgID string, config *runconfig.Config) (*image.Image, error) {
 	// Retrieve all images
 	// Retrieve all images
 	images := daemon.Graph().Map()
 	images := daemon.Graph().Map()
 
 
@@ -896,7 +897,7 @@ func (daemon *Daemon) ImageGetCached(imgID string, config *runconfig.Config) (*g
 	}
 	}
 
 
 	// Loop on the children of the given image and check the config
 	// Loop on the children of the given image and check the config
-	var match *graph.Image
+	var match *image.Image
 	for elem := range imageMap[imgID] {
 	for elem := range imageMap[imgID] {
 		img, ok := images[elem]
 		img, ok := images[elem]
 		if !ok {
 		if !ok {

+ 2 - 1
daemon/image_delete.go

@@ -7,6 +7,7 @@ import (
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/graph"
 	"github.com/docker/docker/graph"
+	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/utils"
 	"github.com/docker/docker/utils"
@@ -159,7 +160,7 @@ func (daemon *Daemon) canDeleteImage(imgID string, force bool) error {
 			return err
 			return err
 		}
 		}
 
 
-		if err := daemon.graph.WalkHistory(parent, func(p graph.Image) error {
+		if err := daemon.graph.WalkHistory(parent, func(p image.Image) error {
 			if imgID == p.ID {
 			if imgID == p.ID {
 				if container.IsRunning() {
 				if container.IsRunning() {
 					if force {
 					if force {

+ 17 - 44
graph/graph.go

@@ -82,22 +82,6 @@ type Graph struct {
 	retained   *retainedLayers
 	retained   *retainedLayers
 }
 }
 
 
-type Image struct {
-	ID              string            `json:"id"`
-	Parent          string            `json:"parent,omitempty"`
-	Comment         string            `json:"comment,omitempty"`
-	Created         time.Time         `json:"created"`
-	Container       string            `json:"container,omitempty"`
-	ContainerConfig runconfig.Config  `json:"container_config,omitempty"`
-	DockerVersion   string            `json:"docker_version,omitempty"`
-	Author          string            `json:"author,omitempty"`
-	Config          *runconfig.Config `json:"config,omitempty"`
-	Architecture    string            `json:"architecture,omitempty"`
-	OS              string            `json:"os,omitempty"`
-	Size            int64
-	graph           Graph
-}
-
 var (
 var (
 	// ErrDigestNotSet is used when request the digest for a layer
 	// ErrDigestNotSet is used when request the digest for a layer
 	// but the layer has no digest value or content to compute the
 	// but the layer has no digest value or content to compute the
@@ -174,7 +158,7 @@ func (graph *Graph) Exists(id string) bool {
 }
 }
 
 
 // Get returns the image with the given id, or an error if the image doesn't exist.
 // Get returns the image with the given id, or an error if the image doesn't exist.
-func (graph *Graph) Get(name string) (*Image, error) {
+func (graph *Graph) Get(name string) (*image.Image, error) {
 	id, err := graph.idIndex.Get(name)
 	id, err := graph.idIndex.Get(name)
 	if err != nil {
 	if err != nil {
 		return nil, fmt.Errorf("could not find image: %v", err)
 		return nil, fmt.Errorf("could not find image: %v", err)
@@ -202,8 +186,8 @@ func (graph *Graph) Get(name string) (*Image, error) {
 }
 }
 
 
 // Create creates a new image and registers it in the graph.
 // Create creates a new image and registers it in the graph.
-func (graph *Graph) Create(layerData archive.ArchiveReader, containerID, containerImage, comment, author string, containerConfig, config *runconfig.Config) (*Image, error) {
-	img := &Image{
+func (graph *Graph) Create(layerData archive.ArchiveReader, containerID, containerImage, comment, author string, containerConfig, config *runconfig.Config) (*image.Image, error) {
+	img := &image.Image{
 		ID:            stringid.GenerateRandomID(),
 		ID:            stringid.GenerateRandomID(),
 		Comment:       comment,
 		Comment:       comment,
 		Created:       time.Now().UTC(),
 		Created:       time.Now().UTC(),
@@ -227,7 +211,7 @@ func (graph *Graph) Create(layerData archive.ArchiveReader, containerID, contain
 }
 }
 
 
 // Register imports a pre-existing image into the graph.
 // Register imports a pre-existing image into the graph.
-func (graph *Graph) Register(img *Image, layerData archive.ArchiveReader) (err error) {
+func (graph *Graph) Register(img *image.Image, layerData archive.ArchiveReader) (err error) {
 
 
 	if err := image.ValidateID(img.ID); err != nil {
 	if err := image.ValidateID(img.ID); err != nil {
 		return err
 		return err
@@ -380,9 +364,9 @@ func (graph *Graph) Delete(name string) error {
 }
 }
 
 
 // Map returns a list of all images in the graph, addressable by ID.
 // Map returns a list of all images in the graph, addressable by ID.
-func (graph *Graph) Map() map[string]*Image {
-	images := make(map[string]*Image)
-	graph.walkAll(func(image *Image) {
+func (graph *Graph) Map() map[string]*image.Image {
+	images := make(map[string]*image.Image)
+	graph.walkAll(func(image *image.Image) {
 		images[image.ID] = image
 		images[image.ID] = image
 	})
 	})
 	return images
 	return images
@@ -390,7 +374,7 @@ func (graph *Graph) Map() map[string]*Image {
 
 
 // walkAll iterates over each image in the graph, and passes it to a handler.
 // walkAll iterates over each image in the graph, and passes it to a handler.
 // The walking order is undetermined.
 // The walking order is undetermined.
-func (graph *Graph) walkAll(handler func(*Image)) {
+func (graph *Graph) walkAll(handler func(*image.Image)) {
 	graph.idIndex.Iterate(func(id string) {
 	graph.idIndex.Iterate(func(id string) {
 		if img, err := graph.Get(id); err != nil {
 		if img, err := graph.Get(id); err != nil {
 			return
 			return
@@ -404,9 +388,9 @@ func (graph *Graph) walkAll(handler func(*Image)) {
 // If an image of id ID has 3 children images, then the value for key ID
 // If an image of id ID has 3 children images, then the value for key ID
 // will be a list of 3 images.
 // will be a list of 3 images.
 // If an image has no children, it will not have an entry in the table.
 // If an image has no children, it will not have an entry in the table.
-func (graph *Graph) ByParent() map[string][]*Image {
-	byParent := make(map[string][]*Image)
-	graph.walkAll(func(img *Image) {
+func (graph *Graph) ByParent() map[string][]*image.Image {
+	byParent := make(map[string][]*image.Image)
+	graph.walkAll(func(img *image.Image) {
 		parent, err := graph.Get(img.Parent)
 		parent, err := graph.Get(img.Parent)
 		if err != nil {
 		if err != nil {
 			return
 			return
@@ -414,7 +398,7 @@ func (graph *Graph) ByParent() map[string][]*Image {
 		if children, exists := byParent[parent.ID]; exists {
 		if children, exists := byParent[parent.ID]; exists {
 			byParent[parent.ID] = append(children, img)
 			byParent[parent.ID] = append(children, img)
 		} else {
 		} else {
-			byParent[parent.ID] = []*Image{img}
+			byParent[parent.ID] = []*image.Image{img}
 		}
 		}
 	})
 	})
 	return byParent
 	return byParent
@@ -433,10 +417,10 @@ func (graph *Graph) Release(sessionID string, layerIDs ...string) {
 
 
 // Heads returns all heads in the graph, keyed by id.
 // Heads returns all heads in the graph, keyed by id.
 // A head is an image which is not the parent of another image in the graph.
 // A head is an image which is not the parent of another image in the graph.
-func (graph *Graph) Heads() map[string]*Image {
-	heads := make(map[string]*Image)
+func (graph *Graph) Heads() map[string]*image.Image {
+	heads := make(map[string]*image.Image)
 	byParent := graph.ByParent()
 	byParent := graph.ByParent()
-	graph.walkAll(func(image *Image) {
+	graph.walkAll(func(image *image.Image) {
 		// If it's not in the byParent lookup table, then
 		// If it's not in the byParent lookup table, then
 		// it's not a parent -> so it's a head!
 		// it's not a parent -> so it's a head!
 		if _, exists := byParent[image.ID]; !exists {
 		if _, exists := byParent[image.ID]; !exists {
@@ -451,7 +435,7 @@ func (graph *Graph) imageRoot(id string) string {
 }
 }
 
 
 // loadImage fetches the image with the given id from the graph.
 // loadImage fetches the image with the given id from the graph.
-func (graph *Graph) loadImage(id string) (*Image, error) {
+func (graph *Graph) loadImage(id string) (*image.Image, error) {
 	root := graph.imageRoot(id)
 	root := graph.imageRoot(id)
 
 
 	// Open the JSON file to decode by streaming
 	// Open the JSON file to decode by streaming
@@ -461,7 +445,7 @@ func (graph *Graph) loadImage(id string) (*Image, error) {
 	}
 	}
 	defer jsonSource.Close()
 	defer jsonSource.Close()
 
 
-	img := &Image{}
+	img := &image.Image{}
 	dec := json.NewDecoder(jsonSource)
 	dec := json.NewDecoder(jsonSource)
 
 
 	// Decode the JSON data
 	// Decode the JSON data
@@ -538,14 +522,3 @@ func (graph *Graph) RawJSON(id string) ([]byte, error) {
 func jsonPath(root string) string {
 func jsonPath(root string) string {
 	return filepath.Join(root, "json")
 	return filepath.Join(root, "json")
 }
 }
-
-// Build an Image object from raw json data
-func NewImgJSON(src []byte) (*Image, error) {
-	ret := &Image{}
-
-	// FIXME: Is there a cleaner way to "purify" the input json?
-	if err := json.Unmarshal(src, ret); err != nil {
-		return nil, err
-	}
-	return ret, nil
-}

+ 6 - 6
graph/graph_test.go

@@ -67,7 +67,7 @@ func TestInterruptedRegister(t *testing.T) {
 	graph, _ := tempGraph(t)
 	graph, _ := tempGraph(t)
 	defer nukeGraph(graph)
 	defer nukeGraph(graph)
 	badArchive, w := io.Pipe() // Use a pipe reader as a fake archive which never yields data
 	badArchive, w := io.Pipe() // Use a pipe reader as a fake archive which never yields data
-	image := &Image{
+	image := &image.Image{
 		ID:      stringid.GenerateRandomID(),
 		ID:      stringid.GenerateRandomID(),
 		Comment: "testing",
 		Comment: "testing",
 		Created: time.Now(),
 		Created: time.Now(),
@@ -125,7 +125,7 @@ func TestRegister(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	image := &Image{
+	image := &image.Image{
 		ID:      stringid.GenerateRandomID(),
 		ID:      stringid.GenerateRandomID(),
 		Comment: "testing",
 		Comment: "testing",
 		Created: time.Now(),
 		Created: time.Now(),
@@ -228,19 +228,19 @@ func TestByParent(t *testing.T) {
 
 
 	graph, _ := tempGraph(t)
 	graph, _ := tempGraph(t)
 	defer nukeGraph(graph)
 	defer nukeGraph(graph)
-	parentImage := &Image{
+	parentImage := &image.Image{
 		ID:      stringid.GenerateRandomID(),
 		ID:      stringid.GenerateRandomID(),
 		Comment: "parent",
 		Comment: "parent",
 		Created: time.Now(),
 		Created: time.Now(),
 		Parent:  "",
 		Parent:  "",
 	}
 	}
-	childImage1 := &Image{
+	childImage1 := &image.Image{
 		ID:      stringid.GenerateRandomID(),
 		ID:      stringid.GenerateRandomID(),
 		Comment: "child1",
 		Comment: "child1",
 		Created: time.Now(),
 		Created: time.Now(),
 		Parent:  parentImage.ID,
 		Parent:  parentImage.ID,
 	}
 	}
-	childImage2 := &Image{
+	childImage2 := &image.Image{
 		ID:      stringid.GenerateRandomID(),
 		ID:      stringid.GenerateRandomID(),
 		Comment: "child2",
 		Comment: "child2",
 		Created: time.Now(),
 		Created: time.Now(),
@@ -257,7 +257,7 @@ func TestByParent(t *testing.T) {
 	}
 	}
 }
 }
 
 
-func createTestImage(graph *Graph, t *testing.T) *Image {
+func createTestImage(graph *Graph, t *testing.T) *image.Image {
 	archive, err := fakeTar()
 	archive, err := fakeTar()
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)

+ 4 - 3
graph/graph_unix.go

@@ -10,6 +10,7 @@ import (
 	"strings"
 	"strings"
 	"syscall"
 	"syscall"
 
 
+	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/system"
 )
 )
@@ -72,7 +73,7 @@ func SetupInitLayer(initLayer string) error {
 	return nil
 	return nil
 }
 }
 
 
-func createRootFilesystemInDriver(graph *Graph, img *Image, layerData archive.ArchiveReader) error {
+func createRootFilesystemInDriver(graph *Graph, img *image.Image, layerData archive.ArchiveReader) error {
 	if err := graph.driver.Create(img.ID, img.Parent); err != nil {
 	if err := graph.driver.Create(img.ID, img.Parent); err != nil {
 		return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
 		return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
 	}
 	}
@@ -86,7 +87,7 @@ func (graph *Graph) restoreBaseImages() ([]string, error) {
 // storeImage stores file system layer data for the given image to the
 // storeImage stores file system layer data for the given image to the
 // graph's storage driver. Image metadata is stored in a file
 // graph's storage driver. Image metadata is stored in a file
 // at the specified root directory.
 // at the specified root directory.
-func (graph *Graph) storeImage(img *Image, layerData archive.ArchiveReader, root string) (err error) {
+func (graph *Graph) storeImage(img *image.Image, layerData archive.ArchiveReader, root string) (err error) {
 	// Store the layer. If layerData is not nil, unpack it into the new layer
 	// Store the layer. If layerData is not nil, unpack it into the new layer
 	if layerData != nil {
 	if layerData != nil {
 		if img.Size, err = graph.driver.ApplyDiff(img.ID, img.Parent, layerData); err != nil {
 		if img.Size, err = graph.driver.ApplyDiff(img.ID, img.Parent, layerData); err != nil {
@@ -109,6 +110,6 @@ func (graph *Graph) storeImage(img *Image, layerData archive.ArchiveReader, root
 }
 }
 
 
 // TarLayer returns a tar archive of the image's filesystem layer.
 // TarLayer returns a tar archive of the image's filesystem layer.
-func (graph *Graph) TarLayer(img *Image) (arch archive.Archive, err error) {
+func (graph *Graph) TarLayer(img *image.Image) (arch archive.Archive, err error) {
 	return graph.driver.Diff(img.ID, img.Parent)
 	return graph.driver.Diff(img.ID, img.Parent)
 }
 }

+ 5 - 4
graph/graph_windows.go

@@ -9,6 +9,7 @@ import (
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/daemon/graphdriver/windows"
 	"github.com/docker/docker/daemon/graphdriver/windows"
+	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/archive"
 )
 )
 
 
@@ -18,7 +19,7 @@ func SetupInitLayer(initLayer string) error {
 	return nil
 	return nil
 }
 }
 
 
-func createRootFilesystemInDriver(graph *Graph, img *Image, layerData archive.ArchiveReader) error {
+func createRootFilesystemInDriver(graph *Graph, img *image.Image, layerData archive.ArchiveReader) error {
 	if wd, ok := graph.driver.(*windows.WindowsGraphDriver); ok {
 	if wd, ok := graph.driver.(*windows.WindowsGraphDriver); ok {
 		if img.Container != "" && layerData == nil {
 		if img.Container != "" && layerData == nil {
 			logrus.Debugf("Copying from container %s.", img.Container)
 			logrus.Debugf("Copying from container %s.", img.Container)
@@ -59,7 +60,7 @@ func (graph *Graph) restoreBaseImages() ([]string, error) {
 }
 }
 
 
 // ParentLayerIds returns a list of all parent image IDs for the given image.
 // ParentLayerIds returns a list of all parent image IDs for the given image.
-func (graph *Graph) ParentLayerIds(img *Image) (ids []string, err error) {
+func (graph *Graph) ParentLayerIds(img *image.Image) (ids []string, err error) {
 	for i := img; i != nil && err == nil; i, err = graph.GetParent(i) {
 	for i := img; i != nil && err == nil; i, err = graph.GetParent(i) {
 		ids = append(ids, i.ID)
 		ids = append(ids, i.ID)
 	}
 	}
@@ -70,7 +71,7 @@ func (graph *Graph) ParentLayerIds(img *Image) (ids []string, err error) {
 // storeImage stores file system layer data for the given image to the
 // storeImage stores file system layer data for the given image to the
 // graph's storage driver. Image metadata is stored in a file
 // graph's storage driver. Image metadata is stored in a file
 // at the specified root directory.
 // at the specified root directory.
-func (graph *Graph) storeImage(img *Image, layerData archive.ArchiveReader, root string) (err error) {
+func (graph *Graph) storeImage(img *image.Image, layerData archive.ArchiveReader, root string) (err error) {
 
 
 	if wd, ok := graph.driver.(*windows.WindowsGraphDriver); ok {
 	if wd, ok := graph.driver.(*windows.WindowsGraphDriver); ok {
 		// Store the layer. If layerData is not nil and this isn't a base image,
 		// Store the layer. If layerData is not nil and this isn't a base image,
@@ -135,7 +136,7 @@ func (graph *Graph) storeImage(img *Image, layerData archive.ArchiveReader, root
 }
 }
 
 
 // TarLayer returns a tar archive of the image's filesystem layer.
 // TarLayer returns a tar archive of the image's filesystem layer.
-func (graph *Graph) TarLayer(img *Image) (arch archive.Archive, err error) {
+func (graph *Graph) TarLayer(img *image.Image) (arch archive.Archive, err error) {
 	if wd, ok := graph.driver.(*windows.WindowsGraphDriver); ok {
 	if wd, ok := graph.driver.(*windows.WindowsGraphDriver); ok {
 		var ids []string
 		var ids []string
 		if img.Parent != "" {
 		if img.Parent != "" {

+ 7 - 6
graph/history.go

@@ -5,12 +5,13 @@ import (
 	"strings"
 	"strings"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/image"
 	"github.com/docker/docker/utils"
 	"github.com/docker/docker/utils"
 )
 )
 
 
 // WalkHistory calls the handler function for each image in the
 // WalkHistory calls the handler function for each image in the
 // provided images lineage starting from immediate parent.
 // provided images lineage starting from immediate parent.
-func (graph *Graph) WalkHistory(img *Image, handler func(Image) error) (err error) {
+func (graph *Graph) WalkHistory(img *image.Image, handler func(image.Image) error) (err error) {
 	currentImg := img
 	currentImg := img
 	for currentImg != nil {
 	for currentImg != nil {
 		if handler != nil {
 		if handler != nil {
@@ -28,7 +29,7 @@ func (graph *Graph) WalkHistory(img *Image, handler func(Image) error) (err erro
 
 
 // depth returns the number of parents for a
 // depth returns the number of parents for a
 // current image
 // current image
-func (graph *Graph) depth(img *Image) (int, error) {
+func (graph *Graph) depth(img *image.Image) (int, error) {
 	var (
 	var (
 		count  = 0
 		count  = 0
 		parent = img
 		parent = img
@@ -53,7 +54,7 @@ const MaxImageDepth = 127
 // CheckDepth returns an error if the depth of an image, as returned
 // CheckDepth returns an error if the depth of an image, as returned
 // by ImageDepth, is too large to support creating a container from it
 // by ImageDepth, is too large to support creating a container from it
 // on this daemon.
 // on this daemon.
-func (graph *Graph) CheckDepth(img *Image) error {
+func (graph *Graph) CheckDepth(img *image.Image) error {
 	// We add 2 layers to the depth because the container's rw and
 	// We add 2 layers to the depth because the container's rw and
 	// init layer add to the restriction
 	// init layer add to the restriction
 	depth, err := graph.depth(img)
 	depth, err := graph.depth(img)
@@ -85,7 +86,7 @@ func (s *TagStore) History(name string) ([]*types.ImageHistory, error) {
 
 
 	history := []*types.ImageHistory{}
 	history := []*types.ImageHistory{}
 
 
-	err = s.graph.WalkHistory(foundImage, func(img Image) error {
+	err = s.graph.WalkHistory(foundImage, func(img image.Image) error {
 		history = append(history, &types.ImageHistory{
 		history = append(history, &types.ImageHistory{
 			ID:        img.ID,
 			ID:        img.ID,
 			Created:   img.Created.Unix(),
 			Created:   img.Created.Unix(),
@@ -100,14 +101,14 @@ func (s *TagStore) History(name string) ([]*types.ImageHistory, error) {
 	return history, err
 	return history, err
 }
 }
 
 
-func (graph *Graph) GetParent(img *Image) (*Image, error) {
+func (graph *Graph) GetParent(img *image.Image) (*image.Image, error) {
 	if img.Parent == "" {
 	if img.Parent == "" {
 		return nil, nil
 		return nil, nil
 	}
 	}
 	return graph.Get(img.Parent)
 	return graph.Get(img.Parent)
 }
 }
 
 
-func (graph *Graph) GetParentsSize(img *Image, size int64) int64 {
+func (graph *Graph) GetParentsSize(img *image.Image, size int64) int64 {
 	parentImage, err := graph.GetParent(img)
 	parentImage, err := graph.GetParent(img)
 	if err != nil || parentImage == nil {
 	if err != nil || parentImage == nil {
 		return size
 		return size

+ 2 - 1
graph/list.go

@@ -8,6 +8,7 @@ import (
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/parsers/filters"
 	"github.com/docker/docker/pkg/parsers/filters"
 	"github.com/docker/docker/utils"
 	"github.com/docker/docker/utils"
 )
 )
@@ -31,7 +32,7 @@ func (r ByCreated) Less(i, j int) bool { return r[i].Created < r[j].Created }
 
 
 func (s *TagStore) Images(config *ImagesConfig) ([]*types.Image, error) {
 func (s *TagStore) Images(config *ImagesConfig) ([]*types.Image, error) {
 	var (
 	var (
-		allImages  map[string]*Image
+		allImages  map[string]*image.Image
 		err        error
 		err        error
 		filtTagged = true
 		filtTagged = true
 		filtLabel  = false
 		filtLabel  = false

+ 1 - 1
graph/load.go

@@ -95,7 +95,7 @@ func (s *TagStore) recursiveLoad(address, tmpImageDir string) error {
 			logrus.Debugf("Error reading embedded tar: %v", err)
 			logrus.Debugf("Error reading embedded tar: %v", err)
 			return err
 			return err
 		}
 		}
-		img, err := NewImgJSON(imageJson)
+		img, err := image.NewImgJSON(imageJson)
 		if err != nil {
 		if err != nil {
 			logrus.Debugf("Error unmarshalling json: %v", err)
 			logrus.Debugf("Error unmarshalling json: %v", err)
 			return err
 			return err

+ 3 - 2
graph/pull_v1.go

@@ -10,6 +10,7 @@ import (
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/distribution/registry/client/transport"
+	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/progressreader"
 	"github.com/docker/docker/pkg/progressreader"
 	"github.com/docker/docker/pkg/streamformatter"
 	"github.com/docker/docker/pkg/streamformatter"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
@@ -257,7 +258,7 @@ func (p *v1Puller) pullImage(imgID, endpoint string, token []string) (bool, erro
 				imgJSON []byte
 				imgJSON []byte
 				imgSize int
 				imgSize int
 				err     error
 				err     error
-				img     *Image
+				img     *image.Image
 			)
 			)
 			retries := 5
 			retries := 5
 			for j := 1; j <= retries; j++ {
 			for j := 1; j <= retries; j++ {
@@ -269,7 +270,7 @@ func (p *v1Puller) pullImage(imgID, endpoint string, token []string) (bool, erro
 					time.Sleep(time.Duration(j) * 500 * time.Millisecond)
 					time.Sleep(time.Duration(j) * 500 * time.Millisecond)
 					continue
 					continue
 				}
 				}
-				img, err = NewImgJSON(imgJSON)
+				img, err = image.NewImgJSON(imgJSON)
 				layersDownloaded = true
 				layersDownloaded = true
 				if err != nil && j == retries {
 				if err != nil && j == retries {
 					out.Write(p.sf.FormatProgress(stringid.TruncateID(id), "Error pulling dependent layers", nil))
 					out.Write(p.sf.FormatProgress(stringid.TruncateID(id), "Error pulling dependent layers", nil))

+ 3 - 2
graph/pull_v2.go

@@ -10,6 +10,7 @@ import (
 	"github.com/docker/distribution"
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/manifest"
 	"github.com/docker/distribution/manifest"
+	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/progressreader"
 	"github.com/docker/docker/pkg/progressreader"
 	"github.com/docker/docker/pkg/streamformatter"
 	"github.com/docker/docker/pkg/streamformatter"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
@@ -94,7 +95,7 @@ func (p *v2Puller) pullV2Repository(tag string) (err error) {
 
 
 // downloadInfo is used to pass information from download to extractor
 // downloadInfo is used to pass information from download to extractor
 type downloadInfo struct {
 type downloadInfo struct {
-	img      *Image
+	img      *image.Image
 	tmpFile  *os.File
 	tmpFile  *os.File
 	digest   digest.Digest
 	digest   digest.Digest
 	layer    distribution.ReadSeekCloser
 	layer    distribution.ReadSeekCloser
@@ -208,7 +209,7 @@ func (p *v2Puller) pullV2Tag(tag, taggedName string) (bool, error) {
 	}()
 	}()
 
 
 	for i := len(manifest.FSLayers) - 1; i >= 0; i-- {
 	for i := len(manifest.FSLayers) - 1; i >= 0; i-- {
-		img, err := NewImgJSON([]byte(manifest.History[i].V1Compatibility))
+		img, err := image.NewImgJSON([]byte(manifest.History[i].V1Compatibility))
 		if err != nil {
 		if err != nil {
 			logrus.Debugf("error getting image v1 json: %v", err)
 			logrus.Debugf("error getting image v1 json: %v", err)
 			return false, err
 			return false, err

+ 2 - 1
graph/push_v2.go

@@ -9,6 +9,7 @@ import (
 	"github.com/docker/distribution"
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/manifest"
 	"github.com/docker/distribution/manifest"
+	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/progressreader"
 	"github.com/docker/docker/pkg/progressreader"
 	"github.com/docker/docker/pkg/streamformatter"
 	"github.com/docker/docker/pkg/streamformatter"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
@@ -190,7 +191,7 @@ func (p *v2Pusher) pushV2Tag(tag string) error {
 	return p.repo.Manifests().Put(signed)
 	return p.repo.Manifests().Put(signed)
 }
 }
 
 
-func (p *v2Pusher) pushV2Image(bs distribution.BlobService, img *Image) (digest.Digest, error) {
+func (p *v2Pusher) pushV2Image(bs distribution.BlobService, img *image.Image) (digest.Digest, error) {
 	out := p.config.OutStream
 	out := p.config.OutStream
 
 
 	out.Write(p.sf.FormatProgress(stringid.TruncateID(img.ID), "Buffering to Disk", nil))
 	out.Write(p.sf.FormatProgress(stringid.TruncateID(img.ID), "Buffering to Disk", nil))

+ 4 - 3
graph/tags.go

@@ -15,6 +15,7 @@ import (
 	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/daemon/events"
 	"github.com/docker/docker/daemon/events"
 	"github.com/docker/docker/graph/tags"
 	"github.com/docker/docker/graph/tags"
+	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
@@ -120,7 +121,7 @@ func (store *TagStore) reload() error {
 	return nil
 	return nil
 }
 }
 
 
-func (store *TagStore) LookupImage(name string) (*Image, error) {
+func (store *TagStore) LookupImage(name string) (*image.Image, error) {
 	// FIXME: standardize on returning nil when the image doesn't exist, and err for everything else
 	// FIXME: standardize on returning nil when the image doesn't exist, and err for everything else
 	// (so we can pass all errors here)
 	// (so we can pass all errors here)
 	repoName, ref := parsers.ParseRepositoryTag(name)
 	repoName, ref := parsers.ParseRepositoryTag(name)
@@ -129,7 +130,7 @@ func (store *TagStore) LookupImage(name string) (*Image, error) {
 	}
 	}
 	var (
 	var (
 		err error
 		err error
-		img *Image
+		img *image.Image
 	)
 	)
 
 
 	img, err = store.GetImage(repoName, ref)
 	img, err = store.GetImage(repoName, ref)
@@ -331,7 +332,7 @@ func (store *TagStore) Get(repoName string) (Repository, error) {
 	return nil, nil
 	return nil, nil
 }
 }
 
 
-func (store *TagStore) GetImage(repoName, refOrID string) (*Image, error) {
+func (store *TagStore) GetImage(repoName, refOrID string) (*image.Image, error) {
 	repo, err := store.Get(repoName)
 	repo, err := store.Get(repoName)
 
 
 	if err != nil {
 	if err != nil {

+ 3 - 2
graph/tags_unit_test.go

@@ -11,6 +11,7 @@ import (
 	"github.com/docker/docker/daemon/events"
 	"github.com/docker/docker/daemon/events"
 	"github.com/docker/docker/daemon/graphdriver"
 	"github.com/docker/docker/daemon/graphdriver"
 	_ "github.com/docker/docker/daemon/graphdriver/vfs" // import the vfs driver so it is used in the tests
 	_ "github.com/docker/docker/daemon/graphdriver/vfs" // import the vfs driver so it is used in the tests
+	"github.com/docker/docker/image"
 	"github.com/docker/docker/trust"
 	"github.com/docker/docker/trust"
 	"github.com/docker/docker/utils"
 	"github.com/docker/docker/utils"
 )
 )
@@ -79,7 +80,7 @@ func mkTestTagStore(root string, t *testing.T) *TagStore {
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	img := &Image{ID: testOfficialImageID}
+	img := &image.Image{ID: testOfficialImageID}
 	if err := graph.Register(img, officialArchive); err != nil {
 	if err := graph.Register(img, officialArchive); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -90,7 +91,7 @@ func mkTestTagStore(root string, t *testing.T) *TagStore {
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	img = &Image{ID: testPrivateImageID}
+	img = &image.Image{ID: testPrivateImageID}
 	if err := graph.Register(img, privateArchive); err != nil {
 	if err := graph.Register(img, privateArchive); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}

+ 30 - 0
image/image.go

@@ -1,12 +1,42 @@
 package image
 package image
 
 
 import (
 import (
+	"encoding/json"
 	"fmt"
 	"fmt"
 	"regexp"
 	"regexp"
+	"time"
+
+	"github.com/docker/docker/runconfig"
 )
 )
 
 
 var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`)
 var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`)
 
 
+type Image struct {
+	ID              string            `json:"id"`
+	Parent          string            `json:"parent,omitempty"`
+	Comment         string            `json:"comment,omitempty"`
+	Created         time.Time         `json:"created"`
+	Container       string            `json:"container,omitempty"`
+	ContainerConfig runconfig.Config  `json:"container_config,omitempty"`
+	DockerVersion   string            `json:"docker_version,omitempty"`
+	Author          string            `json:"author,omitempty"`
+	Config          *runconfig.Config `json:"config,omitempty"`
+	Architecture    string            `json:"architecture,omitempty"`
+	OS              string            `json:"os,omitempty"`
+	Size            int64
+}
+
+// Build an Image object from raw json data
+func NewImgJSON(src []byte) (*Image, error) {
+	ret := &Image{}
+
+	// FIXME: Is there a cleaner way to "purify" the input json?
+	if err := json.Unmarshal(src, ret); err != nil {
+		return nil, err
+	}
+	return ret, nil
+}
+
 // Check wheather id is a valid image ID or not
 // Check wheather id is a valid image ID or not
 func ValidateID(id string) error {
 func ValidateID(id string) error {
 	if ok := validHex.MatchString(id); !ok {
 	if ok := validHex.MatchString(id); !ok {