tags.go
ef711962
 package docker
680f40c3
 
 import (
 	"encoding/json"
49a78929
 	"fmt"
680f40c3
 	"io/ioutil"
d0c77652
 	"os"
680f40c3
 	"path/filepath"
846a9e89
 	"sort"
49a78929
 	"strings"
680f40c3
 )
 
640026ec
 const DEFAULT_TAG = "latest"
 
44faa07b
 type TagStore struct {
680f40c3
 	path         string
 	graph        *Graph
44faa07b
 	Repositories map[string]Repository
680f40c3
 }
 
44faa07b
 type Repository map[string]string
680f40c3
 
44faa07b
 func NewTagStore(path string, graph *Graph) (*TagStore, error) {
680f40c3
 	abspath, err := filepath.Abs(path)
 	if err != nil {
 		return nil, err
 	}
44faa07b
 	store := &TagStore{
680f40c3
 		path:         abspath,
 		graph:        graph,
44faa07b
 		Repositories: make(map[string]Repository),
680f40c3
 	}
d0c77652
 	// Load the json file if it exists, otherwise create it.
 	if err := store.Reload(); os.IsNotExist(err) {
 		if err := store.Save(); err != nil {
 			return nil, err
 		}
 	} else if err != nil {
680f40c3
 		return nil, err
 	}
 	return store, nil
 }
 
44faa07b
 func (store *TagStore) Save() error {
680f40c3
 	// Store the json ball
 	jsonData, err := json.Marshal(store)
 	if err != nil {
 		return err
 	}
 	if err := ioutil.WriteFile(store.path, jsonData, 0600); err != nil {
 		return err
 	}
 	return nil
 }
 
44faa07b
 func (store *TagStore) Reload() error {
680f40c3
 	jsonData, err := ioutil.ReadFile(store.path)
 	if err != nil {
 		return err
 	}
 	if err := json.Unmarshal(jsonData, store); err != nil {
 		return err
 	}
 	return nil
 }
 
640026ec
 func (store *TagStore) LookupImage(name string) (*Image, error) {
 	img, err := store.graph.Get(name)
 	if err != nil {
 		// FIXME: standardize on returning nil when the image doesn't exist, and err for everything else
 		// (so we can pass all errors here)
 		repoAndTag := strings.SplitN(name, ":", 2)
 		if len(repoAndTag) == 1 {
 			repoAndTag = append(repoAndTag, DEFAULT_TAG)
 		}
 		if i, err := store.GetImage(repoAndTag[0], repoAndTag[1]); err != nil {
 			return nil, err
 		} else if i == nil {
004a5310
 			return nil, fmt.Errorf("Image does not exist: %s", name)
640026ec
 		} else {
 			img = i
 		}
 	}
 	return img, nil
 }
 
12049f95
 // Return a reverse-lookup table of all the names which refer to each image
 // Eg. {"43b5f19b10584": {"base:latest", "base:v1"}}
 func (store *TagStore) ById() map[string][]string {
 	byId := make(map[string][]string)
 	for repoName, repository := range store.Repositories {
 		for tag, id := range repository {
 			name := repoName + ":" + tag
 			if _, exists := byId[id]; !exists {
 				byId[id] = []string{name}
 			} else {
 				byId[id] = append(byId[id], name)
846a9e89
 				sort.Strings(byId[id])
12049f95
 			}
 		}
 	}
 	return byId
 }
 
 func (store *TagStore) ImageName(id string) string {
 	if names, exists := store.ById()[id]; exists && len(names) > 0 {
 		return names[0]
 	}
1632566e
 	return TruncateId(id)
12049f95
 }
 
bf7602bc
 func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
 	img, err := store.LookupImage(imageName)
 	if err != nil {
 		return err
 	}
640026ec
 	if tag == "" {
 		tag = DEFAULT_TAG
49a78929
 	}
640026ec
 	if err := validateRepoName(repoName); err != nil {
 		return err
 	}
 	if err := validateTagName(tag); err != nil {
 		return err
49a78929
 	}
680f40c3
 	if err := store.Reload(); err != nil {
 		return err
 	}
44faa07b
 	var repo Repository
680f40c3
 	if r, exists := store.Repositories[repoName]; exists {
 		repo = r
 	} else {
44faa07b
 		repo = make(map[string]string)
bf7602bc
 		if old, exists := store.Repositories[repoName]; exists && !force {
 			return fmt.Errorf("Tag %s:%s is already set to %s", repoName, tag, old)
 		}
680f40c3
 		store.Repositories[repoName] = repo
 	}
bf7602bc
 	repo[tag] = img.Id
680f40c3
 	return store.Save()
 }
 
44faa07b
 func (store *TagStore) Get(repoName string) (Repository, error) {
680f40c3
 	if err := store.Reload(); err != nil {
 		return nil, err
 	}
 	if r, exists := store.Repositories[repoName]; exists {
 		return r, nil
 	}
 	return nil, nil
 }
 
44faa07b
 func (store *TagStore) GetImage(repoName, tag string) (*Image, error) {
680f40c3
 	repo, err := store.Get(repoName)
 	if err != nil {
 		return nil, err
 	} else if repo == nil {
 		return nil, nil
 	}
44faa07b
 	if revision, exists := repo[tag]; exists {
680f40c3
 		return store.graph.Get(revision)
 	}
 	return nil, nil
 }
640026ec
 
 // Validate the name of a repository
 func validateRepoName(name string) error {
 	if name == "" {
 		return fmt.Errorf("Repository name can't be empty")
 	}
 	if strings.Contains(name, ":") {
 		return fmt.Errorf("Illegal repository name: %s", name)
 	}
 	return nil
 }
 
 // Validate the name of a tag
 func validateTagName(name string) error {
 	if name == "" {
 		return fmt.Errorf("Tag name can't be empty")
 	}
 	if strings.Contains(name, "/") || strings.Contains(name, ":") {
 		return fmt.Errorf("Illegal tag name: %s", name)
 	}
 	return nil
 }