Exemplo n.º 1
0
// 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
}
Exemplo n.º 2
0
// 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
}
Exemplo n.º 3
0
// 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
}
Exemplo n.º 4
0
Arquivo: image_gc.go Projeto: nak3/rkt
// 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
}
Exemplo n.º 5
0
// 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
}
Exemplo n.º 6
0
// 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
}
Exemplo n.º 7
0
// 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
}
Exemplo n.º 8
0
// 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
}
Exemplo n.º 9
0
// 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
}
Exemplo n.º 10
0
// 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
}
Exemplo n.º 11
0
// 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
}
Exemplo n.º 12
0
Arquivo: store.go Projeto: nhlfr/rkt
// 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
}
Exemplo n.º 13
0
// 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
}