github.com/sshnaidm/storage@v1.12.13/containers.go (about) 1 package storage 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "time" 10 11 "github.com/containers/storage/pkg/idtools" 12 "github.com/containers/storage/pkg/ioutils" 13 "github.com/containers/storage/pkg/stringid" 14 "github.com/containers/storage/pkg/truncindex" 15 digest "github.com/opencontainers/go-digest" 16 "github.com/pkg/errors" 17 ) 18 19 // A Container is a reference to a read-write layer with metadata. 20 type Container struct { 21 // ID is either one which was specified at create-time, or a random 22 // value which was generated by the library. 23 ID string `json:"id"` 24 25 // Names is an optional set of user-defined convenience values. The 26 // container can be referred to by its ID or any of its names. Names 27 // are unique among containers. 28 Names []string `json:"names,omitempty"` 29 30 // ImageID is the ID of the image which was used to create the container. 31 ImageID string `json:"image"` 32 33 // LayerID is the ID of the read-write layer for the container itself. 34 // It is assumed that the image's top layer is the parent of the container's 35 // read-write layer. 36 LayerID string `json:"layer"` 37 38 // Metadata is data we keep for the convenience of the caller. It is not 39 // expected to be large, since it is kept in memory. 40 Metadata string `json:"metadata,omitempty"` 41 42 // BigDataNames is a list of names of data items that we keep for the 43 // convenience of the caller. They can be large, and are only in 44 // memory when being read from or written to disk. 45 BigDataNames []string `json:"big-data-names,omitempty"` 46 47 // BigDataSizes maps the names in BigDataNames to the sizes of the data 48 // that has been stored, if they're known. 49 BigDataSizes map[string]int64 `json:"big-data-sizes,omitempty"` 50 51 // BigDataDigests maps the names in BigDataNames to the digests of the 52 // data that has been stored, if they're known. 53 BigDataDigests map[string]digest.Digest `json:"big-data-digests,omitempty"` 54 55 // Created is the datestamp for when this container was created. Older 56 // versions of the library did not track this information, so callers 57 // will likely want to use the IsZero() method to verify that a value 58 // is set before using it. 59 Created time.Time `json:"created,omitempty"` 60 61 // UIDMap and GIDMap are used for setting up a container's root 62 // filesystem for use inside of a user namespace where UID mapping is 63 // being used. 64 UIDMap []idtools.IDMap `json:"uidmap,omitempty"` 65 GIDMap []idtools.IDMap `json:"gidmap,omitempty"` 66 67 Flags map[string]interface{} `json:"flags,omitempty"` 68 } 69 70 // ContainerStore provides bookkeeping for information about Containers. 71 type ContainerStore interface { 72 FileBasedStore 73 MetadataStore 74 ContainerBigDataStore 75 FlaggableStore 76 77 // Create creates a container that has a specified ID (or generates a 78 // random one if an empty value is supplied) and optional names, 79 // based on the specified image, using the specified layer as its 80 // read-write layer. 81 // The maps in the container's options structure are recorded for the 82 // convenience of the caller, nothing more. 83 Create(id string, names []string, image, layer, metadata string, options *ContainerOptions) (*Container, error) 84 85 // SetNames updates the list of names associated with the container 86 // with the specified ID. 87 SetNames(id string, names []string) error 88 89 // Get retrieves information about a container given an ID or name. 90 Get(id string) (*Container, error) 91 92 // Exists checks if there is a container with the given ID or name. 93 Exists(id string) bool 94 95 // Delete removes the record of the container. 96 Delete(id string) error 97 98 // Wipe removes records of all containers. 99 Wipe() error 100 101 // Lookup attempts to translate a name to an ID. Most methods do this 102 // implicitly. 103 Lookup(name string) (string, error) 104 105 // Containers returns a slice enumerating the known containers. 106 Containers() ([]Container, error) 107 } 108 109 type containerStore struct { 110 lockfile Locker 111 dir string 112 containers []*Container 113 idindex *truncindex.TruncIndex 114 byid map[string]*Container 115 bylayer map[string]*Container 116 byname map[string]*Container 117 } 118 119 func copyContainer(c *Container) *Container { 120 return &Container{ 121 ID: c.ID, 122 Names: copyStringSlice(c.Names), 123 ImageID: c.ImageID, 124 LayerID: c.LayerID, 125 Metadata: c.Metadata, 126 BigDataNames: copyStringSlice(c.BigDataNames), 127 BigDataSizes: copyStringInt64Map(c.BigDataSizes), 128 BigDataDigests: copyStringDigestMap(c.BigDataDigests), 129 Created: c.Created, 130 UIDMap: copyIDMap(c.UIDMap), 131 GIDMap: copyIDMap(c.GIDMap), 132 Flags: copyStringInterfaceMap(c.Flags), 133 } 134 } 135 136 func (c *Container) MountLabel() string { 137 if label, ok := c.Flags["MountLabel"].(string); ok { 138 return label 139 } 140 return "" 141 } 142 143 func (c *Container) ProcessLabel() string { 144 if label, ok := c.Flags["ProcessLabel"].(string); ok { 145 return label 146 } 147 return "" 148 } 149 150 func (c *Container) MountOpts() []string { 151 if mountOpts, ok := c.Flags["MountOpts"].([]string); ok { 152 return mountOpts 153 } 154 return nil 155 } 156 157 func (r *containerStore) Containers() ([]Container, error) { 158 containers := make([]Container, len(r.containers)) 159 for i := range r.containers { 160 containers[i] = *copyContainer(r.containers[i]) 161 } 162 return containers, nil 163 } 164 165 func (r *containerStore) containerspath() string { 166 return filepath.Join(r.dir, "containers.json") 167 } 168 169 func (r *containerStore) datadir(id string) string { 170 return filepath.Join(r.dir, id) 171 } 172 173 func (r *containerStore) datapath(id, key string) string { 174 return filepath.Join(r.datadir(id), makeBigDataBaseName(key)) 175 } 176 177 func (r *containerStore) Load() error { 178 needSave := false 179 rpath := r.containerspath() 180 data, err := ioutil.ReadFile(rpath) 181 if err != nil && !os.IsNotExist(err) { 182 return err 183 } 184 containers := []*Container{} 185 layers := make(map[string]*Container) 186 idlist := []string{} 187 ids := make(map[string]*Container) 188 names := make(map[string]*Container) 189 if err = json.Unmarshal(data, &containers); len(data) == 0 || err == nil { 190 idlist = make([]string, 0, len(containers)) 191 for n, container := range containers { 192 idlist = append(idlist, container.ID) 193 ids[container.ID] = containers[n] 194 layers[container.LayerID] = containers[n] 195 for _, name := range container.Names { 196 if conflict, ok := names[name]; ok { 197 r.removeName(conflict, name) 198 needSave = true 199 } 200 names[name] = containers[n] 201 } 202 } 203 } 204 r.containers = containers 205 r.idindex = truncindex.NewTruncIndex(idlist) 206 r.byid = ids 207 r.bylayer = layers 208 r.byname = names 209 if needSave { 210 return r.Save() 211 } 212 return nil 213 } 214 215 func (r *containerStore) Save() error { 216 if !r.Locked() { 217 return errors.New("container store is not locked") 218 } 219 rpath := r.containerspath() 220 if err := os.MkdirAll(filepath.Dir(rpath), 0700); err != nil { 221 return err 222 } 223 jdata, err := json.Marshal(&r.containers) 224 if err != nil { 225 return err 226 } 227 defer r.Touch() 228 return ioutils.AtomicWriteFile(rpath, jdata, 0600) 229 } 230 231 func newContainerStore(dir string) (ContainerStore, error) { 232 if err := os.MkdirAll(dir, 0700); err != nil { 233 return nil, err 234 } 235 lockfile, err := GetLockfile(filepath.Join(dir, "containers.lock")) 236 if err != nil { 237 return nil, err 238 } 239 lockfile.Lock() 240 defer lockfile.Unlock() 241 cstore := containerStore{ 242 lockfile: lockfile, 243 dir: dir, 244 containers: []*Container{}, 245 byid: make(map[string]*Container), 246 bylayer: make(map[string]*Container), 247 byname: make(map[string]*Container), 248 } 249 if err := cstore.Load(); err != nil { 250 return nil, err 251 } 252 return &cstore, nil 253 } 254 255 func (r *containerStore) lookup(id string) (*Container, bool) { 256 if container, ok := r.byid[id]; ok { 257 return container, ok 258 } else if container, ok := r.byname[id]; ok { 259 return container, ok 260 } else if container, ok := r.bylayer[id]; ok { 261 return container, ok 262 } else if longid, err := r.idindex.Get(id); err == nil { 263 if container, ok := r.byid[longid]; ok { 264 return container, ok 265 } 266 } 267 return nil, false 268 } 269 270 func (r *containerStore) ClearFlag(id string, flag string) error { 271 container, ok := r.lookup(id) 272 if !ok { 273 return ErrContainerUnknown 274 } 275 delete(container.Flags, flag) 276 return r.Save() 277 } 278 279 func (r *containerStore) SetFlag(id string, flag string, value interface{}) error { 280 container, ok := r.lookup(id) 281 if !ok { 282 return ErrContainerUnknown 283 } 284 if container.Flags == nil { 285 container.Flags = make(map[string]interface{}) 286 } 287 container.Flags[flag] = value 288 return r.Save() 289 } 290 291 func (r *containerStore) Create(id string, names []string, image, layer, metadata string, options *ContainerOptions) (container *Container, err error) { 292 if id == "" { 293 id = stringid.GenerateRandomID() 294 _, idInUse := r.byid[id] 295 for idInUse { 296 id = stringid.GenerateRandomID() 297 _, idInUse = r.byid[id] 298 } 299 } 300 if _, idInUse := r.byid[id]; idInUse { 301 return nil, ErrDuplicateID 302 } 303 if options.MountOpts != nil { 304 options.Flags["MountOpts"] = append([]string{}, options.MountOpts...) 305 } 306 names = dedupeNames(names) 307 for _, name := range names { 308 if _, nameInUse := r.byname[name]; nameInUse { 309 return nil, errors.Wrapf(ErrDuplicateName, 310 fmt.Sprintf("the container name \"%s\" is already in use by \"%s\". You have to remove that container to be able to reuse that name.", name, r.byname[name].ID)) 311 } 312 } 313 if err == nil { 314 container = &Container{ 315 ID: id, 316 Names: names, 317 ImageID: image, 318 LayerID: layer, 319 Metadata: metadata, 320 BigDataNames: []string{}, 321 BigDataSizes: make(map[string]int64), 322 BigDataDigests: make(map[string]digest.Digest), 323 Created: time.Now().UTC(), 324 Flags: copyStringInterfaceMap(options.Flags), 325 UIDMap: copyIDMap(options.UIDMap), 326 GIDMap: copyIDMap(options.GIDMap), 327 } 328 r.containers = append(r.containers, container) 329 r.byid[id] = container 330 r.idindex.Add(id) 331 r.bylayer[layer] = container 332 for _, name := range names { 333 r.byname[name] = container 334 } 335 err = r.Save() 336 container = copyContainer(container) 337 } 338 return container, err 339 } 340 341 func (r *containerStore) Metadata(id string) (string, error) { 342 if container, ok := r.lookup(id); ok { 343 return container.Metadata, nil 344 } 345 return "", ErrContainerUnknown 346 } 347 348 func (r *containerStore) SetMetadata(id, metadata string) error { 349 if container, ok := r.lookup(id); ok { 350 container.Metadata = metadata 351 return r.Save() 352 } 353 return ErrContainerUnknown 354 } 355 356 func (r *containerStore) removeName(container *Container, name string) { 357 container.Names = stringSliceWithoutValue(container.Names, name) 358 } 359 360 func (r *containerStore) SetNames(id string, names []string) error { 361 names = dedupeNames(names) 362 if container, ok := r.lookup(id); ok { 363 for _, name := range container.Names { 364 delete(r.byname, name) 365 } 366 for _, name := range names { 367 if otherContainer, ok := r.byname[name]; ok { 368 r.removeName(otherContainer, name) 369 } 370 r.byname[name] = container 371 } 372 container.Names = names 373 return r.Save() 374 } 375 return ErrContainerUnknown 376 } 377 378 func (r *containerStore) Delete(id string) error { 379 container, ok := r.lookup(id) 380 if !ok { 381 return ErrContainerUnknown 382 } 383 id = container.ID 384 toDeleteIndex := -1 385 for i, candidate := range r.containers { 386 if candidate.ID == id { 387 toDeleteIndex = i 388 break 389 } 390 } 391 delete(r.byid, id) 392 r.idindex.Delete(id) 393 delete(r.bylayer, container.LayerID) 394 for _, name := range container.Names { 395 delete(r.byname, name) 396 } 397 if toDeleteIndex != -1 { 398 // delete the container at toDeleteIndex 399 if toDeleteIndex == len(r.containers)-1 { 400 r.containers = r.containers[:len(r.containers)-1] 401 } else { 402 r.containers = append(r.containers[:toDeleteIndex], r.containers[toDeleteIndex+1:]...) 403 } 404 } 405 if err := r.Save(); err != nil { 406 return err 407 } 408 if err := os.RemoveAll(r.datadir(id)); err != nil { 409 return err 410 } 411 return nil 412 } 413 414 func (r *containerStore) Get(id string) (*Container, error) { 415 if container, ok := r.lookup(id); ok { 416 return copyContainer(container), nil 417 } 418 return nil, ErrContainerUnknown 419 } 420 421 func (r *containerStore) Lookup(name string) (id string, err error) { 422 if container, ok := r.lookup(name); ok { 423 return container.ID, nil 424 } 425 return "", ErrContainerUnknown 426 } 427 428 func (r *containerStore) Exists(id string) bool { 429 _, ok := r.lookup(id) 430 return ok 431 } 432 433 func (r *containerStore) BigData(id, key string) ([]byte, error) { 434 if key == "" { 435 return nil, errors.Wrapf(ErrInvalidBigDataName, "can't retrieve container big data value for empty name") 436 } 437 c, ok := r.lookup(id) 438 if !ok { 439 return nil, ErrContainerUnknown 440 } 441 return ioutil.ReadFile(r.datapath(c.ID, key)) 442 } 443 444 func (r *containerStore) BigDataSize(id, key string) (int64, error) { 445 if key == "" { 446 return -1, errors.Wrapf(ErrInvalidBigDataName, "can't retrieve size of container big data with empty name") 447 } 448 c, ok := r.lookup(id) 449 if !ok { 450 return -1, ErrContainerUnknown 451 } 452 if c.BigDataSizes == nil { 453 c.BigDataSizes = make(map[string]int64) 454 } 455 if size, ok := c.BigDataSizes[key]; ok { 456 return size, nil 457 } 458 if data, err := r.BigData(id, key); err == nil && data != nil { 459 if err = r.SetBigData(id, key, data); err == nil { 460 c, ok := r.lookup(id) 461 if !ok { 462 return -1, ErrContainerUnknown 463 } 464 if size, ok := c.BigDataSizes[key]; ok { 465 return size, nil 466 } 467 } else { 468 return -1, err 469 } 470 } 471 return -1, ErrSizeUnknown 472 } 473 474 func (r *containerStore) BigDataDigest(id, key string) (digest.Digest, error) { 475 if key == "" { 476 return "", errors.Wrapf(ErrInvalidBigDataName, "can't retrieve digest of container big data value with empty name") 477 } 478 c, ok := r.lookup(id) 479 if !ok { 480 return "", ErrContainerUnknown 481 } 482 if c.BigDataDigests == nil { 483 c.BigDataDigests = make(map[string]digest.Digest) 484 } 485 if d, ok := c.BigDataDigests[key]; ok { 486 return d, nil 487 } 488 if data, err := r.BigData(id, key); err == nil && data != nil { 489 if err = r.SetBigData(id, key, data); err == nil { 490 c, ok := r.lookup(id) 491 if !ok { 492 return "", ErrContainerUnknown 493 } 494 if d, ok := c.BigDataDigests[key]; ok { 495 return d, nil 496 } 497 } else { 498 return "", err 499 } 500 } 501 return "", ErrDigestUnknown 502 } 503 504 func (r *containerStore) BigDataNames(id string) ([]string, error) { 505 c, ok := r.lookup(id) 506 if !ok { 507 return nil, ErrContainerUnknown 508 } 509 return copyStringSlice(c.BigDataNames), nil 510 } 511 512 func (r *containerStore) SetBigData(id, key string, data []byte) error { 513 if key == "" { 514 return errors.Wrapf(ErrInvalidBigDataName, "can't set empty name for container big data item") 515 } 516 c, ok := r.lookup(id) 517 if !ok { 518 return ErrContainerUnknown 519 } 520 if err := os.MkdirAll(r.datadir(c.ID), 0700); err != nil { 521 return err 522 } 523 err := ioutils.AtomicWriteFile(r.datapath(c.ID, key), data, 0600) 524 if err == nil { 525 save := false 526 if c.BigDataSizes == nil { 527 c.BigDataSizes = make(map[string]int64) 528 } 529 oldSize, sizeOk := c.BigDataSizes[key] 530 c.BigDataSizes[key] = int64(len(data)) 531 if c.BigDataDigests == nil { 532 c.BigDataDigests = make(map[string]digest.Digest) 533 } 534 oldDigest, digestOk := c.BigDataDigests[key] 535 newDigest := digest.Canonical.FromBytes(data) 536 c.BigDataDigests[key] = newDigest 537 if !sizeOk || oldSize != c.BigDataSizes[key] || !digestOk || oldDigest != newDigest { 538 save = true 539 } 540 addName := true 541 for _, name := range c.BigDataNames { 542 if name == key { 543 addName = false 544 break 545 } 546 } 547 if addName { 548 c.BigDataNames = append(c.BigDataNames, key) 549 save = true 550 } 551 if save { 552 err = r.Save() 553 } 554 } 555 return err 556 } 557 558 func (r *containerStore) Wipe() error { 559 ids := make([]string, 0, len(r.byid)) 560 for id := range r.byid { 561 ids = append(ids, id) 562 } 563 for _, id := range ids { 564 if err := r.Delete(id); err != nil { 565 return err 566 } 567 } 568 return nil 569 } 570 571 func (r *containerStore) Lock() { 572 r.lockfile.Lock() 573 } 574 575 func (r *containerStore) RecursiveLock() { 576 r.lockfile.RecursiveLock() 577 } 578 579 func (r *containerStore) RLock() { 580 r.lockfile.RLock() 581 } 582 func (r *containerStore) Unlock() { 583 r.lockfile.Unlock() 584 } 585 586 func (r *containerStore) Touch() error { 587 return r.lockfile.Touch() 588 } 589 590 func (r *containerStore) Modified() (bool, error) { 591 return r.lockfile.Modified() 592 } 593 594 func (r *containerStore) IsReadWrite() bool { 595 return r.lockfile.IsReadWrite() 596 } 597 598 func (r *containerStore) TouchedSince(when time.Time) bool { 599 return r.lockfile.TouchedSince(when) 600 } 601 602 func (r *containerStore) Locked() bool { 603 return r.lockfile.Locked() 604 }