ef711962 |
package docker |
680f40c3 |
import (
"encoding/json" |
49a78929 |
"fmt" |
2e69e172 |
"github.com/dotcloud/docker/utils" |
680f40c3 |
"io/ioutil" |
d0c77652 |
"os" |
680f40c3 |
"path/filepath" |
846a9e89 |
"sort" |
49a78929 |
"strings" |
680f40c3 |
)
|
fd224ee5 |
const DEFAULTTAG = "latest" |
640026ec |
|
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) { |
ca98434a |
// FIXME: standardize on returning nil when the image doesn't exist, and err for everything else
// (so we can pass all errors here)
repos, tag := utils.ParseRepositoryTag(name)
if tag == "" {
tag = DEFAULTTAG
}
img, err := store.GetImage(repos, tag) |
640026ec |
if err != nil { |
ca98434a |
return nil, err
} else if img == nil {
if img, err = store.graph.Get(name); err != nil { |
640026ec |
return nil, err
}
}
return img, nil
}
|
12049f95 |
// Return a reverse-lookup table of all the names which refer to each image
// Eg. {"43b5f19b10584": {"base:latest", "base:v1"}} |
fd224ee5 |
func (store *TagStore) ByID() map[string][]string {
byID := make(map[string][]string) |
12049f95 |
for repoName, repository := range store.Repositories {
for tag, id := range repository {
name := repoName + ":" + tag |
fd224ee5 |
if _, exists := byID[id]; !exists {
byID[id] = []string{name} |
12049f95 |
} else { |
fd224ee5 |
byID[id] = append(byID[id], name)
sort.Strings(byID[id]) |
12049f95 |
}
}
} |
fd224ee5 |
return byID |
12049f95 |
}
func (store *TagStore) ImageName(id string) string { |
fd224ee5 |
if names, exists := store.ByID()[id]; exists && len(names) > 0 { |
12049f95 |
return names[0]
} |
fd224ee5 |
return utils.TruncateID(id) |
12049f95 |
}
|
5aa95b66 |
func (store *TagStore) DeleteAll(id string) error { |
66d9a733 |
names, exists := store.ByID()[id] |
5aa95b66 |
if !exists || len(names) == 0 {
return nil
}
for _, name := range names {
if strings.Contains(name, ":") {
nameParts := strings.Split(name, ":") |
9060b5c2 |
if _, err := store.Delete(nameParts[0], nameParts[1]); err != nil { |
5aa95b66 |
return err
}
} else { |
9060b5c2 |
if _, err := store.Delete(name, ""); err != nil { |
5aa95b66 |
return err
}
}
}
return nil
}
|
9060b5c2 |
func (store *TagStore) Delete(repoName, tag string) (bool, error) {
deleted := false |
c80448c4 |
if err := store.Reload(); err != nil { |
9060b5c2 |
return false, err |
c80448c4 |
}
if r, exists := store.Repositories[repoName]; exists {
if tag != "" {
if _, exists2 := r[tag]; exists2 {
delete(r, tag)
if len(r) == 0 {
delete(store.Repositories, repoName)
} |
9060b5c2 |
deleted = true |
c80448c4 |
} else { |
9060b5c2 |
return false, fmt.Errorf("No such tag: %s:%s", repoName, tag) |
c80448c4 |
}
} else {
delete(store.Repositories, repoName) |
9060b5c2 |
deleted = true |
c80448c4 |
}
} else {
fmt.Errorf("No such repository: %s", repoName)
} |
9060b5c2 |
return deleted, store.Save() |
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 == "" { |
fd224ee5 |
tag = DEFAULTTAG |
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 { |
67b20f2c |
return fmt.Errorf("Conflict: Tag %s:%s is already set to %s", repoName, tag, old) |
bf7602bc |
} |
680f40c3 |
store.Repositories[repoName] = repo
} |
fd224ee5 |
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
}
|
19121c16 |
func (store *TagStore) GetImage(repoName, tagOrID string) (*Image, error) { |
680f40c3 |
repo, err := store.Get(repoName)
if err != nil {
return nil, err
} else if repo == nil {
return nil, nil
} |
44b3e8d5 |
if revision, exists := repo[tagOrID]; exists {
return store.graph.Get(revision)
}
// If no matching tag is found, search through images for a matching image id |
d26a3b37 |
for _, revision := range repo { |
19121c16 |
if strings.HasPrefix(revision, tagOrID) { |
d26a3b37 |
return store.graph.Get(revision)
}
} |
680f40c3 |
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")
}
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
} |