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