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 }