github.com/rentongzhang/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 }