From c5a5900eb9b40e17d484adcb5571c028e04040e7 Mon Sep 17 00:00:00 2001 From: Aaron Lehmann Date: Wed, 29 Jul 2015 16:45:47 -0700 Subject: [PATCH] Documentation improvements and code cleanups for graph package Expand the godoc documentation for the graph package. Centralize DefaultTag in the graphs/tag package instead of defining it twice. Remove some unnecessary "config" structs that are only used to pass a few parameters to a function. Simplify the GetParentsSize function - there's no reason for it to take an accumulator argument. Unexport some functions that aren't needed outside the package. Signed-off-by: Aaron Lehmann Upstream-commit: d4836cd7ec1c085c5a5caa7eb7f5eda4ace10eb6 Component: engine --- components/engine/api/client/build.go | 2 +- components/engine/api/client/create.go | 4 +- components/engine/api/client/pull.go | 2 +- components/engine/api/server/server.go | 28 +++------ components/engine/daemon/create.go | 4 +- components/engine/daemon/image_delete.go | 11 ++-- components/engine/graph/export.go | 25 +++----- components/engine/graph/graph.go | 13 ++-- components/engine/graph/history.go | 15 ++--- components/engine/graph/import.go | 31 +++------- components/engine/graph/list.go | 35 +++++------ components/engine/graph/load.go | 2 +- components/engine/graph/pull.go | 25 +++++--- components/engine/graph/push.go | 26 +++++--- components/engine/graph/registry.go | 4 +- components/engine/graph/service.go | 7 ++- components/engine/graph/tags.go | 74 ++++++++++++++--------- components/engine/graph/tags/tags.go | 6 +- components/engine/graph/tags_unit_test.go | 15 ++--- 19 files changed, 168 insertions(+), 161 deletions(-) diff --git a/components/engine/api/client/build.go b/components/engine/api/client/build.go index 05dec0bbfc..0bcd66b8f7 100644 --- a/components/engine/api/client/build.go +++ b/components/engine/api/client/build.go @@ -526,7 +526,7 @@ func rewriteDockerfileFrom(dockerfileName string, translator func(string, regist // Replace the line with a resolved "FROM repo@digest" repo, tag := parsers.ParseRepositoryTag(matches[1]) if tag == "" { - tag = tags.DEFAULTTAG + tag = tags.DefaultTag } ref := registry.ParseReference(tag) diff --git a/components/engine/api/client/create.go b/components/engine/api/client/create.go index 76e935eb15..47e69feb26 100644 --- a/components/engine/api/client/create.go +++ b/components/engine/api/client/create.go @@ -26,7 +26,7 @@ func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error { repos, tag := parsers.ParseRepositoryTag(image) // pull only the image tagged 'latest' if no tag was specified if tag == "" { - tag = tags.DEFAULTTAG + tag = tags.DefaultTag } v.Set("fromImage", repos) v.Set("tag", tag) @@ -96,7 +96,7 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc repo, tag := parsers.ParseRepositoryTag(config.Image) if tag == "" { - tag = tags.DEFAULTTAG + tag = tags.DefaultTag } ref := registry.ParseReference(tag) diff --git a/components/engine/api/client/pull.go b/components/engine/api/client/pull.go index d6b8554311..ecce34161e 100644 --- a/components/engine/api/client/pull.go +++ b/components/engine/api/client/pull.go @@ -25,7 +25,7 @@ func (cli *DockerCli) CmdPull(args ...string) error { taglessRemote, tag := parsers.ParseRepositoryTag(remote) if tag == "" && !*allTags { - tag = tags.DEFAULTTAG + tag = tags.DefaultTag fmt.Fprintf(cli.out, "Using default tag: %s\n", tag) } else if tag != "" && *allTags { return fmt.Errorf("tag can't be used with --all-tags/-a") diff --git a/components/engine/api/server/server.go b/components/engine/api/server/server.go index 3cd499f2ac..9caf279fad 100644 --- a/components/engine/api/server/server.go +++ b/components/engine/api/server/server.go @@ -364,14 +364,8 @@ func (s *Server) getImagesJSON(version version.Version, w http.ResponseWriter, r return err } - imagesConfig := graph.ImagesConfig{ - Filters: r.Form.Get("filters"), - // FIXME this parameter could just be a match filter - Filter: r.Form.Get("filter"), - All: boolValue(r, "all"), - } - - images, err := s.daemon.Repositories().Images(&imagesConfig) + // FIXME: The filter parameter could just be a match filter + images, err := s.daemon.Repositories().Images(r.Form.Get("filters"), r.Form.Get("filter"), boolValue(r, "all")) if err != nil { return err } @@ -785,23 +779,17 @@ func (s *Server) postImagesCreate(version version.Version, w http.ResponseWriter } src := r.Form.Get("fromSrc") - imageImportConfig := &graph.ImageImportConfig{ - Changes: r.Form["changes"], - InConfig: r.Body, - OutStream: output, - } // 'err' MUST NOT be defined within this block, we need any error // generated from the download to be available to the output // stream processing below var newConfig *runconfig.Config - newConfig, err = builder.BuildFromConfig(s.daemon, &runconfig.Config{}, imageImportConfig.Changes) + newConfig, err = builder.BuildFromConfig(s.daemon, &runconfig.Config{}, r.Form["changes"]) if err != nil { return err } - imageImportConfig.ContainerConfig = newConfig - err = s.daemon.Repositories().Import(src, repo, tag, imageImportConfig) + err = s.daemon.Repositories().Import(src, repo, tag, r.Body, output, newConfig) } if err != nil { if !output.Flushed() { @@ -909,14 +897,14 @@ func (s *Server) getImagesGet(version version.Version, w http.ResponseWriter, r w.Header().Set("Content-Type", "application/x-tar") output := ioutils.NewWriteFlusher(w) - imageExportConfig := &graph.ImageExportConfig{Outstream: output} + var names []string if name, ok := vars["name"]; ok { - imageExportConfig.Names = []string{name} + names = []string{name} } else { - imageExportConfig.Names = r.Form["names"] + names = r.Form["names"] } - if err := s.daemon.Repositories().ImageExport(imageExportConfig); err != nil { + if err := s.daemon.Repositories().ImageExport(names, output); err != nil { if !output.Flushed() { return err } diff --git a/components/engine/daemon/create.go b/components/engine/daemon/create.go index 1d0a9a5b6e..e06fc1046a 100644 --- a/components/engine/daemon/create.go +++ b/components/engine/daemon/create.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/Sirupsen/logrus" - "github.com/docker/docker/graph" + "github.com/docker/docker/graph/tags" "github.com/docker/docker/image" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/runconfig" @@ -27,7 +27,7 @@ func (daemon *Daemon) ContainerCreate(name string, config *runconfig.Config, hos if daemon.Graph().IsNotExist(err, config.Image) { _, tag := parsers.ParseRepositoryTag(config.Image) if tag == "" { - tag = graph.DefaultTag + tag = tags.DefaultTag } return "", warnings, fmt.Errorf("No such image: %s (tag: %s)", config.Image, tag) } diff --git a/components/engine/daemon/image_delete.go b/components/engine/daemon/image_delete.go index 3929b13385..f6f851c870 100644 --- a/components/engine/daemon/image_delete.go +++ b/components/engine/daemon/image_delete.go @@ -6,7 +6,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/api/types" - "github.com/docker/docker/graph" + "github.com/docker/docker/graph/tags" "github.com/docker/docker/image" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/stringid" @@ -27,16 +27,13 @@ func (daemon *Daemon) ImageDelete(name string, force, noprune bool) ([]types.Ima } func (daemon *Daemon) imgDeleteHelper(name string, list *[]types.ImageDelete, first, force, noprune bool) error { - var ( - repoName, tag string - tags = []string{} - ) + var repoName, tag string repoAndTags := make(map[string][]string) // FIXME: please respect DRY and centralize repo+tag parsing in a single central place! -- shykes repoName, tag = parsers.ParseRepositoryTag(name) if tag == "" { - tag = graph.DefaultTag + tag = tags.DefaultTag } if name == "" { @@ -111,7 +108,7 @@ func (daemon *Daemon) imgDeleteHelper(name string, list *[]types.ImageDelete, fi } } } - tags = daemon.Repositories().ByID()[img.ID] + tags := daemon.Repositories().ByID()[img.ID] if (len(tags) <= 1 && repoName == "") || len(tags) == 0 { if len(byParents[img.ID]) == 0 { if err := daemon.Repositories().DeleteAll(img.ID); err != nil { diff --git a/components/engine/graph/export.go b/components/engine/graph/export.go index a57c81a8ff..28cef2e4fb 100644 --- a/components/engine/graph/export.go +++ b/components/engine/graph/export.go @@ -13,21 +13,12 @@ import ( "github.com/docker/docker/registry" ) -// ImageExportConfig holds list of names to be exported to a output stream. -// All images with the given tag and all versions -// containing the same tag are exported. The resulting output is an -// uncompressed tar ball. -type ImageExportConfig struct { - // Names is the set of tags to export. - Names []string - // OutStream is the writer where the images are written to. - Outstream io.Writer -} - -// ImageExport exports list of images to a output stream specified in the config. -// The exported images are archived into a tar when written to the output stream. -func (s *TagStore) ImageExport(imageExportConfig *ImageExportConfig) error { - +// ImageExport exports list of images to a output stream specified in the +// config. The exported images are archived into a tar when written to the +// output stream. All images with the given tag and all versions containing the +// same tag are exported. names is the set of tags to export, and outStream +// is the writer which the images are written to. +func (s *TagStore) ImageExport(names []string, outStream io.Writer) error { // get image json tempdir, err := ioutil.TempDir("", "docker-export-") if err != nil { @@ -44,7 +35,7 @@ func (s *TagStore) ImageExport(imageExportConfig *ImageExportConfig) error { repo[tag] = id } } - for _, name := range imageExportConfig.Names { + for _, name := range names { name = registry.NormalizeLocalName(name) logrus.Debugf("Serializing %s", name) rootRepo := s.Repositories[name] @@ -107,7 +98,7 @@ func (s *TagStore) ImageExport(imageExportConfig *ImageExportConfig) error { } defer fs.Close() - if _, err := io.Copy(imageExportConfig.Outstream, fs); err != nil { + if _, err := io.Copy(outStream, fs); err != nil { return err } logrus.Debugf("End export image") diff --git a/components/engine/graph/graph.go b/components/engine/graph/graph.go index 4a6079c87f..37f83ef519 100644 --- a/components/engine/graph/graph.go +++ b/components/engine/graph/graph.go @@ -152,10 +152,11 @@ func (graph *Graph) restore() error { return nil } -// IsNotExist detects whether an image exists by parsing the incoming error message. -// FIXME: Implement error subclass instead of looking at the error text -// Note: This is the way golang implements os.IsNotExists on Plan9 +// IsNotExist detects whether an image exists by parsing the incoming error +// message. func (graph *Graph) IsNotExist(err error, id string) bool { + // FIXME: Implement error subclass instead of looking at the error text + // Note: This is the way golang implements os.IsNotExists on Plan9 return err != nil && (strings.Contains(strings.ToLower(err.Error()), "does not exist") || strings.Contains(strings.ToLower(err.Error()), "no such")) && strings.Contains(err.Error(), id) } @@ -415,13 +416,13 @@ func (graph *Graph) ByParent() map[string][]*image.Image { return byParent } -// Retain keeps the images and layers that are in pulling chain so that they are not deleted. -// If not, they may be deleted by rmi with dangling condition. +// Retain keeps the images and layers that are in the pulling chain so that +// they are not deleted. If not retained, they may be deleted by rmi. func (graph *Graph) Retain(sessionID string, layerIDs ...string) { graph.retained.Add(sessionID, layerIDs) } -// Release removes the referenced image id from the provided set of layers. +// Release removes the referenced image ID from the provided set of layers. func (graph *Graph) Release(sessionID string, layerIDs ...string) { graph.retained.Delete(sessionID, layerIDs) } diff --git a/components/engine/graph/history.go b/components/engine/graph/history.go index 390a4873e6..ab072d7296 100644 --- a/components/engine/graph/history.go +++ b/components/engine/graph/history.go @@ -67,7 +67,8 @@ func (graph *Graph) CheckDepth(img *image.Image) error { return nil } -// History returns a list of ImageHistory for the specified image name by walking the image lineage. +// History returns a slice of ImageHistory structures for the specified image +// name by walking the image lineage. func (s *TagStore) History(name string) ([]*types.ImageHistory, error) { foundImage, err := s.LookupImage(name) if err != nil { @@ -102,7 +103,7 @@ func (s *TagStore) History(name string) ([]*types.ImageHistory, error) { return history, err } -// GetParent returns the parent image. +// GetParent returns the parent image for the specified image. func (graph *Graph) GetParent(img *image.Image) (*image.Image, error) { if img.Parent == "" { return nil, nil @@ -110,12 +111,12 @@ func (graph *Graph) GetParent(img *image.Image) (*image.Image, error) { return graph.Get(img.Parent) } -// GetParentsSize returns the size of the parent. -func (graph *Graph) GetParentsSize(img *image.Image, size int64) int64 { +// GetParentsSize returns the combined size of all parent images. If there is +// no parent image or it's unavailable, it returns 0. +func (graph *Graph) GetParentsSize(img *image.Image) int64 { parentImage, err := graph.GetParent(img) if err != nil || parentImage == nil { - return size + return 0 } - size += parentImage.Size - return graph.GetParentsSize(parentImage, size) + return parentImage.Size + graph.GetParentsSize(parentImage) } diff --git a/components/engine/graph/import.go b/components/engine/graph/import.go index 33207c9b47..4e09cf0296 100644 --- a/components/engine/graph/import.go +++ b/components/engine/graph/import.go @@ -13,22 +13,11 @@ import ( "github.com/docker/docker/utils" ) -// ImageImportConfig holds configuration to import a image. -type ImageImportConfig struct { - // Changes are the container changes written to top layer. - Changes []string - // InConfig is the input stream containers layered data. - InConfig io.ReadCloser - // OutStream is the output stream where the image is written. - OutStream io.Writer - // ContainerConfig is the configuration of commit container. - ContainerConfig *runconfig.Config -} - -// Import allows to download image from a archive. -// If the src is a URL, the content is downloaded from the archive. If the source is '-' then the imageImportConfig.InConfig -// reader will be used to load the image. Once all the layers required are loaded locally, image is then tagged using the tag specified. -func (s *TagStore) Import(src string, repo string, tag string, imageImportConfig *ImageImportConfig) error { +// Import imports an image, getting the archived layer data either from +// inConfig (if src is "-"), or from a URI specified in src. Progress output is +// written to outStream. Repository and tag names can optionally be given in +// the repo and tag arguments, respectively. +func (s *TagStore) Import(src string, repo string, tag string, inConfig io.ReadCloser, outStream io.Writer, containerConfig *runconfig.Config) error { var ( sf = streamformatter.NewJSONStreamFormatter() archive archive.ArchiveReader @@ -36,7 +25,7 @@ func (s *TagStore) Import(src string, repo string, tag string, imageImportConfig ) if src == "-" { - archive = imageImportConfig.InConfig + archive = inConfig } else { u, err := url.Parse(src) if err != nil { @@ -47,14 +36,14 @@ func (s *TagStore) Import(src string, repo string, tag string, imageImportConfig u.Host = src u.Path = "" } - imageImportConfig.OutStream.Write(sf.FormatStatus("", "Downloading from %s", u)) + outStream.Write(sf.FormatStatus("", "Downloading from %s", u)) resp, err = httputils.Download(u.String()) if err != nil { return err } progressReader := progressreader.New(progressreader.Config{ In: resp.Body, - Out: imageImportConfig.OutStream, + Out: outStream, Formatter: sf, Size: int(resp.ContentLength), NewLines: true, @@ -65,7 +54,7 @@ func (s *TagStore) Import(src string, repo string, tag string, imageImportConfig archive = progressReader } - img, err := s.graph.Create(archive, "", "", "Imported from "+src, "", nil, imageImportConfig.ContainerConfig) + img, err := s.graph.Create(archive, "", "", "Imported from "+src, "", nil, containerConfig) if err != nil { return err } @@ -75,7 +64,7 @@ func (s *TagStore) Import(src string, repo string, tag string, imageImportConfig return err } } - imageImportConfig.OutStream.Write(sf.FormatStatus("", img.ID)) + outStream.Write(sf.FormatStatus("", img.ID)) logID := img.ID if tag != "" { logID = utils.ImageReference(logID, tag) diff --git a/components/engine/graph/list.go b/components/engine/graph/list.go index 28c83bc264..72eae63427 100644 --- a/components/engine/graph/list.go +++ b/components/engine/graph/list.go @@ -18,25 +18,20 @@ var acceptedImageFilterTags = map[string]struct{}{ "label": {}, } -// ImagesConfig defines the criteria to obtain a list of images. -type ImagesConfig struct { - // Filters is supported list of filters used to get list of images. - Filters string - // Filter the list of images by name. - Filter string - // All inditest that all the images will be returned in the list, if set to true. - All bool -} - -// byCreated is a temporary type used to sort list of images on their field 'Created'. +// byCreated is a temporary type used to sort a list of images by creation +// time. type byCreated []*types.Image func (r byCreated) Len() int { return len(r) } func (r byCreated) Swap(i, j int) { r[i], r[j] = r[j], r[i] } func (r byCreated) Less(i, j int) bool { return r[i].Created < r[j].Created } -// Images provide list of images based on selection criteria. -func (s *TagStore) Images(config *ImagesConfig) ([]*types.Image, error) { +// Images returns a filtered list of images. filterArgs is a JSON-encoded set +// of filter arguments which will be interpreted by pkg/parsers/filters. +// filter is a shell glob string applied to repository names. The argument +// named all controls whether all images in the graph are filtered, or just +// the heads. +func (s *TagStore) Images(filterArgs, filter string, all bool) ([]*types.Image, error) { var ( allImages map[string]*image.Image err error @@ -44,7 +39,7 @@ func (s *TagStore) Images(config *ImagesConfig) ([]*types.Image, error) { filtLabel = false ) - imageFilters, err := filters.FromParam(config.Filters) + imageFilters, err := filters.FromParam(filterArgs) if err != nil { return nil, err } @@ -64,7 +59,7 @@ func (s *TagStore) Images(config *ImagesConfig) ([]*types.Image, error) { _, filtLabel = imageFilters["label"] - if config.All && filtTagged { + if all && filtTagged { allImages = s.graph.Map() } else { allImages = s.graph.Heads() @@ -73,8 +68,8 @@ func (s *TagStore) Images(config *ImagesConfig) ([]*types.Image, error) { lookup := make(map[string]*types.Image) s.Lock() for repoName, repository := range s.Repositories { - if config.Filter != "" { - if match, _ := path.Match(config.Filter, repoName); !match { + if filter != "" { + if match, _ := path.Match(filter, repoName); !match { continue } } @@ -106,7 +101,7 @@ func (s *TagStore) Images(config *ImagesConfig) ([]*types.Image, error) { newImage.ID = image.ID newImage.Created = int(image.Created.Unix()) newImage.Size = int(image.Size) - newImage.VirtualSize = int(s.graph.GetParentsSize(image, 0) + image.Size) + newImage.VirtualSize = int(s.graph.GetParentsSize(image) + image.Size) newImage.Labels = image.ContainerConfig.Labels if utils.DigestReference(ref) { @@ -131,7 +126,7 @@ func (s *TagStore) Images(config *ImagesConfig) ([]*types.Image, error) { } // Display images which aren't part of a repository/tag - if config.Filter == "" || filtLabel { + if filter == "" || filtLabel { for _, image := range allImages { if !imageFilters.MatchKVList("label", image.ContainerConfig.Labels) { continue @@ -143,7 +138,7 @@ func (s *TagStore) Images(config *ImagesConfig) ([]*types.Image, error) { newImage.ID = image.ID newImage.Created = int(image.Created.Unix()) newImage.Size = int(image.Size) - newImage.VirtualSize = int(s.graph.GetParentsSize(image, 0) + image.Size) + newImage.VirtualSize = int(s.graph.GetParentsSize(image) + image.Size) newImage.Labels = image.ContainerConfig.Labels images = append(images, newImage) diff --git a/components/engine/graph/load.go b/components/engine/graph/load.go index 4d133c3adf..8f7efa6af5 100644 --- a/components/engine/graph/load.go +++ b/components/engine/graph/load.go @@ -71,7 +71,7 @@ func (s *TagStore) Load(inTar io.ReadCloser, outStream io.Writer) error { for imageName, tagMap := range repositories { for tag, address := range tagMap { - if err := s.SetLoad(imageName, tag, address, true, outStream); err != nil { + if err := s.setLoad(imageName, tag, address, true, outStream); err != nil { return err } } diff --git a/components/engine/graph/pull.go b/components/engine/graph/pull.go index 0cfc457f2d..7e6291ec94 100644 --- a/components/engine/graph/pull.go +++ b/components/engine/graph/pull.go @@ -13,15 +13,18 @@ import ( // ImagePullConfig stores pull configuration. type ImagePullConfig struct { - // MetaHeaders store meta data about the image (DockerHeaders with prefix X-Meta- in the request). + // MetaHeaders stores HTTP headers with metadata about the image + // (DockerHeaders with prefix X-Meta- in the request). MetaHeaders map[string][]string - // AuthConfig holds authentication information for authorizing with the registry. + // AuthConfig holds authentication credentials for authenticating with + // the registry. AuthConfig *cliconfig.AuthConfig - // OutStream is the output writer for showing the status of the pull operation. + // OutStream is the output writer for showing the status of the pull + // operation. OutStream io.Writer } -// Puller is an interface to define Pull behavior. +// Puller is an interface that abstracts pulling for different API versions. type Puller interface { // Pull tries to pull the image referenced by `tag` // Pull returns an error if any, as well as a boolean that determines whether to retry Pull on the next configured endpoint. @@ -30,7 +33,11 @@ type Puller interface { Pull(tag string) (fallback bool, err error) } -// NewPuller returns a new instance of an implementation conforming to Puller interface. +// NewPuller returns a Puller interface that will pull from either a v1 or v2 +// registry. The endpoint argument contains a Version field that determines +// whether a v1 or v2 puller will be created. The other parameters are passed +// through to the underlying puller implementation for use during the actual +// pull operation. func NewPuller(s *TagStore, endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, imagePullConfig *ImagePullConfig, sf *streamformatter.StreamFormatter) (Puller, error) { switch endpoint.Version { case registry.APIVersion2: @@ -53,7 +60,8 @@ func NewPuller(s *TagStore, endpoint registry.APIEndpoint, repoInfo *registry.Re return nil, fmt.Errorf("unknown version %d for registry %s", endpoint.Version, endpoint.URL) } -// Pull downloads a image with specified name and tag from the repo. +// Pull initiates a pull operation. image is the repository name to pull, and +// tag may be either empty, or indicate a specific tag to pull. func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConfig) error { var sf = streamformatter.NewJSONStreamFormatter() @@ -133,7 +141,10 @@ func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConf return lastErr } -// writeStatus shows status of the pull command. +// writeStatus writes a status message to out. If layersDownloaded is true, the +// status message indicates that a newer image was downloaded. Otherwise, it +// indicates that the image is up to date. requestedTag is the tag the message +// will refer to. func writeStatus(requestedTag string, out io.Writer, sf *streamformatter.StreamFormatter, layersDownloaded bool) { if layersDownloaded { out.Write(sf.FormatStatus("", "Status: Downloaded newer image for %s", requestedTag)) diff --git a/components/engine/graph/push.go b/components/engine/graph/push.go index 9095ae6afe..77c473f270 100644 --- a/components/engine/graph/push.go +++ b/components/engine/graph/push.go @@ -12,17 +12,21 @@ import ( // ImagePushConfig stores push configuration. type ImagePushConfig struct { - // MetaHeaders store meta data about the image (DockerHeaders with prefix X-Meta- in the request). + // MetaHeaders store HTTP headers with metadata about the image + // (DockerHeaders with prefix X-Meta- in the request). MetaHeaders map[string][]string - // AuthConfig holds authentication information for authorizing with the registry. + // AuthConfig holds authentication credentials for authenticating with + // the registry. AuthConfig *cliconfig.AuthConfig - // Tag is the specific variant of the image to be pushed, this tag used when image is pushed. If no tag is provided, all tags will be pushed. + // Tag is the specific variant of the image to be pushed. + // If no tag is provided, all tags will be pushed. Tag string - // OutStream is the output writer for showing the status of the push operation. + // OutStream is the output writer for showing the status of the push + // operation. OutStream io.Writer } -// Pusher is an interface to define Push behavior. +// Pusher is an interface that abstracts pushing for different API versions. type Pusher interface { // Push tries to push the image configured at the creation of Pusher. // Push returns an error if any, as well as a boolean that determines whether to retry Push on the next configured endpoint. @@ -31,7 +35,11 @@ type Pusher interface { Push() (fallback bool, err error) } -// NewPusher returns a new instance of an implementation conforming to Pusher interface. +// NewPusher creates a new Pusher interface that will push to either a v1 or v2 +// registry. The endpoint argument contains a Version field that determines +// whether a v1 or v2 pusher will be created. The other parameters are passed +// through to the underlying pusher implementation for use during the actual +// push operation. func (s *TagStore) NewPusher(endpoint registry.APIEndpoint, localRepo Repository, repoInfo *registry.RepositoryInfo, imagePushConfig *ImagePushConfig, sf *streamformatter.StreamFormatter) (Pusher, error) { switch endpoint.Version { case registry.APIVersion2: @@ -57,10 +65,10 @@ func (s *TagStore) NewPusher(endpoint registry.APIEndpoint, localRepo Repository return nil, fmt.Errorf("unknown version %d for registry %s", endpoint.Version, endpoint.URL) } -// FIXME: Allow to interrupt current push when new push of same image is done. - -// Push a image to the repo. +// Push initiates a push operation on the repository named localName. func (s *TagStore) Push(localName string, imagePushConfig *ImagePushConfig) error { + // FIXME: Allow to interrupt current push when new push of same image is done. + var sf = streamformatter.NewJSONStreamFormatter() // Resolve the Repository name from fqn to RepositoryInfo diff --git a/components/engine/graph/registry.go b/components/engine/graph/registry.go index 974235b21a..2898e00c78 100644 --- a/components/engine/graph/registry.go +++ b/components/engine/graph/registry.go @@ -27,7 +27,9 @@ func (dcs dumbCredentialStore) Basic(*url.URL) (string, string) { return dcs.auth.Username, dcs.auth.Password } -// NewV2Repository creates a v2 only repository. +// NewV2Repository returns a repository (v2 only). It creates a HTTP transport +// providing timeout settings and authentication support, and also verifies the +// remote API version. func NewV2Repository(repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, metaHeaders http.Header, authConfig *cliconfig.AuthConfig) (distribution.Repository, error) { ctx := context.Background() diff --git a/components/engine/graph/service.go b/components/engine/graph/service.go index 1ab7485e4c..1584479c66 100644 --- a/components/engine/graph/service.go +++ b/components/engine/graph/service.go @@ -10,6 +10,8 @@ import ( "github.com/docker/docker/api/types" ) +// lookupRaw looks up an image by name in a TagStore and returns the raw JSON +// describing the image. func (s *TagStore) lookupRaw(name string) ([]byte, error) { image, err := s.LookupImage(name) if err != nil || image == nil { @@ -24,7 +26,8 @@ func (s *TagStore) lookupRaw(name string) ([]byte, error) { return imageInspectRaw, nil } -// Lookup return an image encoded in JSON +// Lookup looks up an image by name in a TagStore and returns it as an +// ImageInspect structure. func (s *TagStore) Lookup(name string) (*types.ImageInspect, error) { image, err := s.LookupImage(name) if err != nil || image == nil { @@ -44,7 +47,7 @@ func (s *TagStore) Lookup(name string) (*types.ImageInspect, error) { Architecture: image.Architecture, Os: image.OS, Size: image.Size, - VirtualSize: s.graph.GetParentsSize(image, 0) + image.Size, + VirtualSize: s.graph.GetParentsSize(image) + image.Size, } imageInspect.GraphDriver.Name = s.graph.driver.String() diff --git a/components/engine/graph/tags.go b/components/engine/graph/tags.go index 7ea782fe96..f6a7bc9b4e 100644 --- a/components/engine/graph/tags.go +++ b/components/engine/graph/tags.go @@ -24,13 +24,13 @@ import ( "github.com/docker/libtrust" ) -// DefaultTag defines the default tag used when performing images related actions and no tag string is specified -const DefaultTag = "latest" - -// TagStore contains information to push and pull to the repo. +// TagStore manages repositories. It encompasses the Graph used for versioned +// storage, as well as various services involved in pushing and pulling +// repositories. type TagStore struct { - path string - graph *Graph + path string + graph *Graph + // Repositories is a map of repositories, indexed by name. Repositories map[string]Repository trustKey libtrust.PrivateKey sync.Mutex @@ -43,7 +43,7 @@ type TagStore struct { trustService *trust.TrustStore } -// Repository maps image id to image tag. +// Repository maps tags to image IDs. type Repository map[string]string // Update updates repository mapping with content of repository 'u'. @@ -53,7 +53,8 @@ func (r Repository) Update(u Repository) { } } -// Contains returns true if the contents of u Repository, are wholly contained in r Repository. +// Contains returns true if the contents of Repository u are wholly contained +// in Repository r. func (r Repository) Contains(u Repository) bool { for k, v := range u { // if u's key is not present in r OR u's key is present, but not the same value @@ -64,16 +65,23 @@ func (r Repository) Contains(u Repository) bool { return true } -// TagStoreConfig holds tag store configuration. +// TagStoreConfig provides parameters for a new TagStore. type TagStoreConfig struct { - Graph *Graph - Key libtrust.PrivateKey + // Graph is the versioned image store + Graph *Graph + // Key is the private key to use for signing manifests. + Key libtrust.PrivateKey + // Registry is the registry service to use for TLS configuration and + // endpoint lookup. Registry *registry.Service - Events *events.Events - Trust *trust.TrustStore + // Events is the events service to use for logging. + Events *events.Events + // Trust is the trust service to use for push and pull operations. + Trust *trust.TrustStore } -// NewTagStore creates a tag store to specified path. +// NewTagStore creates a new TagStore at specified path, using the parameters +// and services provided in cfg. func NewTagStore(path string, cfg *TagStoreConfig) (*TagStore, error) { abspath, err := filepath.Abs(path) if err != nil { @@ -126,13 +134,15 @@ func (store *TagStore) reload() error { return nil } -// LookupImage returns the image from the store. +// LookupImage returns pointer to an Image struct corresponding to the given +// name. The name can include an optional tag; otherwise the default tag will +// be used. 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 // (so we can pass all errors here) repoName, ref := parsers.ParseRepositoryTag(name) if ref == "" { - ref = DefaultTag + ref = tags.DefaultTag } var ( err error @@ -158,8 +168,8 @@ func (store *TagStore) LookupImage(name string) (*image.Image, error) { return img, nil } -// ByID returns a reverse-lookup table of all the names which refer to each image. -// Eg. {"43b5f19b10584": {"base:latest", "base:v1"}} +// ByID returns a reverse-lookup table of all the names which refer to each +// image - e.g. {"43b5f19b10584": {"base:latest", "base:v1"}} func (store *TagStore) ByID() map[string][]string { store.Lock() defer store.Unlock() @@ -178,7 +188,7 @@ func (store *TagStore) ByID() map[string][]string { return byID } -// ImageName returns name of the image. +// ImageName returns name of an image, given the image's ID. func (store *TagStore) ImageName(id string) string { if names, exists := store.ByID()[id]; exists && len(names) > 0 { return names[0] @@ -186,7 +196,7 @@ func (store *TagStore) ImageName(id string) string { return stringid.TruncateID(id) } -// DeleteAll removes images identified by a specific id from the store. +// DeleteAll removes images identified by a specific ID from the store. func (store *TagStore) DeleteAll(id string) error { names, exists := store.ByID()[id] if !exists || len(names) == 0 { @@ -207,7 +217,9 @@ func (store *TagStore) DeleteAll(id string) error { return nil } -// Delete removes a repo identified by a given name from the store +// Delete deletes a repository or a specific tag. If ref is empty, the entire +// repository named repoName will be deleted; otherwise only the tag named by +// ref will be deleted. func (store *TagStore) Delete(repoName, ref string) (bool, error) { store.Lock() defer store.Unlock() @@ -240,14 +252,16 @@ func (store *TagStore) Delete(repoName, ref string) (bool, error) { return deleted, store.save() } -// Tag adds a new tag to an existing image. +// Tag creates a tag in the repository reponame, pointing to the image named +// imageName. If force is true, an existing tag with the same name may be +// overwritten. func (store *TagStore) Tag(repoName, tag, imageName string, force bool) error { - return store.SetLoad(repoName, tag, imageName, force, nil) + return store.setLoad(repoName, tag, imageName, force, nil) } -// SetLoad stores the image to the store. +// setLoad stores the image to the store. // If the imageName is already in the repo then a '-f' flag should be used to replace existing image. -func (store *TagStore) SetLoad(repoName, tag, imageName string, force bool, out io.Writer) error { +func (store *TagStore) setLoad(repoName, tag, imageName string, force bool, out io.Writer) error { img, err := store.LookupImage(imageName) store.Lock() defer store.Unlock() @@ -255,7 +269,7 @@ func (store *TagStore) SetLoad(repoName, tag, imageName string, force bool, out return err } if tag == "" { - tag = tags.DEFAULTTAG + tag = tags.DefaultTag } if err := validateRepoName(repoName); err != nil { return err @@ -331,7 +345,7 @@ func (store *TagStore) SetDigest(repoName, digest, imageName string) error { return store.save() } -// Get returns a repo from the store. +// Get returns the Repository tag/image map for a given repository. func (store *TagStore) Get(repoName string) (Repository, error) { store.Lock() defer store.Unlock() @@ -345,7 +359,8 @@ func (store *TagStore) Get(repoName string) (Repository, error) { return nil, nil } -// GetImage returns an image from a given repo from the store. +// GetImage returns a pointer to an Image structure describing the image +// referred to by refOrID inside repository repoName. func (store *TagStore) GetImage(repoName, refOrID string) (*image.Image, error) { repo, err := store.Get(repoName) @@ -375,7 +390,8 @@ func (store *TagStore) GetImage(repoName, refOrID string) (*image.Image, error) return nil, nil } -// GetRepoRefs returns list of repos. +// GetRepoRefs returns a map with image IDs as keys, and slices listing +// repo/tag references as the values. It covers all repositories. func (store *TagStore) GetRepoRefs() map[string][]string { store.Lock() reporefs := make(map[string][]string) diff --git a/components/engine/graph/tags/tags.go b/components/engine/graph/tags/tags.go index cbd0f6bc94..b174b720c6 100644 --- a/components/engine/graph/tags/tags.go +++ b/components/engine/graph/tags/tags.go @@ -6,8 +6,12 @@ import ( "github.com/docker/distribution/registry/api/v2" ) -const DEFAULTTAG = "latest" +// DefaultTag is the default tag for the case where no explicit tag is +// specified. +const DefaultTag = "latest" +// ErrTagInvalidFormat is an error type used when the tag name has invalid +// characters or is longer than allowed. type ErrTagInvalidFormat struct { name string } diff --git a/components/engine/graph/tags_unit_test.go b/components/engine/graph/tags_unit_test.go index 5effb5c6fc..d711fe6599 100644 --- a/components/engine/graph/tags_unit_test.go +++ b/components/engine/graph/tags_unit_test.go @@ -11,6 +11,7 @@ import ( "github.com/docker/docker/daemon/events" "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/graph/tags" "github.com/docker/docker/image" "github.com/docker/docker/trust" "github.com/docker/docker/utils" @@ -119,17 +120,17 @@ func TestLookupImage(t *testing.T) { testOfficialImageName + ":" + testOfficialImageID, testOfficialImageName + ":" + testOfficialImageIDShort, testOfficialImageName, - testOfficialImageName + ":" + DefaultTag, + testOfficialImageName + ":" + tags.DefaultTag, "docker.io/" + testOfficialImageName, - "docker.io/" + testOfficialImageName + ":" + DefaultTag, + "docker.io/" + testOfficialImageName + ":" + tags.DefaultTag, "index.docker.io/" + testOfficialImageName, - "index.docker.io/" + testOfficialImageName + ":" + DefaultTag, + "index.docker.io/" + testOfficialImageName + ":" + tags.DefaultTag, "library/" + testOfficialImageName, - "library/" + testOfficialImageName + ":" + DefaultTag, + "library/" + testOfficialImageName + ":" + tags.DefaultTag, "docker.io/library/" + testOfficialImageName, - "docker.io/library/" + testOfficialImageName + ":" + DefaultTag, + "docker.io/library/" + testOfficialImageName + ":" + tags.DefaultTag, "index.docker.io/library/" + testOfficialImageName, - "index.docker.io/library/" + testOfficialImageName + ":" + DefaultTag, + "index.docker.io/library/" + testOfficialImageName + ":" + tags.DefaultTag, } privateLookups := []string{ @@ -138,7 +139,7 @@ func TestLookupImage(t *testing.T) { testPrivateImageName + ":" + testPrivateImageID, testPrivateImageName + ":" + testPrivateImageIDShort, testPrivateImageName, - testPrivateImageName + ":" + DefaultTag, + testPrivateImageName + ":" + tags.DefaultTag, } invalidLookups := []string{