github.com/slava-ustovytski/docker@v1.8.2-rc1/graph/tags.go (about)

     1  package graph
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"sort"
    12  	"strings"
    13  	"sync"
    14  
    15  	"github.com/docker/distribution/digest"
    16  	"github.com/docker/docker/daemon/events"
    17  	"github.com/docker/docker/graph/tags"
    18  	"github.com/docker/docker/image"
    19  	"github.com/docker/docker/pkg/parsers"
    20  	"github.com/docker/docker/pkg/stringid"
    21  	"github.com/docker/docker/registry"
    22  	"github.com/docker/docker/trust"
    23  	"github.com/docker/docker/utils"
    24  	"github.com/docker/libtrust"
    25  )
    26  
    27  const DEFAULTTAG = "latest"
    28  
    29  type TagStore struct {
    30  	path         string
    31  	graph        *Graph
    32  	Repositories map[string]Repository
    33  	trustKey     libtrust.PrivateKey
    34  	sync.Mutex
    35  	// FIXME: move push/pull-related fields
    36  	// to a helper type
    37  	pullingPool     map[string]chan struct{}
    38  	pushingPool     map[string]chan struct{}
    39  	registryService *registry.Service
    40  	eventsService   *events.Events
    41  	trustService    *trust.TrustStore
    42  }
    43  
    44  type Repository map[string]string
    45  
    46  // update Repository mapping with content of u
    47  func (r Repository) Update(u Repository) {
    48  	for k, v := range u {
    49  		r[k] = v
    50  	}
    51  }
    52  
    53  // return true if the contents of u Repository, are wholly contained in r Repository
    54  func (r Repository) Contains(u Repository) bool {
    55  	for k, v := range u {
    56  		// if u's key is not present in r OR u's key is present, but not the same value
    57  		if rv, ok := r[k]; !ok || (ok && rv != v) {
    58  			return false
    59  		}
    60  	}
    61  	return true
    62  }
    63  
    64  type TagStoreConfig struct {
    65  	Graph    *Graph
    66  	Key      libtrust.PrivateKey
    67  	Registry *registry.Service
    68  	Events   *events.Events
    69  	Trust    *trust.TrustStore
    70  }
    71  
    72  func NewTagStore(path string, cfg *TagStoreConfig) (*TagStore, error) {
    73  	abspath, err := filepath.Abs(path)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	store := &TagStore{
    79  		path:            abspath,
    80  		graph:           cfg.Graph,
    81  		trustKey:        cfg.Key,
    82  		Repositories:    make(map[string]Repository),
    83  		pullingPool:     make(map[string]chan struct{}),
    84  		pushingPool:     make(map[string]chan struct{}),
    85  		registryService: cfg.Registry,
    86  		eventsService:   cfg.Events,
    87  		trustService:    cfg.Trust,
    88  	}
    89  	// Load the json file if it exists, otherwise create it.
    90  	if err := store.reload(); os.IsNotExist(err) {
    91  		if err := store.save(); err != nil {
    92  			return nil, err
    93  		}
    94  	} else if err != nil {
    95  		return nil, err
    96  	}
    97  	return store, nil
    98  }
    99  
   100  func (store *TagStore) save() error {
   101  	// Store the json ball
   102  	jsonData, err := json.Marshal(store)
   103  	if err != nil {
   104  		return err
   105  	}
   106  	if err := ioutil.WriteFile(store.path, jsonData, 0600); err != nil {
   107  		return err
   108  	}
   109  	return nil
   110  }
   111  
   112  func (store *TagStore) reload() error {
   113  	f, err := os.Open(store.path)
   114  	if err != nil {
   115  		return err
   116  	}
   117  	defer f.Close()
   118  	if err := json.NewDecoder(f).Decode(&store); err != nil {
   119  		return err
   120  	}
   121  	return nil
   122  }
   123  
   124  func (store *TagStore) LookupImage(name string) (*image.Image, error) {
   125  	// FIXME: standardize on returning nil when the image doesn't exist, and err for everything else
   126  	// (so we can pass all errors here)
   127  	repoName, ref := parsers.ParseRepositoryTag(name)
   128  	if ref == "" {
   129  		ref = DEFAULTTAG
   130  	}
   131  	var (
   132  		err error
   133  		img *image.Image
   134  	)
   135  
   136  	img, err = store.GetImage(repoName, ref)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  
   141  	if img != nil {
   142  		return img, err
   143  	}
   144  
   145  	// name must be an image ID.
   146  	store.Lock()
   147  	defer store.Unlock()
   148  	if img, err = store.graph.Get(name); err != nil {
   149  		return nil, err
   150  	}
   151  
   152  	return img, nil
   153  }
   154  
   155  // Return a reverse-lookup table of all the names which refer to each image
   156  // Eg. {"43b5f19b10584": {"base:latest", "base:v1"}}
   157  func (store *TagStore) ByID() map[string][]string {
   158  	store.Lock()
   159  	defer store.Unlock()
   160  	byID := make(map[string][]string)
   161  	for repoName, repository := range store.Repositories {
   162  		for tag, id := range repository {
   163  			name := utils.ImageReference(repoName, tag)
   164  			if _, exists := byID[id]; !exists {
   165  				byID[id] = []string{name}
   166  			} else {
   167  				byID[id] = append(byID[id], name)
   168  				sort.Strings(byID[id])
   169  			}
   170  		}
   171  	}
   172  	return byID
   173  }
   174  
   175  func (store *TagStore) ImageName(id string) string {
   176  	if names, exists := store.ByID()[id]; exists && len(names) > 0 {
   177  		return names[0]
   178  	}
   179  	return stringid.TruncateID(id)
   180  }
   181  
   182  func (store *TagStore) DeleteAll(id string) error {
   183  	names, exists := store.ByID()[id]
   184  	if !exists || len(names) == 0 {
   185  		return nil
   186  	}
   187  	for _, name := range names {
   188  		if strings.Contains(name, ":") {
   189  			nameParts := strings.Split(name, ":")
   190  			if _, err := store.Delete(nameParts[0], nameParts[1]); err != nil {
   191  				return err
   192  			}
   193  		} else {
   194  			if _, err := store.Delete(name, ""); err != nil {
   195  				return err
   196  			}
   197  		}
   198  	}
   199  	return nil
   200  }
   201  
   202  func (store *TagStore) Delete(repoName, ref string) (bool, error) {
   203  	store.Lock()
   204  	defer store.Unlock()
   205  	deleted := false
   206  	if err := store.reload(); err != nil {
   207  		return false, err
   208  	}
   209  
   210  	repoName = registry.NormalizeLocalName(repoName)
   211  
   212  	if ref == "" {
   213  		// Delete the whole repository.
   214  		delete(store.Repositories, repoName)
   215  		return true, store.save()
   216  	}
   217  
   218  	repoRefs, exists := store.Repositories[repoName]
   219  	if !exists {
   220  		return false, fmt.Errorf("No such repository: %s", repoName)
   221  	}
   222  
   223  	if _, exists := repoRefs[ref]; exists {
   224  		delete(repoRefs, ref)
   225  		if len(repoRefs) == 0 {
   226  			delete(store.Repositories, repoName)
   227  		}
   228  		deleted = true
   229  	}
   230  
   231  	return deleted, store.save()
   232  }
   233  
   234  func (store *TagStore) Tag(repoName, tag, imageName string, force bool) error {
   235  	return store.SetLoad(repoName, tag, imageName, force, nil)
   236  }
   237  
   238  func (store *TagStore) SetLoad(repoName, tag, imageName string, force bool, out io.Writer) error {
   239  	img, err := store.LookupImage(imageName)
   240  	store.Lock()
   241  	defer store.Unlock()
   242  	if err != nil {
   243  		return err
   244  	}
   245  	if tag == "" {
   246  		tag = tags.DEFAULTTAG
   247  	}
   248  	if err := validateRepoName(repoName); err != nil {
   249  		return err
   250  	}
   251  	if err := tags.ValidateTagName(tag); err != nil {
   252  		if _, formatError := err.(tags.ErrTagInvalidFormat); !formatError {
   253  			return err
   254  		}
   255  		if _, dErr := digest.ParseDigest(tag); dErr != nil {
   256  			// Still return the tag validation error.
   257  			// It's more likely to be a user generated issue.
   258  			return err
   259  		}
   260  	}
   261  	if err := store.reload(); err != nil {
   262  		return err
   263  	}
   264  	var repo Repository
   265  	repoName = registry.NormalizeLocalName(repoName)
   266  	if r, exists := store.Repositories[repoName]; exists {
   267  		repo = r
   268  		if old, exists := store.Repositories[repoName][tag]; exists {
   269  
   270  			if !force {
   271  				return fmt.Errorf("Conflict: Tag %s is already set to image %s, if you want to replace it, please use -f option", tag, old)
   272  			}
   273  
   274  			if old != img.ID && out != nil {
   275  
   276  				fmt.Fprintf(out, "The image %s:%s already exists, renaming the old one with ID %s to empty string\n", repoName, tag, old[:12])
   277  
   278  			}
   279  		}
   280  	} else {
   281  		repo = make(map[string]string)
   282  		store.Repositories[repoName] = repo
   283  	}
   284  	repo[tag] = img.ID
   285  	return store.save()
   286  }
   287  
   288  // SetDigest creates a digest reference to an image ID.
   289  func (store *TagStore) SetDigest(repoName, digest, imageName string) error {
   290  	img, err := store.LookupImage(imageName)
   291  	if err != nil {
   292  		return err
   293  	}
   294  
   295  	if err := validateRepoName(repoName); err != nil {
   296  		return err
   297  	}
   298  
   299  	if err := validateDigest(digest); err != nil {
   300  		return err
   301  	}
   302  
   303  	store.Lock()
   304  	defer store.Unlock()
   305  	if err := store.reload(); err != nil {
   306  		return err
   307  	}
   308  
   309  	repoName = registry.NormalizeLocalName(repoName)
   310  	repoRefs, exists := store.Repositories[repoName]
   311  	if !exists {
   312  		repoRefs = Repository{}
   313  		store.Repositories[repoName] = repoRefs
   314  	} else if oldID, exists := repoRefs[digest]; exists && oldID != img.ID {
   315  		return fmt.Errorf("Conflict: Digest %s is already set to image %s", digest, oldID)
   316  	}
   317  
   318  	repoRefs[digest] = img.ID
   319  	return store.save()
   320  }
   321  
   322  func (store *TagStore) Get(repoName string) (Repository, error) {
   323  	store.Lock()
   324  	defer store.Unlock()
   325  	if err := store.reload(); err != nil {
   326  		return nil, err
   327  	}
   328  	repoName = registry.NormalizeLocalName(repoName)
   329  	if r, exists := store.Repositories[repoName]; exists {
   330  		return r, nil
   331  	}
   332  	return nil, nil
   333  }
   334  
   335  func (store *TagStore) GetImage(repoName, refOrID string) (*image.Image, error) {
   336  	repo, err := store.Get(repoName)
   337  
   338  	if err != nil {
   339  		return nil, err
   340  	}
   341  	if repo == nil {
   342  		return nil, nil
   343  	}
   344  
   345  	store.Lock()
   346  	defer store.Unlock()
   347  	if imgID, exists := repo[refOrID]; exists {
   348  		return store.graph.Get(imgID)
   349  	}
   350  
   351  	// If no matching tag is found, search through images for a matching image id
   352  	// iff it looks like a short ID or would look like a short ID
   353  	if stringid.IsShortID(stringid.TruncateID(refOrID)) {
   354  		for _, revision := range repo {
   355  			if strings.HasPrefix(revision, refOrID) {
   356  				return store.graph.Get(revision)
   357  			}
   358  		}
   359  	}
   360  
   361  	return nil, nil
   362  }
   363  
   364  func (store *TagStore) GetRepoRefs() map[string][]string {
   365  	store.Lock()
   366  	reporefs := make(map[string][]string)
   367  
   368  	for name, repository := range store.Repositories {
   369  		for tag, id := range repository {
   370  			shortID := stringid.TruncateID(id)
   371  			reporefs[shortID] = append(reporefs[shortID], utils.ImageReference(name, tag))
   372  		}
   373  	}
   374  	store.Unlock()
   375  	return reporefs
   376  }
   377  
   378  // Validate the name of a repository
   379  func validateRepoName(name string) error {
   380  	if name == "" {
   381  		return fmt.Errorf("Repository name can't be empty")
   382  	}
   383  	if name == "scratch" {
   384  		return fmt.Errorf("'scratch' is a reserved name")
   385  	}
   386  	return nil
   387  }
   388  
   389  func validateDigest(dgst string) error {
   390  	if dgst == "" {
   391  		return errors.New("digest can't be empty")
   392  	}
   393  	if _, err := digest.ParseDigest(dgst); err != nil {
   394  		return err
   395  	}
   396  	return nil
   397  }
   398  
   399  func (store *TagStore) poolAdd(kind, key string) (chan struct{}, error) {
   400  	store.Lock()
   401  	defer store.Unlock()
   402  
   403  	if c, exists := store.pullingPool[key]; exists {
   404  		return c, fmt.Errorf("pull %s is already in progress", key)
   405  	}
   406  	if c, exists := store.pushingPool[key]; exists {
   407  		return c, fmt.Errorf("push %s is already in progress", key)
   408  	}
   409  
   410  	c := make(chan struct{})
   411  	switch kind {
   412  	case "pull":
   413  		store.pullingPool[key] = c
   414  	case "push":
   415  		store.pushingPool[key] = c
   416  	default:
   417  		return nil, fmt.Errorf("Unknown pool type")
   418  	}
   419  	return c, nil
   420  }
   421  
   422  func (store *TagStore) poolRemove(kind, key string) error {
   423  	store.Lock()
   424  	defer store.Unlock()
   425  	switch kind {
   426  	case "pull":
   427  		if c, exists := store.pullingPool[key]; exists {
   428  			close(c)
   429  			delete(store.pullingPool, key)
   430  		}
   431  	case "push":
   432  		if c, exists := store.pushingPool[key]; exists {
   433  			close(c)
   434  			delete(store.pushingPool, key)
   435  		}
   436  	default:
   437  		return fmt.Errorf("Unknown pool type")
   438  	}
   439  	return nil
   440  }