github.com/microsoft/docker@v1.5.0-rc2/graph/tags.go (about) 1 package graph 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "regexp" 10 "sort" 11 "strings" 12 "sync" 13 14 "github.com/docker/docker/image" 15 "github.com/docker/docker/pkg/parsers" 16 "github.com/docker/docker/registry" 17 "github.com/docker/docker/utils" 18 "github.com/docker/libtrust" 19 ) 20 21 const DEFAULTTAG = "latest" 22 23 var ( 24 validTagName = regexp.MustCompile(`^[\w][\w.-]{0,127}$`) 25 ) 26 27 type TagStore struct { 28 path string 29 graph *Graph 30 Repositories map[string]Repository 31 trustKey libtrust.PrivateKey 32 sync.Mutex 33 // FIXME: move push/pull-related fields 34 // to a helper type 35 pullingPool map[string]chan struct{} 36 pushingPool map[string]chan struct{} 37 } 38 39 type Repository map[string]string 40 41 // update Repository mapping with content of u 42 func (r Repository) Update(u Repository) { 43 for k, v := range u { 44 r[k] = v 45 } 46 } 47 48 // return true if the contents of u Repository, are wholly contained in r Repository 49 func (r Repository) Contains(u Repository) bool { 50 for k, v := range u { 51 // if u's key is not present in r OR u's key is present, but not the same value 52 if rv, ok := r[k]; !ok || (ok && rv != v) { 53 return false 54 } 55 } 56 return true 57 } 58 59 func NewTagStore(path string, graph *Graph, key libtrust.PrivateKey) (*TagStore, error) { 60 abspath, err := filepath.Abs(path) 61 if err != nil { 62 return nil, err 63 } 64 65 store := &TagStore{ 66 path: abspath, 67 graph: graph, 68 trustKey: key, 69 Repositories: make(map[string]Repository), 70 pullingPool: make(map[string]chan struct{}), 71 pushingPool: make(map[string]chan struct{}), 72 } 73 // Load the json file if it exists, otherwise create it. 74 if err := store.reload(); os.IsNotExist(err) { 75 if err := store.save(); err != nil { 76 return nil, err 77 } 78 } else if err != nil { 79 return nil, err 80 } 81 return store, nil 82 } 83 84 func (store *TagStore) save() error { 85 // Store the json ball 86 jsonData, err := json.Marshal(store) 87 if err != nil { 88 return err 89 } 90 if err := ioutil.WriteFile(store.path, jsonData, 0600); err != nil { 91 return err 92 } 93 return nil 94 } 95 96 func (store *TagStore) reload() error { 97 jsonData, err := ioutil.ReadFile(store.path) 98 if err != nil { 99 return err 100 } 101 if err := json.Unmarshal(jsonData, store); err != nil { 102 return err 103 } 104 return nil 105 } 106 107 func (store *TagStore) LookupImage(name string) (*image.Image, error) { 108 // FIXME: standardize on returning nil when the image doesn't exist, and err for everything else 109 // (so we can pass all errors here) 110 repos, tag := parsers.ParseRepositoryTag(name) 111 if tag == "" { 112 tag = DEFAULTTAG 113 } 114 img, err := store.GetImage(repos, tag) 115 store.Lock() 116 defer store.Unlock() 117 if err != nil { 118 return nil, err 119 } else if img == nil { 120 if img, err = store.graph.Get(name); err != nil { 121 return nil, err 122 } 123 } 124 return img, nil 125 } 126 127 // Return a reverse-lookup table of all the names which refer to each image 128 // Eg. {"43b5f19b10584": {"base:latest", "base:v1"}} 129 func (store *TagStore) ByID() map[string][]string { 130 store.Lock() 131 defer store.Unlock() 132 byID := make(map[string][]string) 133 for repoName, repository := range store.Repositories { 134 for tag, id := range repository { 135 name := repoName + ":" + tag 136 if _, exists := byID[id]; !exists { 137 byID[id] = []string{name} 138 } else { 139 byID[id] = append(byID[id], name) 140 sort.Strings(byID[id]) 141 } 142 } 143 } 144 return byID 145 } 146 147 func (store *TagStore) ImageName(id string) string { 148 if names, exists := store.ByID()[id]; exists && len(names) > 0 { 149 return names[0] 150 } 151 return utils.TruncateID(id) 152 } 153 154 func (store *TagStore) DeleteAll(id string) error { 155 names, exists := store.ByID()[id] 156 if !exists || len(names) == 0 { 157 return nil 158 } 159 for _, name := range names { 160 if strings.Contains(name, ":") { 161 nameParts := strings.Split(name, ":") 162 if _, err := store.Delete(nameParts[0], nameParts[1]); err != nil { 163 return err 164 } 165 } else { 166 if _, err := store.Delete(name, ""); err != nil { 167 return err 168 } 169 } 170 } 171 return nil 172 } 173 174 func (store *TagStore) Delete(repoName, tag string) (bool, error) { 175 store.Lock() 176 defer store.Unlock() 177 deleted := false 178 if err := store.reload(); err != nil { 179 return false, err 180 } 181 repoName = registry.NormalizeLocalName(repoName) 182 if r, exists := store.Repositories[repoName]; exists { 183 if tag != "" { 184 if _, exists2 := r[tag]; exists2 { 185 delete(r, tag) 186 if len(r) == 0 { 187 delete(store.Repositories, repoName) 188 } 189 deleted = true 190 } else { 191 return false, fmt.Errorf("No such tag: %s:%s", repoName, tag) 192 } 193 } else { 194 delete(store.Repositories, repoName) 195 deleted = true 196 } 197 } else { 198 return false, fmt.Errorf("No such repository: %s", repoName) 199 } 200 return deleted, store.save() 201 } 202 203 func (store *TagStore) Set(repoName, tag, imageName string, force bool) error { 204 img, err := store.LookupImage(imageName) 205 store.Lock() 206 defer store.Unlock() 207 if err != nil { 208 return err 209 } 210 if tag == "" { 211 tag = DEFAULTTAG 212 } 213 if err := validateRepoName(repoName); err != nil { 214 return err 215 } 216 if err := ValidateTagName(tag); err != nil { 217 return err 218 } 219 if err := store.reload(); err != nil { 220 return err 221 } 222 var repo Repository 223 repoName = registry.NormalizeLocalName(repoName) 224 if r, exists := store.Repositories[repoName]; exists { 225 repo = r 226 if old, exists := store.Repositories[repoName][tag]; exists && !force { 227 return fmt.Errorf("Conflict: Tag %s is already set to image %s, if you want to replace it, please use -f option", tag, old) 228 } 229 } else { 230 repo = make(map[string]string) 231 store.Repositories[repoName] = repo 232 } 233 repo[tag] = img.ID 234 return store.save() 235 } 236 237 func (store *TagStore) Get(repoName string) (Repository, error) { 238 store.Lock() 239 defer store.Unlock() 240 if err := store.reload(); err != nil { 241 return nil, err 242 } 243 repoName = registry.NormalizeLocalName(repoName) 244 if r, exists := store.Repositories[repoName]; exists { 245 return r, nil 246 } 247 return nil, nil 248 } 249 250 func (store *TagStore) GetImage(repoName, tagOrID string) (*image.Image, error) { 251 repo, err := store.Get(repoName) 252 store.Lock() 253 defer store.Unlock() 254 if err != nil { 255 return nil, err 256 } else if repo == nil { 257 return nil, nil 258 } 259 if revision, exists := repo[tagOrID]; exists { 260 return store.graph.Get(revision) 261 } 262 // If no matching tag is found, search through images for a matching image id 263 for _, revision := range repo { 264 if strings.HasPrefix(revision, tagOrID) { 265 return store.graph.Get(revision) 266 } 267 } 268 return nil, nil 269 } 270 271 func (store *TagStore) GetRepoRefs() map[string][]string { 272 store.Lock() 273 reporefs := make(map[string][]string) 274 275 for name, repository := range store.Repositories { 276 for tag, id := range repository { 277 shortID := utils.TruncateID(id) 278 reporefs[shortID] = append(reporefs[shortID], fmt.Sprintf("%s:%s", name, tag)) 279 } 280 } 281 store.Unlock() 282 return reporefs 283 } 284 285 // Validate the name of a repository 286 func validateRepoName(name string) error { 287 if name == "" { 288 return fmt.Errorf("Repository name can't be empty") 289 } 290 if name == "scratch" { 291 return fmt.Errorf("'scratch' is a reserved name") 292 } 293 return nil 294 } 295 296 // Validate the name of a tag 297 func ValidateTagName(name string) error { 298 if name == "" { 299 return fmt.Errorf("Tag name can't be empty") 300 } 301 if !validTagName.MatchString(name) { 302 return fmt.Errorf("Illegal tag name (%s): only [A-Za-z0-9_.-] are allowed, minimum 1, maximum 128 in length", name) 303 } 304 return nil 305 } 306 307 func (store *TagStore) poolAdd(kind, key string) (chan struct{}, error) { 308 store.Lock() 309 defer store.Unlock() 310 311 if c, exists := store.pullingPool[key]; exists { 312 return c, fmt.Errorf("pull %s is already in progress", key) 313 } 314 if c, exists := store.pushingPool[key]; exists { 315 return c, fmt.Errorf("push %s is already in progress", key) 316 } 317 318 c := make(chan struct{}) 319 switch kind { 320 case "pull": 321 store.pullingPool[key] = c 322 case "push": 323 store.pushingPool[key] = c 324 default: 325 return nil, fmt.Errorf("Unknown pool type") 326 } 327 return c, nil 328 } 329 330 func (store *TagStore) poolRemove(kind, key string) error { 331 store.Lock() 332 defer store.Unlock() 333 switch kind { 334 case "pull": 335 if c, exists := store.pullingPool[key]; exists { 336 close(c) 337 delete(store.pullingPool, key) 338 } 339 case "push": 340 if c, exists := store.pushingPool[key]; exists { 341 close(c) 342 delete(store.pushingPool, key) 343 } 344 default: 345 return fmt.Errorf("Unknown pool type") 346 } 347 return nil 348 }