// RenderTreeStore renders a treestore for the given image key if it's not // already fully rendered. // Users of treestore should call s.RenderTreeStore before using it to ensure // that the treestore is completely rendered. func (s Store) RenderTreeStore(key string, rebuild bool) error { // this lock references the treestore dir for the specified key. This // is different from a lock on an image key as internally // treestore.Write calls the acirenderer functions that use GetACI and // GetImageManifest which are taking the image(s) lock. treeStoreKeyLock, err := lock.ExclusiveKeyLock(s.treeStoreLockDir, key) if err != nil { return fmt.Errorf("error locking tree store: %v", err) } defer treeStoreKeyLock.Close() if !rebuild { rendered, err := s.treestore.IsRendered(key) if err != nil { return fmt.Errorf("cannot determine if tree is already rendered: %v", err) } if rendered { return nil } } // Firstly remove a possible partial treestore if existing. // This is needed as a previous ACI removal operation could have failed // cleaning the tree store leaving some stale files. if err := s.treestore.Remove(key); err != nil { return err } if err := s.treestore.Write(key, &s); err != nil { return err } return nil }
// Render renders a treestore for the given image key if it's not // already fully rendered. // Users of treestore should call s.Render before using it to ensure // that the treestore is completely rendered. // Returns the id and hash of the rendered treestore if it is newly rendered, // and only the id if it is already rendered. func (ts *Store) Render(key string, rebuild bool) (id string, hash string, err error) { id, err = ts.GetID(key) if err != nil { return "", "", errwrap.Wrap(errors.New("cannot calculate treestore id"), err) } // this lock references the treestore dir for the specified id. treeStoreKeyLock, err := lock.ExclusiveKeyLock(ts.lockDir, id) if err != nil { return "", "", errwrap.Wrap(errors.New("error locking tree store"), err) } defer treeStoreKeyLock.Close() if !rebuild { rendered, err := ts.IsRendered(id) if err != nil { return "", "", errwrap.Wrap(errors.New("cannot determine if tree is already rendered"), err) } if rendered { return id, "", nil } } // Firstly remove a possible partial treestore if existing. // This is needed as a previous ACI removal operation could have failed // cleaning the tree store leaving some stale files. if err := ts.remove(id); err != nil { return "", "", err } if hash, err = ts.render(id, key); err != nil { return "", "", err } return id, hash, nil }
// gcTreeStore removes all treeStoreIDs not referenced by any non garbage // collected pod from the store. func gcTreeStore(s *store.Store) error { // Take an exclusive lock to block other pods being created. // This is needed to avoid races between the below steps (getting the // list of referenced treeStoreIDs, getting the list of treeStoreIDs // from the store, removal of unreferenced treeStoreIDs) and new // pods/treeStores being created/referenced keyLock, err := lock.ExclusiveKeyLock(lockDir(), common.PrepareLock) if err != nil { return fmt.Errorf("cannot get exclusive prepare lock: %v", err) } defer keyLock.Close() referencedTreeStoreIDs, err := getReferencedTreeStoreIDs() if err != nil { return fmt.Errorf("cannot get referenced treestoreIDs: %v", err) } treeStoreIDs, err := s.GetTreeStoreIDs() if err != nil { return fmt.Errorf("cannot get treestoreIDs from the store: %v", err) } for _, treeStoreID := range treeStoreIDs { if _, ok := referencedTreeStoreIDs[treeStoreID]; !ok { if err := s.RemoveTreeStore(treeStoreID); err != nil { stderr("rkt: error removing treestore %q: %v", treeStoreID, err) } else { stderr("rkt: removed treestore %q", treeStoreID) } } } return nil }
// gcTreeStore removes all treeStoreIDs not referenced by any non garbage // collected pod from the store. func gcTreeStore(ts *treestore.Store) error { // Take an exclusive lock to block other pods being created. // This is needed to avoid races between the below steps (getting the // list of referenced treeStoreIDs, getting the list of treeStoreIDs // from the store, removal of unreferenced treeStoreIDs) and new // pods/treeStores being created/referenced keyLock, err := lock.ExclusiveKeyLock(lockDir(), common.PrepareLock) if err != nil { return errwrap.Wrap(errors.New("cannot get exclusive prepare lock"), err) } defer keyLock.Close() referencedTreeStoreIDs, err := getReferencedTreeStoreIDs() if err != nil { return errwrap.Wrap(errors.New("cannot get referenced treestoreIDs"), err) } treeStoreIDs, err := ts.GetIDs() if err != nil { return errwrap.Wrap(errors.New("cannot get treestoreIDs from the store"), err) } for _, treeStoreID := range treeStoreIDs { if _, ok := referencedTreeStoreIDs[treeStoreID]; !ok { if err := ts.Remove(treeStoreID); err != nil { stderr.PrintE(fmt.Sprintf("error removing treestore %q", treeStoreID), err) } else { stderr.Printf("removed treestore %q", treeStoreID) } } } return nil }
// RenderTreeStore renders a treestore for the given image key if it's not // already fully rendered. // Users of treestore should call s.RenderTreeStore before using it to ensure // that the treestore is completely rendered. // Returns the id of the rendered treestore. func (s Store) RenderTreeStore(key string, rebuild bool) (string, error) { id, err := s.GetTreeStoreID(key) if err != nil { return "", fmt.Errorf("cannot calculate treestore id: %v", err) } // this lock references the treestore dir for the specified id. treeStoreKeyLock, err := lock.ExclusiveKeyLock(s.treeStoreLockDir, id) if err != nil { return "", fmt.Errorf("error locking tree store: %v", err) } defer treeStoreKeyLock.Close() if !rebuild { rendered, err := s.treestore.IsRendered(id) if err != nil { return "", fmt.Errorf("cannot determine if tree is already rendered: %v", err) } if rendered { return id, nil } } // Firstly remove a possible partial treestore if existing. // This is needed as a previous ACI removal operation could have failed // cleaning the tree store leaving some stale files. if err := s.treestore.Remove(id); err != nil { return "", err } if err := s.treestore.Write(id, key, &s); err != nil { return "", err } return id, nil }
// RemoveTreeStore removes the rendered image in tree store with the given key. func (ds Store) RemoveTreeStore(key string) error { treeStoreKeyLock, err := lock.ExclusiveKeyLock(ds.treeStoreLockDir, key) if err != nil { return fmt.Errorf("error locking tree store: %v", err) } defer treeStoreKeyLock.Close() if err := ds.treestore.Remove(key); err != nil { return fmt.Errorf("error removing the tree store: %v", err) } return nil }
// Remove removes the rendered image in tree store with the given id. func (ts *Store) Remove(id string) error { treeStoreKeyLock, err := lock.ExclusiveKeyLock(ts.lockDir, id) if err != nil { return errwrap.Wrap(errors.New("error locking tree store"), err) } defer treeStoreKeyLock.Close() if err := ts.remove(id); err != nil { return errwrap.Wrap(errors.New("error removing the tree store"), err) } return nil }
// RemoveTreeStore removes the rendered image in tree store with the given id. func (s *Store) RemoveTreeStore(id string) error { treeStoreKeyLock, err := lock.ExclusiveKeyLock(s.treeStoreLockDir, id) if err != nil { return fmt.Errorf("error locking tree store: %v", err) } defer treeStoreKeyLock.Close() if err := s.treestore.Remove(id, s); err != nil { return fmt.Errorf("error removing the tree store: %v", err) } return nil }
// RemoveACI removes the ACI with the given key. It firstly removes the aci // infos inside the db, then it tries to remove the non transactional data. // If some error occurs removing some non transactional data a // StoreRemovalError is returned. func (ds Store) RemoveACI(key string) error { imageKeyLock, err := lock.ExclusiveKeyLock(ds.imageLockDir, key) if err != nil { return fmt.Errorf("error locking image: %v", err) } defer imageKeyLock.Close() // Firstly remove aciinfo and remote from the db in an unique transaction. // remote needs to be removed or a GetRemote will return a blobKey not // referenced by any ACIInfo. err = ds.db.Do(func(tx *sql.Tx) error { if _, found, err := GetACIInfoWithBlobKey(tx, key); err != nil { return fmt.Errorf("error getting aciinfo: %v", err) } else if !found { return fmt.Errorf("cannot find image with key: %s", key) } if err := RemoveACIInfo(tx, key); err != nil { return err } if err := RemoveRemote(tx, key); err != nil { return err } return nil }) if err != nil { return fmt.Errorf("cannot remove image with key: %s from db: %v", key, err) } // Then remove non transactional entries from the blob, imageManifest // and tree store. // TODO(sgotti). Now that the ACIInfo is removed the image doesn't // exists anymore, but errors removing non transactional entries can // leave stale data that will require a cas GC to be implemented. storeErrors := []error{} for _, s := range ds.stores { if err := s.Erase(key); err != nil { // If there's an error save it and continue with the other stores storeErrors = append(storeErrors, err) } } if len(storeErrors) > 0 { return &StoreRemovalError{errors: storeErrors} } return nil }
// WriteACI takes an ACI encapsulated in an io.Reader, decompresses it if // necessary, and then stores it in the store under a key based on the image ID // (i.e. the hash of the uncompressed ACI) // latest defines if the aci has to be marked as the latest. For example an ACI // discovered without asking for a specific version (latest pattern). func (s Store) WriteACI(r io.ReadSeeker, latest bool) (string, error) { dr, err := aci.NewCompressedReader(r) if err != nil { return "", fmt.Errorf("error decompressing image: %v", err) } // Write the decompressed image (tar) to a temporary file on disk, and // tee so we can generate the hash h := sha512.New() tr := io.TeeReader(dr, h) fh, err := s.TmpFile() if err != nil { return "", fmt.Errorf("error creating image: %v", err) } if _, err := io.Copy(fh, tr); err != nil { return "", fmt.Errorf("error copying image: %v", err) } im, err := aci.ManifestFromImage(fh) if err != nil { return "", fmt.Errorf("error extracting image manifest: %v", err) } if err := fh.Close(); err != nil { return "", fmt.Errorf("error closing image: %v", err) } // Import the uncompressed image into the store at the real key key := s.HashToKey(h) keyLock, err := lock.ExclusiveKeyLock(s.imageLockDir, key) if err != nil { return "", fmt.Errorf("error locking image: %v", err) } defer keyLock.Close() if err = s.stores[blobType].Import(fh.Name(), key, true); err != nil { return "", fmt.Errorf("error importing image: %v", err) } // Save the imagemanifest using the same key used for the image imj, err := json.Marshal(im) if err != nil { return "", fmt.Errorf("error marshalling image manifest: %v", err) } if err = s.stores[imageManifestType].Write(key, imj); err != nil { return "", fmt.Errorf("error importing image manifest: %v", err) } // Save aciinfo if err = s.db.Do(func(tx *sql.Tx) error { aciinfo := &ACIInfo{ BlobKey: key, AppName: im.Name.String(), ImportTime: time.Now(), Latest: latest, } return WriteACIInfo(tx, aciinfo) }); err != nil { return "", fmt.Errorf("error writing ACI Info: %v", err) } // The treestore for this ACI is not written here as ACIs downloaded as // dependencies of another ACI will be exploded also if never directly used. // Users of treestore should call s.RenderTreeStore before using it. return key, nil }
// WriteACI takes an ACI encapsulated in an io.Reader, decompresses it if // necessary, and then stores it in the store under a key based on the image ID // (i.e. the hash of the uncompressed ACI) // latest defines if the aci has to be marked as the latest. For example an ACI // discovered without asking for a specific version (latest pattern). func (s Store) WriteACI(r io.Reader, latest bool) (string, error) { // Peek at the first 512 bytes of the reader to detect filetype br := bufio.NewReaderSize(r, 32768) hd, err := br.Peek(512) switch err { case nil: case io.EOF: // We may have still peeked enough to guess some types, so fall through default: return "", fmt.Errorf("error reading image header: %v", err) } typ, err := aci.DetectFileType(bytes.NewBuffer(hd)) if err != nil { return "", fmt.Errorf("error detecting image type: %v", err) } dr, err := decompress(br, typ) if err != nil { return "", fmt.Errorf("error decompressing image: %v", err) } // Write the decompressed image (tar) to a temporary file on disk, and // tee so we can generate the hash h := sha512.New() tr := io.TeeReader(dr, h) fh, err := s.TmpFile() if err != nil { return "", fmt.Errorf("error creating image: %v", err) } if _, err := io.Copy(fh, tr); err != nil { return "", fmt.Errorf("error copying image: %v", err) } im, err := aci.ManifestFromImage(fh) if err != nil { return "", fmt.Errorf("error extracting image manifest: %v", err) } if err := fh.Close(); err != nil { return "", fmt.Errorf("error closing image: %v", err) } // Import the uncompressed image into the store at the real key key := s.HashToKey(h) keyLock, err := lock.ExclusiveKeyLock(s.imageLockDir, key) if err != nil { return "", fmt.Errorf("error locking image: %v", err) } defer keyLock.Close() if err = s.stores[blobType].Import(fh.Name(), key, true); err != nil { return "", fmt.Errorf("error importing image: %v", err) } // Save the imagemanifest using the same key used for the image imj, err := json.Marshal(im) if err != nil { return "", fmt.Errorf("error marshalling image manifest: %v", err) } if err = s.stores[imageManifestType].Write(key, imj); err != nil { return "", fmt.Errorf("error importing image manifest: %v", err) } // Save aciinfo if err = s.db.Do(func(tx *sql.Tx) error { aciinfo := &ACIInfo{ BlobKey: key, AppName: im.Name.String(), ImportTime: time.Now(), Latest: latest, } return WriteACIInfo(tx, aciinfo) }); err != nil { return "", fmt.Errorf("error writing ACI Info: %v", err) } // The treestore for this ACI is not written here as ACIs downloaded as // dependencies of another ACI will be exploded also if never directly used. // Users of treestore should call s.RenderTreeStore before using it. return key, nil }
// WriteACI takes an ACI encapsulated in an io.Reader, decompresses it if // necessary, and then stores it in the store under a key based on the image ID // (i.e. the hash of the uncompressed ACI) // latest defines if the aci has to be marked as the latest. For example an ACI // discovered without asking for a specific version (latest pattern). func (s *Store) WriteACI(r io.ReadSeeker, fetchInfo ACIFetchInfo) (string, error) { // We need to allow the store's setgid bits (if any) to propagate, so // disable umask um := syscall.Umask(0) defer syscall.Umask(um) dr, err := aci.NewCompressedReader(r) if err != nil { return "", errwrap.Wrap(errors.New("error decompressing image"), err) } defer dr.Close() // Write the decompressed image (tar) to a temporary file on disk, and // tee so we can generate the hash h := sha512.New() tr := io.TeeReader(dr, h) fh, err := s.TmpFile() if err != nil { return "", errwrap.Wrap(errors.New("error creating image"), err) } sz, err := io.Copy(fh, tr) if err != nil { return "", errwrap.Wrap(errors.New("error copying image"), err) } im, err := aci.ManifestFromImage(fh) if err != nil { return "", errwrap.Wrap(errors.New("error extracting image manifest"), err) } if err := fh.Close(); err != nil { return "", errwrap.Wrap(errors.New("error closing image"), err) } // Import the uncompressed image into the store at the real key key := s.HashToKey(h) keyLock, err := lock.ExclusiveKeyLock(s.imageLockDir, key) if err != nil { return "", errwrap.Wrap(errors.New("error locking image"), err) } defer keyLock.Close() if err = s.stores[blobType].Import(fh.Name(), key, true); err != nil { return "", errwrap.Wrap(errors.New("error importing image"), err) } // Save the imagemanifest using the same key used for the image imj, err := json.Marshal(im) if err != nil { return "", errwrap.Wrap(errors.New("error marshalling image manifest"), err) } if err := s.stores[imageManifestType].Write(key, imj); err != nil { return "", errwrap.Wrap(errors.New("error importing image manifest"), err) } // Save aciinfo if err = s.db.Do(func(tx *sql.Tx) error { aciinfo := &ACIInfo{ BlobKey: key, Name: im.Name.String(), ImportTime: time.Now(), LastUsed: time.Now(), Latest: fetchInfo.Latest, Size: sz, } return WriteACIInfo(tx, aciinfo) }); err != nil { return "", errwrap.Wrap(errors.New("error writing ACI Info"), err) } return key, nil }
// RemoveACI removes the ACI with the given key. It firstly removes the aci // infos inside the db, then it tries to remove the non transactional data. // If some error occurs removing some non transactional data a // StoreRemovalError is returned. func (ds Store) RemoveACI(key string) error { imageKeyLock, err := lock.ExclusiveKeyLock(ds.imageLockDir, key) if err != nil { return fmt.Errorf("error locking image: %v", err) } defer imageKeyLock.Close() // Try to see if we are the owner of the images, if not, returns not enough permission error. for _, s := range ds.stores { // XXX: The construction of 'path' depends on the implementation of diskv. path := filepath.Join(s.BasePath, filepath.Join(s.Transform(key)...)) fi, err := os.Stat(path) if err != nil { return fmt.Errorf("cannot get the stat of the image directory: %v", err) } uid := os.Getuid() dirUid := int(fi.Sys().(*syscall.Stat_t).Uid) if uid != dirUid && uid != 0 { return fmt.Errorf("permission denied, are you root or the owner of the image?") } } // Firstly remove aciinfo and remote from the db in an unique transaction. // remote needs to be removed or a GetRemote will return a blobKey not // referenced by any ACIInfo. err = ds.db.Do(func(tx *sql.Tx) error { if _, found, err := GetACIInfoWithBlobKey(tx, key); err != nil { return fmt.Errorf("error getting aciinfo: %v", err) } else if !found { return fmt.Errorf("cannot find image with key: %s", key) } if err := RemoveACIInfo(tx, key); err != nil { return err } if err := RemoveRemote(tx, key); err != nil { return err } return nil }) if err != nil { return fmt.Errorf("cannot remove image with ID: %s from db: %v", key, err) } // Then remove non transactional entries from the blob, imageManifest // and tree store. // TODO(sgotti). Now that the ACIInfo is removed the image doesn't // exists anymore, but errors removing non transactional entries can // leave stale data that will require a cas GC to be implemented. storeErrors := []error{} for _, s := range ds.stores { if err := s.Erase(key); err != nil { // If there's an error save it and continue with the other stores storeErrors = append(storeErrors, err) } } if len(storeErrors) > 0 { return &StoreRemovalError{errors: storeErrors} } return nil }