/graph fix lin errors/warnings

Addresses #14756
Signed-off-by: Srini Brahmaroutu <srbrahma@us.ibm.com>
This commit is contained in:
Srini Brahmaroutu 2015-07-21 16:21:45 +00:00 committed by Srini Brahmaroutu
parent 0f85fadb4e
commit 1d6e443119
21 changed files with 140 additions and 87 deletions

View file

@ -27,7 +27,7 @@ func (daemon *Daemon) ContainerCreate(name string, config *runconfig.Config, hos
if daemon.Graph().IsNotExist(err, config.Image) { if daemon.Graph().IsNotExist(err, config.Image) {
_, tag := parsers.ParseRepositoryTag(config.Image) _, tag := parsers.ParseRepositoryTag(config.Image)
if tag == "" { if tag == "" {
tag = graph.DEFAULTTAG tag = graph.DefaultTag
} }
return "", warnings, fmt.Errorf("No such image: %s (tag: %s)", config.Image, tag) return "", warnings, fmt.Errorf("No such image: %s (tag: %s)", config.Image, tag)
} }

View file

@ -36,7 +36,7 @@ func (daemon *Daemon) imgDeleteHelper(name string, list *[]types.ImageDelete, fi
// FIXME: please respect DRY and centralize repo+tag parsing in a single central place! -- shykes // FIXME: please respect DRY and centralize repo+tag parsing in a single central place! -- shykes
repoName, tag = parsers.ParseRepositoryTag(name) repoName, tag = parsers.ParseRepositoryTag(name)
if tag == "" { if tag == "" {
tag = graph.DEFAULTTAG tag = graph.DefaultTag
} }
if name == "" { if name == "" {

View file

@ -13,16 +13,19 @@ import (
"github.com/docker/docker/registry" "github.com/docker/docker/registry"
) )
// CmdImageExport exports all images with the given tag. All versions // 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 // containing the same tag are exported. The resulting output is an
// uncompressed tar ball. // uncompressed tar ball.
// name is the set of tags to export.
// out is the writer where the images are written to.
type ImageExportConfig struct { type ImageExportConfig struct {
Names []string // Names is the set of tags to export.
Names []string
// OutStream is the writer where the images are written to.
Outstream io.Writer 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 { func (s *TagStore) ImageExport(imageExportConfig *ImageExportConfig) error {
// get image json // get image json
@ -135,7 +138,7 @@ func (s *TagStore) exportImage(name, tempdir string) error {
if err != nil { if err != nil {
return err return err
} }
imageInspectRaw, err := s.LookupRaw(n) imageInspectRaw, err := s.lookupRaw(n)
if err != nil { if err != nil {
return err return err
} }

View file

@ -152,6 +152,7 @@ func (graph *Graph) restore() error {
return nil 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 // FIXME: Implement error subclass instead of looking at the error text
// Note: This is the way golang implements os.IsNotExists on Plan9 // Note: This is the way golang implements os.IsNotExists on Plan9
func (graph *Graph) IsNotExist(err error, id string) bool { func (graph *Graph) IsNotExist(err error, id string) bool {
@ -414,7 +415,7 @@ func (graph *Graph) ByParent() map[string][]*image.Image {
return byParent return byParent
} }
// If the images and layers are in pulling chain, retain them. // 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. // If not, they may be deleted by rmi with dangling condition.
func (graph *Graph) Retain(sessionID string, layerIDs ...string) { func (graph *Graph) Retain(sessionID string, layerIDs ...string) {
graph.retained.Add(sessionID, layerIDs) graph.retained.Add(sessionID, layerIDs)

View file

@ -16,10 +16,9 @@ import (
"github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/system"
) )
// setupInitLayer populates a directory with mountpoints suitable // SetupInitLayer populates a directory with mountpoints suitable
// for bind-mounting dockerinit into the container. The mountpoint is simply an // for bind-mounting dockerinit into the container. The mountpoint is simply an
// empty file at /.dockerinit // empty file at /.dockerinit
//
// This extra layer is used by all containers as the top-most ro layer. It protects // This extra layer is used by all containers as the top-most ro layer. It protects
// the container from unwanted side-effects on the rw layer. // the container from unwanted side-effects on the rw layer.
func SetupInitLayer(initLayer string) error { func SetupInitLayer(initLayer string) error {

View file

@ -13,7 +13,7 @@ import (
"github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/archive"
) )
// setupInitLayer populates a directory with mountpoints suitable // SetupInitLayer populates a directory with mountpoints suitable
// for bind-mounting dockerinit into the container. T // for bind-mounting dockerinit into the container. T
func SetupInitLayer(initLayer string) error { func SetupInitLayer(initLayer string) error {
return nil return nil
@ -106,33 +106,32 @@ func (graph *Graph) storeImage(img *image.Image, layerData archive.ArchiveReader
defer f.Close() defer f.Close()
return json.NewEncoder(f).Encode(img)
} else {
// We keep this functionality here so that we can still work with the
// VFS driver during development. This will not be used for actual running
// of Windows containers. Without this code, it would not be possible to
// docker pull using the VFS driver.
// Store the layer. If layerData is not nil, unpack it into the new layer
if layerData != nil {
if err := graph.disassembleAndApplyTarLayer(img, layerData, root); err != nil {
return err
}
}
if err := graph.saveSize(root, int(img.Size)); err != nil {
return err
}
f, err := os.OpenFile(jsonPath(root), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
if err != nil {
return err
}
defer f.Close()
return json.NewEncoder(f).Encode(img) return json.NewEncoder(f).Encode(img)
} }
// We keep this functionality here so that we can still work with the
// VFS driver during development. This will not be used for actual running
// of Windows containers. Without this code, it would not be possible to
// docker pull using the VFS driver.
// Store the layer. If layerData is not nil, unpack it into the new layer
if layerData != nil {
if err := graph.disassembleAndApplyTarLayer(img, layerData, root); err != nil {
return err
}
}
if err := graph.saveSize(root, int(img.Size)); err != nil {
return err
}
f, err := os.OpenFile(jsonPath(root), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
if err != nil {
return err
}
defer f.Close()
return json.NewEncoder(f).Encode(img)
} }
// TarLayer returns a tar archive of the image's filesystem layer. // TarLayer returns a tar archive of the image's filesystem layer.
@ -152,15 +151,14 @@ func (graph *Graph) TarLayer(img *image.Image) (arch archive.Archive, err error)
} }
return wd.Export(img.ID, wd.LayerIdsToPaths(ids)) return wd.Export(img.ID, wd.LayerIdsToPaths(ids))
} else {
// We keep this functionality here so that we can still work with the VFS
// driver during development. VFS is not supported (and just will not work)
// for Windows containers.
rdr, err := graph.assembleTarLayer(img)
if err != nil {
logrus.Debugf("[graph] TarLayer with traditional differ: %s", img.ID)
return graph.driver.Diff(img.ID, img.Parent)
}
return rdr, nil
} }
// We keep this functionality here so that we can still work with the VFS
// driver during development. VFS is not supported (and just will not work)
// for Windows containers.
rdr, err := graph.assembleTarLayer(img)
if err != nil {
logrus.Debugf("[graph] TarLayer with traditional differ: %s", img.ID)
return graph.driver.Diff(img.ID, img.Parent)
}
return rdr, nil
} }

View file

@ -67,6 +67,7 @@ func (graph *Graph) CheckDepth(img *image.Image) error {
return nil return nil
} }
// History returns a list of ImageHistory for the specified image name by walking the image lineage.
func (s *TagStore) History(name string) ([]*types.ImageHistory, error) { func (s *TagStore) History(name string) ([]*types.ImageHistory, error) {
foundImage, err := s.LookupImage(name) foundImage, err := s.LookupImage(name)
if err != nil { if err != nil {
@ -101,6 +102,7 @@ func (s *TagStore) History(name string) ([]*types.ImageHistory, error) {
return history, err return history, err
} }
// GetParent returns the parent image.
func (graph *Graph) GetParent(img *image.Image) (*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
@ -108,6 +110,7 @@ func (graph *Graph) GetParent(img *image.Image) (*image.Image, error) {
return graph.Get(img.Parent) return graph.Get(img.Parent)
} }
// GetParentsSize returns the size of the parent.
func (graph *Graph) GetParentsSize(img *image.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 {

View file

@ -13,13 +13,21 @@ import (
"github.com/docker/docker/utils" "github.com/docker/docker/utils"
) )
// ImageImportConfig holds configuration to import a image.
type ImageImportConfig struct { type ImageImportConfig struct {
Changes []string // Changes are the container changes written to top layer.
InConfig io.ReadCloser Changes []string
OutStream io.Writer // 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 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 { func (s *TagStore) Import(src string, repo string, tag string, imageImportConfig *ImageImportConfig) error {
var ( var (
sf = streamformatter.NewJSONStreamFormatter() sf = streamformatter.NewJSONStreamFormatter()

View file

@ -18,18 +18,24 @@ var acceptedImageFilterTags = map[string]struct{}{
"label": {}, "label": {},
} }
// ImagesConfig defines the criteria to obtain a list of images.
type ImagesConfig struct { type ImagesConfig struct {
// Filters is supported list of filters used to get list of images.
Filters string Filters string
Filter string // Filter the list of images by name.
All bool Filter string
// All inditest that all the images will be returned in the list, if set to true.
All bool
} }
type ByCreated []*types.Image // byCreated is a temporary type used to sort list of images on their field 'Created'.
type byCreated []*types.Image
func (r ByCreated) Len() int { return len(r) } 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) 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 } 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) { func (s *TagStore) Images(config *ImagesConfig) ([]*types.Image, error) {
var ( var (
allImages map[string]*image.Image allImages map[string]*image.Image
@ -144,7 +150,7 @@ func (s *TagStore) Images(config *ImagesConfig) ([]*types.Image, error) {
} }
} }
sort.Sort(sort.Reverse(ByCreated(images))) sort.Sort(sort.Reverse(byCreated(images)))
return images, nil return images, nil
} }

View file

@ -15,7 +15,7 @@ import (
"github.com/docker/docker/pkg/chrootarchive" "github.com/docker/docker/pkg/chrootarchive"
) )
// Loads a set of images into the repository. This is the complementary of ImageExport. // Load uploads a set of images into the repository. This is the complementary of ImageExport.
// The input stream is an uncompressed tar ball containing images and metadata. // The input stream is an uncompressed tar ball containing images and metadata.
func (s *TagStore) Load(inTar io.ReadCloser, outStream io.Writer) error { func (s *TagStore) Load(inTar io.ReadCloser, outStream io.Writer) error {
tmpImageDir, err := ioutil.TempDir("", "docker-import-") tmpImageDir, err := ioutil.TempDir("", "docker-import-")
@ -84,7 +84,7 @@ func (s *TagStore) recursiveLoad(address, tmpImageDir string) error {
if _, err := s.LookupImage(address); err != nil { if _, err := s.LookupImage(address); err != nil {
logrus.Debugf("Loading %s", address) logrus.Debugf("Loading %s", address)
imageJson, err := ioutil.ReadFile(filepath.Join(tmpImageDir, "repo", address, "json")) imageJSON, err := ioutil.ReadFile(filepath.Join(tmpImageDir, "repo", address, "json"))
if err != nil { if err != nil {
logrus.Debugf("Error reading json: %v", err) logrus.Debugf("Error reading json: %v", err)
return err return err
@ -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 := image.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

View file

@ -7,6 +7,8 @@ import (
"io" "io"
) )
// Load method is implemented here for non-linux and non-windows platforms and
// may return an error indicating that image load is not supported on other platforms.
func (s *TagStore) Load(inTar io.ReadCloser, outStream io.Writer) error { func (s *TagStore) Load(inTar io.ReadCloser, outStream io.Writer) error {
return fmt.Errorf("Load is not supported on this platform") return fmt.Errorf("Load is not supported on this platform")
} }

View file

@ -11,12 +11,17 @@ import (
"github.com/docker/docker/utils" "github.com/docker/docker/utils"
) )
// ImagePullConfig stores pull configuration.
type ImagePullConfig struct { type ImagePullConfig struct {
// MetaHeaders store meta data about the image (DockerHeaders with prefix X-Meta- in the request).
MetaHeaders map[string][]string MetaHeaders map[string][]string
AuthConfig *cliconfig.AuthConfig // AuthConfig holds authentication information for authorizing with the registry.
OutStream io.Writer AuthConfig *cliconfig.AuthConfig
// OutStream is the output writer for showing the status of the pull operation.
OutStream io.Writer
} }
// Puller is an interface to define Pull behavior.
type Puller interface { type Puller interface {
// Pull tries to pull the image referenced by `tag` // 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. // Pull returns an error if any, as well as a boolean that determines whether to retry Pull on the next configured endpoint.
@ -25,6 +30,7 @@ type Puller interface {
Pull(tag string) (fallback bool, err error) Pull(tag string) (fallback bool, err error)
} }
// NewPuller returns a new instance of an implementation conforming to Puller interface.
func NewPuller(s *TagStore, endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, imagePullConfig *ImagePullConfig, sf *streamformatter.StreamFormatter) (Puller, error) { func NewPuller(s *TagStore, endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, imagePullConfig *ImagePullConfig, sf *streamformatter.StreamFormatter) (Puller, error) {
switch endpoint.Version { switch endpoint.Version {
case registry.APIVersion2: case registry.APIVersion2:
@ -47,6 +53,7 @@ func NewPuller(s *TagStore, endpoint registry.APIEndpoint, repoInfo *registry.Re
return nil, fmt.Errorf("unknown version %d for registry %s", endpoint.Version, endpoint.URL) 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.
func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConfig) error { func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConfig) error {
var sf = streamformatter.NewJSONStreamFormatter() var sf = streamformatter.NewJSONStreamFormatter()
@ -126,7 +133,8 @@ func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConf
return lastErr return lastErr
} }
func WriteStatus(requestedTag string, out io.Writer, sf *streamformatter.StreamFormatter, layersDownloaded bool) { // writeStatus shows status of the pull command.
func writeStatus(requestedTag string, out io.Writer, sf *streamformatter.StreamFormatter, layersDownloaded bool) {
if layersDownloaded { if layersDownloaded {
out.Write(sf.FormatStatus("", "Status: Downloaded newer image for %s", requestedTag)) out.Write(sf.FormatStatus("", "Status: Downloaded newer image for %s", requestedTag))
} else { } else {

View file

@ -80,9 +80,9 @@ func (p *v1Puller) pullRepository(askedTag string) error {
if askedTag == "" { if askedTag == "" {
tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo.RemoteName) tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo.RemoteName)
} else { } else {
var tagId string var tagID string
tagId, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo.RemoteName, askedTag) tagID, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo.RemoteName, askedTag)
tagsList[askedTag] = tagId tagsList[askedTag] = tagID
} }
if err != nil { if err != nil {
if err == registry.ErrRepoNotFound && askedTag != "" { if err == registry.ErrRepoNotFound && askedTag != "" {
@ -222,7 +222,7 @@ func (p *v1Puller) pullRepository(askedTag string) error {
if len(askedTag) > 0 { if len(askedTag) > 0 {
requestedTag = utils.ImageReference(p.repoInfo.LocalName, askedTag) requestedTag = utils.ImageReference(p.repoInfo.LocalName, askedTag)
} }
WriteStatus(requestedTag, out, p.sf, layersDownloaded) writeStatus(requestedTag, out, p.sf, layersDownloaded)
return nil return nil
} }

View file

@ -95,7 +95,7 @@ func (p *v2Puller) pullV2Repository(tag string) (err error) {
layersDownloaded = layersDownloaded || pulledNew layersDownloaded = layersDownloaded || pulledNew
} }
WriteStatus(taggedName, p.config.OutStream, p.sf, layersDownloaded) writeStatus(taggedName, p.config.OutStream, p.sf, layersDownloaded)
return nil return nil
} }

View file

@ -10,13 +10,19 @@ import (
"github.com/docker/docker/registry" "github.com/docker/docker/registry"
) )
// ImagePushConfig stores push configuration.
type ImagePushConfig struct { type ImagePushConfig struct {
// MetaHeaders store meta data about the image (DockerHeaders with prefix X-Meta- in the request).
MetaHeaders map[string][]string MetaHeaders map[string][]string
AuthConfig *cliconfig.AuthConfig // AuthConfig holds authentication information for authorizing with the registry.
Tag string AuthConfig *cliconfig.AuthConfig
OutStream io.Writer // 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 string
// OutStream is the output writer for showing the status of the push operation.
OutStream io.Writer
} }
// Pusher is an interface to define Push behavior.
type Pusher interface { type Pusher interface {
// Push tries to push the image configured at the creation of Pusher. // 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. // Push returns an error if any, as well as a boolean that determines whether to retry Push on the next configured endpoint.
@ -25,6 +31,7 @@ type Pusher interface {
Push() (fallback bool, err error) Push() (fallback bool, err error)
} }
// NewPusher returns a new instance of an implementation conforming to Pusher interface.
func (s *TagStore) NewPusher(endpoint registry.APIEndpoint, localRepo Repository, repoInfo *registry.RepositoryInfo, imagePushConfig *ImagePushConfig, sf *streamformatter.StreamFormatter) (Pusher, error) { func (s *TagStore) NewPusher(endpoint registry.APIEndpoint, localRepo Repository, repoInfo *registry.RepositoryInfo, imagePushConfig *ImagePushConfig, sf *streamformatter.StreamFormatter) (Pusher, error) {
switch endpoint.Version { switch endpoint.Version {
case registry.APIVersion2: case registry.APIVersion2:
@ -51,6 +58,8 @@ func (s *TagStore) NewPusher(endpoint registry.APIEndpoint, localRepo Repository
} }
// FIXME: Allow to interrupt current push when new push of same image is done. // FIXME: Allow to interrupt current push when new push of same image is done.
// Push a image to the repo.
func (s *TagStore) Push(localName string, imagePushConfig *ImagePushConfig) error { func (s *TagStore) Push(localName string, imagePushConfig *ImagePushConfig) error {
var sf = streamformatter.NewJSONStreamFormatter() var sf = streamformatter.NewJSONStreamFormatter()

View file

@ -87,12 +87,12 @@ func (p *v2Pusher) pushV2Repository(tag string) error {
func (p *v2Pusher) pushV2Tag(tag string) error { func (p *v2Pusher) pushV2Tag(tag string) error {
logrus.Debugf("Pushing repository: %s:%s", p.repo.Name(), tag) logrus.Debugf("Pushing repository: %s:%s", p.repo.Name(), tag)
layerId, exists := p.localRepo[tag] layerID, exists := p.localRepo[tag]
if !exists { if !exists {
return fmt.Errorf("tag does not exist: %s", tag) return fmt.Errorf("tag does not exist: %s", tag)
} }
layer, err := p.graph.Get(layerId) layer, err := p.graph.Get(layerID)
if err != nil { if err != nil {
return err return err
} }

View file

@ -27,7 +27,7 @@ func (dcs dumbCredentialStore) Basic(*url.URL) (string, string) {
return dcs.auth.Username, dcs.auth.Password return dcs.auth.Username, dcs.auth.Password
} }
// v2 only // NewV2Repository creates a v2 only repository.
func NewV2Repository(repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, metaHeaders http.Header, authConfig *cliconfig.AuthConfig) (distribution.Repository, error) { func NewV2Repository(repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, metaHeaders http.Header, authConfig *cliconfig.AuthConfig) (distribution.Repository, error) {
ctx := context.Background() ctx := context.Background()

View file

@ -10,7 +10,7 @@ import (
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
) )
func (s *TagStore) LookupRaw(name string) ([]byte, error) { func (s *TagStore) lookupRaw(name string) ([]byte, error) {
image, err := s.LookupImage(name) image, err := s.LookupImage(name)
if err != nil || image == nil { if err != nil || image == nil {
return nil, fmt.Errorf("No such image %s", name) return nil, fmt.Errorf("No such image %s", name)

View file

@ -24,8 +24,10 @@ import (
"github.com/docker/libtrust" "github.com/docker/libtrust"
) )
const DEFAULTTAG = "latest" // 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.
type TagStore struct { type TagStore struct {
path string path string
graph *Graph graph *Graph
@ -41,16 +43,17 @@ type TagStore struct {
trustService *trust.TrustStore trustService *trust.TrustStore
} }
// Repository maps image id to image tag.
type Repository map[string]string type Repository map[string]string
// update Repository mapping with content of u // Update updates repository mapping with content of repository 'u'.
func (r Repository) Update(u Repository) { func (r Repository) Update(u Repository) {
for k, v := range u { for k, v := range u {
r[k] = v r[k] = v
} }
} }
// return true if the contents of u Repository, are wholly contained in r Repository // Contains returns true if the contents of u Repository, are wholly contained in r Repository.
func (r Repository) Contains(u Repository) bool { func (r Repository) Contains(u Repository) bool {
for k, v := range u { 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 // if u's key is not present in r OR u's key is present, but not the same value
@ -61,6 +64,7 @@ func (r Repository) Contains(u Repository) bool {
return true return true
} }
// TagStoreConfig holds tag store configuration.
type TagStoreConfig struct { type TagStoreConfig struct {
Graph *Graph Graph *Graph
Key libtrust.PrivateKey Key libtrust.PrivateKey
@ -69,6 +73,7 @@ type TagStoreConfig struct {
Trust *trust.TrustStore Trust *trust.TrustStore
} }
// NewTagStore creates a tag store to specified path.
func NewTagStore(path string, cfg *TagStoreConfig) (*TagStore, error) { func NewTagStore(path string, cfg *TagStoreConfig) (*TagStore, error) {
abspath, err := filepath.Abs(path) abspath, err := filepath.Abs(path)
if err != nil { if err != nil {
@ -121,12 +126,13 @@ func (store *TagStore) reload() error {
return nil return nil
} }
// LookupImage returns the image from the store.
func (store *TagStore) LookupImage(name string) (*image.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)
if ref == "" { if ref == "" {
ref = DEFAULTTAG ref = DefaultTag
} }
var ( var (
err error err error
@ -152,7 +158,7 @@ func (store *TagStore) LookupImage(name string) (*image.Image, error) {
return img, nil return img, nil
} }
// Return a reverse-lookup table of all the names which refer to each image // ByID returns a reverse-lookup table of all the names which refer to each image.
// Eg. {"43b5f19b10584": {"base:latest", "base:v1"}} // Eg. {"43b5f19b10584": {"base:latest", "base:v1"}}
func (store *TagStore) ByID() map[string][]string { func (store *TagStore) ByID() map[string][]string {
store.Lock() store.Lock()
@ -172,6 +178,7 @@ func (store *TagStore) ByID() map[string][]string {
return byID return byID
} }
// ImageName returns name of the image.
func (store *TagStore) ImageName(id string) string { func (store *TagStore) ImageName(id string) string {
if names, exists := store.ByID()[id]; exists && len(names) > 0 { if names, exists := store.ByID()[id]; exists && len(names) > 0 {
return names[0] return names[0]
@ -179,6 +186,7 @@ func (store *TagStore) ImageName(id string) string {
return stringid.TruncateID(id) return stringid.TruncateID(id)
} }
// DeleteAll removes images identified by a specific id from the store.
func (store *TagStore) DeleteAll(id string) error { func (store *TagStore) DeleteAll(id string) error {
names, exists := store.ByID()[id] names, exists := store.ByID()[id]
if !exists || len(names) == 0 { if !exists || len(names) == 0 {
@ -199,6 +207,7 @@ func (store *TagStore) DeleteAll(id string) error {
return nil return nil
} }
// Delete removes a repo identified by a given name from the store
func (store *TagStore) Delete(repoName, ref string) (bool, error) { func (store *TagStore) Delete(repoName, ref string) (bool, error) {
store.Lock() store.Lock()
defer store.Unlock() defer store.Unlock()
@ -231,10 +240,13 @@ func (store *TagStore) Delete(repoName, ref string) (bool, error) {
return deleted, store.save() return deleted, store.save()
} }
// Tag adds a new tag to an existing image.
func (store *TagStore) Tag(repoName, tag, imageName string, force bool) error { 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.
// 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) img, err := store.LookupImage(imageName)
store.Lock() store.Lock()
@ -319,6 +331,7 @@ func (store *TagStore) SetDigest(repoName, digest, imageName string) error {
return store.save() return store.save()
} }
// Get returns a repo from the store.
func (store *TagStore) Get(repoName string) (Repository, error) { func (store *TagStore) Get(repoName string) (Repository, error) {
store.Lock() store.Lock()
defer store.Unlock() defer store.Unlock()
@ -332,6 +345,7 @@ func (store *TagStore) Get(repoName string) (Repository, error) {
return nil, nil return nil, nil
} }
// GetImage returns an image from a given repo from the store.
func (store *TagStore) GetImage(repoName, refOrID string) (*image.Image, error) { func (store *TagStore) GetImage(repoName, refOrID string) (*image.Image, error) {
repo, err := store.Get(repoName) repo, err := store.Get(repoName)
@ -361,6 +375,7 @@ func (store *TagStore) GetImage(repoName, refOrID string) (*image.Image, error)
return nil, nil return nil, nil
} }
// GetRepoRefs returns list of repos.
func (store *TagStore) GetRepoRefs() map[string][]string { func (store *TagStore) GetRepoRefs() map[string][]string {
store.Lock() store.Lock()
reporefs := make(map[string][]string) reporefs := make(map[string][]string)
@ -375,7 +390,7 @@ func (store *TagStore) GetRepoRefs() map[string][]string {
return reporefs return reporefs
} }
// Validate the name of a repository // validateRepoName validates the name of a repository.
func validateRepoName(name string) error { func validateRepoName(name string) error {
if name == "" { if name == "" {
return fmt.Errorf("Repository name can't be empty") return fmt.Errorf("Repository name can't be empty")

View file

@ -119,17 +119,17 @@ func TestLookupImage(t *testing.T) {
testOfficialImageName + ":" + testOfficialImageID, testOfficialImageName + ":" + testOfficialImageID,
testOfficialImageName + ":" + testOfficialImageIDShort, testOfficialImageName + ":" + testOfficialImageIDShort,
testOfficialImageName, testOfficialImageName,
testOfficialImageName + ":" + DEFAULTTAG, testOfficialImageName + ":" + DefaultTag,
"docker.io/" + testOfficialImageName, "docker.io/" + testOfficialImageName,
"docker.io/" + testOfficialImageName + ":" + DEFAULTTAG, "docker.io/" + testOfficialImageName + ":" + DefaultTag,
"index.docker.io/" + testOfficialImageName, "index.docker.io/" + testOfficialImageName,
"index.docker.io/" + testOfficialImageName + ":" + DEFAULTTAG, "index.docker.io/" + testOfficialImageName + ":" + DefaultTag,
"library/" + testOfficialImageName, "library/" + testOfficialImageName,
"library/" + testOfficialImageName + ":" + DEFAULTTAG, "library/" + testOfficialImageName + ":" + DefaultTag,
"docker.io/library/" + testOfficialImageName, "docker.io/library/" + testOfficialImageName,
"docker.io/library/" + testOfficialImageName + ":" + DEFAULTTAG, "docker.io/library/" + testOfficialImageName + ":" + DefaultTag,
"index.docker.io/library/" + testOfficialImageName, "index.docker.io/library/" + testOfficialImageName,
"index.docker.io/library/" + testOfficialImageName + ":" + DEFAULTTAG, "index.docker.io/library/" + testOfficialImageName + ":" + DefaultTag,
} }
privateLookups := []string{ privateLookups := []string{
@ -138,7 +138,7 @@ func TestLookupImage(t *testing.T) {
testPrivateImageName + ":" + testPrivateImageID, testPrivateImageName + ":" + testPrivateImageID,
testPrivateImageName + ":" + testPrivateImageIDShort, testPrivateImageName + ":" + testPrivateImageIDShort,
testPrivateImageName, testPrivateImageName,
testPrivateImageName + ":" + DEFAULTTAG, testPrivateImageName + ":" + DefaultTag,
} }
invalidLookups := []string{ invalidLookups := []string{

View file

@ -25,6 +25,7 @@ packages=(
daemon/network daemon/network
docker docker
dockerinit dockerinit
graph
image image
integration-cli integration-cli
pkg/chrootarchive pkg/chrootarchive