github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/store/imagestore/store.go (about)

     1  // Copyright 2014 The rkt Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package imagestore
    16  
    17  import (
    18  	"crypto/sha512"
    19  	"database/sql"
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  	"hash"
    24  	"io"
    25  	"io/ioutil"
    26  	"os"
    27  	"path/filepath"
    28  	"strings"
    29  	"syscall"
    30  	"time"
    31  
    32  	"github.com/rkt/rkt/pkg/backup"
    33  	"github.com/rkt/rkt/pkg/lock"
    34  	"github.com/rkt/rkt/store/db"
    35  
    36  	"github.com/appc/spec/aci"
    37  	"github.com/appc/spec/schema"
    38  	"github.com/appc/spec/schema/types"
    39  
    40  	"github.com/hashicorp/errwrap"
    41  	"github.com/peterbourgon/diskv"
    42  )
    43  
    44  const (
    45  	blobType int64 = iota
    46  	imageManifestType
    47  
    48  	defaultPathPerm = os.FileMode(0770 | os.ModeSetgid)
    49  	defaultFilePerm = os.FileMode(0660)
    50  
    51  	// To ameliorate excessively long paths, keys for the (blob)store use
    52  	// only the first half of a sha512 rather than the entire sum
    53  	hashPrefix = "sha512-"
    54  	lenHash    = sha512.Size       // raw byte size
    55  	lenHashKey = (lenHash / 2) * 2 // half length, in hex characters
    56  	lenKey     = len(hashPrefix) + lenHashKey
    57  	minlenKey  = len(hashPrefix) + 2 // at least sha512-aa
    58  
    59  	// how many backups to keep when migrating to new db version
    60  	backupsNumber = 5
    61  )
    62  
    63  var diskvStores = [...]string{
    64  	"blob",
    65  	"imageManifest",
    66  }
    67  
    68  var (
    69  	ErrKeyNotFound       = errors.New("no image IDs found")
    70  	ErrDBUpdateNeedsRoot = errors.New("database schema needs to be updated, re-run as root to perform the update")
    71  )
    72  
    73  // ACINotFoundError is returned when an ACI cannot be found by GetACI
    74  // Useful to distinguish a generic error from an aci not found.
    75  type ACINotFoundError struct {
    76  	name   types.ACIdentifier
    77  	labels types.Labels
    78  }
    79  
    80  func (e ACINotFoundError) Error() string {
    81  	return fmt.Sprintf(
    82  		"cannot find aci satisfying name: %q and labels: %q in the local store",
    83  		e.name, e.labels)
    84  }
    85  
    86  // StoreRemovalError defines an error removing a non transactional store (like
    87  // a diskv store).
    88  // When this happen there's the possibility that the store is left in an
    89  // unclean state (for example with some stale files).
    90  type StoreRemovalError struct {
    91  	errors []error
    92  }
    93  
    94  func (e *StoreRemovalError) Error() string {
    95  	s := fmt.Sprintf("some aci disk entries cannot be removed: ")
    96  	for _, err := range e.errors {
    97  		s = s + fmt.Sprintf("[%v]", err)
    98  	}
    99  	return s
   100  }
   101  
   102  // Store encapsulates a content-addressable-storage for storing ACIs on disk.
   103  type Store struct {
   104  	dir    string
   105  	stores []*diskv.Diskv
   106  	db     *db.DB
   107  	// storeLock is a lock on the whole store. It's used for store migration. If
   108  	// a previous version of rkt is using the store and in the meantime a
   109  	// new version is installed and executed it will try migrate the store
   110  	// during NewStore. This means that the previous running rkt will fail
   111  	// or behave badly after the migration as it's expecting another db format.
   112  	// For this reason, before executing migration, an exclusive lock must
   113  	// be taken on the whole store.
   114  	storeLock    *lock.FileLock
   115  	imageLockDir string
   116  }
   117  
   118  func (s *Store) updateSize(key string, newSize int64) error {
   119  	return s.db.Do(func(tx *sql.Tx) error {
   120  		_, err := tx.Exec("UPDATE aciinfo SET size = $1 WHERE blobkey == $2", newSize, key)
   121  		return err
   122  	})
   123  }
   124  
   125  // TODO(sgotti) remove this when the treestore will save its images' sizes by itself
   126  func (s *Store) UpdateTreeStoreSize(key string, newSize int64) error {
   127  	return s.db.Do(func(tx *sql.Tx) error {
   128  		_, err := tx.Exec("UPDATE aciinfo SET treestoresize = $1 WHERE blobkey == $2", newSize, key)
   129  		return err
   130  	})
   131  }
   132  
   133  func (s *Store) populateSize() error {
   134  	var ais []*ACIInfo
   135  	err := s.db.Do(func(tx *sql.Tx) error {
   136  		var err error
   137  		ais, err = GetACIInfosWithKeyPrefix(tx, "")
   138  		return err
   139  	})
   140  	if err != nil {
   141  		return errwrap.Wrap(errors.New("error retrieving ACI Infos"), err)
   142  	}
   143  
   144  	aciSizes := make(map[string]int64)
   145  	for _, ai := range ais {
   146  		key := ai.BlobKey
   147  
   148  		im, err := s.ReadStream(key)
   149  		if err != nil {
   150  			return err
   151  		}
   152  
   153  		rd, err := io.Copy(ioutil.Discard, im)
   154  		if err != nil {
   155  			return err
   156  		}
   157  		aciSizes[key] = rd
   158  
   159  	}
   160  
   161  	for k := range aciSizes {
   162  		s.updateSize(k, aciSizes[k])
   163  	}
   164  
   165  	return nil
   166  }
   167  
   168  func NewStore(dir string) (*Store, error) {
   169  	// We need to allow the store's setgid bits (if any) to propagate, so
   170  	// disable umask
   171  	um := syscall.Umask(0)
   172  	defer syscall.Umask(um)
   173  
   174  	s := &Store{
   175  		dir:    dir,
   176  		stores: make([]*diskv.Diskv, len(diskvStores)),
   177  	}
   178  
   179  	s.imageLockDir = filepath.Join(dir, "imagelocks")
   180  	err := os.MkdirAll(s.imageLockDir, defaultPathPerm)
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  
   185  	// Take a shared cas lock
   186  	s.storeLock, err = lock.NewLock(dir, lock.Dir)
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  	if err := s.storeLock.SharedLock(); err != nil {
   191  		return nil, err
   192  	}
   193  
   194  	for i, p := range diskvStores {
   195  		s.stores[i] = diskv.New(diskv.Options{
   196  			PathPerm:  defaultPathPerm,
   197  			FilePerm:  defaultFilePerm,
   198  			BasePath:  filepath.Join(dir, p),
   199  			Transform: blockTransform,
   200  		})
   201  	}
   202  	db, err := db.NewDB(s.dbDir())
   203  	if err != nil {
   204  		return nil, err
   205  	}
   206  	s.db = db
   207  
   208  	needsMigrate := false
   209  	needsSizePopulation := false
   210  	fn := func(tx *sql.Tx) error {
   211  		var err error
   212  		ok, err := dbIsPopulated(tx)
   213  		if err != nil {
   214  			return err
   215  		}
   216  		// populate the db
   217  		if !ok {
   218  			for _, stmt := range dbCreateStmts {
   219  				_, err = tx.Exec(stmt)
   220  				if err != nil {
   221  					return err
   222  				}
   223  			}
   224  			return nil
   225  		}
   226  		// if db is populated check its version
   227  		version, err := getDBVersion(tx)
   228  		if err != nil {
   229  			return err
   230  		}
   231  		if version < dbVersion {
   232  			needsMigrate = true
   233  		}
   234  		if version > dbVersion {
   235  			return fmt.Errorf("current store db version: %d (greater than the current rkt expected version: %d)", version, dbVersion)
   236  		}
   237  		if version < 5 {
   238  			needsSizePopulation = true
   239  		}
   240  		return nil
   241  	}
   242  	if err = db.Do(fn); err != nil {
   243  		return nil, err
   244  	}
   245  
   246  	// migration is done in another transaction as it must take an exclusive
   247  	// store lock. If, in the meantime, another process has already done the
   248  	// migration, between the previous db version check and the below
   249  	// migration code, the migration will do nothing as it'll start
   250  	// migration from the current version.
   251  	if needsMigrate {
   252  		// Take an exclusive store lock
   253  		err := s.storeLock.ExclusiveLock()
   254  		if err != nil {
   255  			return nil, err
   256  		}
   257  		if err := s.backupDB(); err != nil {
   258  			return nil, err
   259  		}
   260  		fn := func(tx *sql.Tx) error {
   261  			return migrate(tx, dbVersion)
   262  		}
   263  		if err = db.Do(fn); err != nil {
   264  			return nil, err
   265  		}
   266  
   267  		if needsSizePopulation {
   268  			if err := s.populateSize(); err != nil {
   269  				return nil, err
   270  			}
   271  		}
   272  	}
   273  
   274  	return s, nil
   275  }
   276  
   277  // Close closes a Store opened with NewStore().
   278  func (s *Store) Close() error {
   279  	return s.storeLock.Close()
   280  }
   281  
   282  // backupDB backs up current database.
   283  func (s *Store) backupDB() error {
   284  	if os.Geteuid() != 0 {
   285  		return ErrDBUpdateNeedsRoot
   286  	}
   287  	backupsDir := filepath.Join(s.dir, "db-backups")
   288  	return backup.CreateBackup(s.dbDir(), backupsDir, backupsNumber)
   289  }
   290  
   291  func (s *Store) dbDir() string {
   292  	return filepath.Join(s.dir, "db")
   293  }
   294  
   295  // TODO(sgotti), unexport this and provide other functions for external users
   296  // TmpFile returns an *os.File local to the same filesystem as the Store, or
   297  // any error encountered
   298  func (s *Store) TmpFile() (*os.File, error) {
   299  	dir, err := s.TmpDir()
   300  	if err != nil {
   301  		return nil, err
   302  	}
   303  	return ioutil.TempFile(dir, "")
   304  }
   305  
   306  // TODO(sgotti), unexport this and provide other functions for external users
   307  // TmpNamedFile returns an *os.File with the specified name local to the same
   308  // filesystem as the Store, or any error encountered. If the file already
   309  // exists it will return the existing file in read/write mode with the cursor
   310  // at the end of the file.
   311  func (s Store) TmpNamedFile(name string) (*os.File, error) {
   312  	dir, err := s.TmpDir()
   313  	if err != nil {
   314  		return nil, err
   315  	}
   316  	fname := filepath.Join(dir, name)
   317  	_, err = os.Stat(fname)
   318  	if os.IsNotExist(err) {
   319  		return os.Create(fname)
   320  	}
   321  	if err != nil {
   322  		return nil, err
   323  	}
   324  	return os.OpenFile(fname, os.O_RDWR|os.O_APPEND, 0644)
   325  }
   326  
   327  // TODO(sgotti), unexport this and provide other functions for external users
   328  // TmpDir creates and returns dir local to the same filesystem as the Store,
   329  // or any error encountered
   330  func (s *Store) TmpDir() (string, error) {
   331  	dir := filepath.Join(s.dir, "tmp")
   332  	if err := os.MkdirAll(dir, defaultPathPerm); err != nil {
   333  		return "", err
   334  	}
   335  	return dir, nil
   336  }
   337  
   338  // ResolveKey resolves a partial key (of format `sha512-0c45e8c0ab2`) to a full
   339  // key by considering the key a prefix and using the store for resolution.
   340  // If the key is longer than the full key length, it is first truncated.
   341  func (s *Store) ResolveKey(key string) (string, error) {
   342  	if !strings.HasPrefix(key, hashPrefix) {
   343  		return "", fmt.Errorf("wrong key prefix")
   344  	}
   345  	if len(key) < minlenKey {
   346  		return "", fmt.Errorf("image ID too short")
   347  	}
   348  	if len(key) > lenKey {
   349  		key = key[:lenKey]
   350  	}
   351  
   352  	var aciInfos []*ACIInfo
   353  	err := s.db.Do(func(tx *sql.Tx) error {
   354  		var err error
   355  		aciInfos, err = GetACIInfosWithKeyPrefix(tx, key)
   356  		return err
   357  	})
   358  	if err != nil {
   359  		return "", errwrap.Wrap(errors.New("error retrieving ACI Infos"), err)
   360  	}
   361  
   362  	keyCount := len(aciInfos)
   363  	if keyCount == 0 {
   364  		return "", ErrKeyNotFound
   365  	}
   366  	if keyCount != 1 {
   367  		return "", fmt.Errorf("ambiguous image ID: %q", key)
   368  	}
   369  	return aciInfos[0].BlobKey, nil
   370  }
   371  
   372  // ResolveName resolves an image name to a list of full keys and using the
   373  // store for resolution.
   374  func (s *Store) ResolveName(name string) ([]string, bool, error) {
   375  	var (
   376  		aciInfos []*ACIInfo
   377  		found    bool
   378  	)
   379  	err := s.db.Do(func(tx *sql.Tx) error {
   380  		var err error
   381  		aciInfos, found, err = GetACIInfosWithName(tx, name)
   382  		return err
   383  	})
   384  	if err != nil {
   385  		return nil, found, errwrap.Wrap(errors.New("error retrieving ACI Infos"), err)
   386  	}
   387  
   388  	keys := make([]string, len(aciInfos))
   389  	for i, aciInfo := range aciInfos {
   390  		keys[i] = aciInfo.BlobKey
   391  	}
   392  
   393  	return keys, found, nil
   394  }
   395  
   396  func (s *Store) ReadStream(key string) (io.ReadCloser, error) {
   397  	key, err := s.ResolveKey(key)
   398  	if err != nil {
   399  		return nil, errwrap.Wrap(errors.New("error resolving image ID"), err)
   400  	}
   401  	keyLock, err := lock.SharedKeyLock(s.imageLockDir, key)
   402  	if err != nil {
   403  		return nil, errwrap.Wrap(errors.New("error locking image"), err)
   404  	}
   405  	defer keyLock.Close()
   406  
   407  	err = s.db.Do(func(tx *sql.Tx) error {
   408  		aciinfo, found, err := GetACIInfoWithBlobKey(tx, key)
   409  		if err != nil {
   410  			return errwrap.Wrap(errors.New("error getting aciinfo"), err)
   411  		} else if !found {
   412  			return fmt.Errorf("cannot find image with key: %s", key)
   413  		}
   414  
   415  		aciinfo.LastUsed = time.Now()
   416  
   417  		return WriteACIInfo(tx, aciinfo)
   418  	})
   419  	if err != nil {
   420  		return nil, errwrap.Wrap(fmt.Errorf("cannot get image info for %q from db", key), err)
   421  	}
   422  
   423  	return s.stores[blobType].ReadStream(key, false)
   424  }
   425  
   426  // WriteACI takes an ACI encapsulated in an io.Reader, decompresses it if
   427  // necessary, and then stores it in the store under a key based on the image ID
   428  // (i.e. the hash of the uncompressed ACI)
   429  // latest defines if the aci has to be marked as the latest. For example an ACI
   430  // discovered without asking for a specific version (latest pattern).
   431  func (s *Store) WriteACI(r io.ReadSeeker, fetchInfo ACIFetchInfo) (string, error) {
   432  	// We need to allow the store's setgid bits (if any) to propagate, so
   433  	// disable umask
   434  	um := syscall.Umask(0)
   435  	defer syscall.Umask(um)
   436  
   437  	dr, err := aci.NewCompressedReader(r)
   438  	if err != nil {
   439  		return "", errwrap.Wrap(errors.New("error decompressing image"), err)
   440  	}
   441  	defer dr.Close()
   442  
   443  	// Write the decompressed image (tar) to a temporary file on disk, and
   444  	// tee so we can generate the hash
   445  	h := sha512.New()
   446  	tr := io.TeeReader(dr, h)
   447  	fh, err := s.TmpFile()
   448  	if err != nil {
   449  		return "", errwrap.Wrap(errors.New("error creating image"), err)
   450  	}
   451  	sz, err := io.Copy(fh, tr)
   452  	if err != nil {
   453  		return "", errwrap.Wrap(errors.New("error copying image"), err)
   454  	}
   455  	im, err := aci.ManifestFromImage(fh)
   456  	if err != nil {
   457  		return "", errwrap.Wrap(errors.New("error extracting image manifest"), err)
   458  	}
   459  	if err := fh.Close(); err != nil {
   460  		return "", errwrap.Wrap(errors.New("error closing image"), err)
   461  	}
   462  
   463  	// Import the uncompressed image into the store at the real key
   464  	key := s.HashToKey(h)
   465  	keyLock, err := lock.ExclusiveKeyLock(s.imageLockDir, key)
   466  	if err != nil {
   467  		return "", errwrap.Wrap(errors.New("error locking image"), err)
   468  	}
   469  	defer keyLock.Close()
   470  
   471  	if err = s.stores[blobType].Import(fh.Name(), key, true); err != nil {
   472  		return "", errwrap.Wrap(errors.New("error importing image"), err)
   473  	}
   474  
   475  	// Save the imagemanifest using the same key used for the image
   476  	imj, err := json.Marshal(im)
   477  	if err != nil {
   478  		return "", errwrap.Wrap(errors.New("error marshalling image manifest"), err)
   479  	}
   480  	if err := s.stores[imageManifestType].Write(key, imj); err != nil {
   481  		return "", errwrap.Wrap(errors.New("error importing image manifest"), err)
   482  	}
   483  
   484  	// Save aciinfo
   485  	if err = s.db.Do(func(tx *sql.Tx) error {
   486  		aciinfo := &ACIInfo{
   487  			BlobKey:    key,
   488  			Name:       im.Name.String(),
   489  			ImportTime: time.Now(),
   490  			LastUsed:   time.Now(),
   491  			Latest:     fetchInfo.Latest,
   492  			Size:       sz,
   493  		}
   494  		return WriteACIInfo(tx, aciinfo)
   495  	}); err != nil {
   496  		return "", errwrap.Wrap(errors.New("error writing ACI Info"), err)
   497  	}
   498  
   499  	return key, nil
   500  }
   501  
   502  // RemoveACI removes the ACI with the given key. It firstly removes the aci
   503  // infos inside the db, then it tries to remove the non transactional data.
   504  // If some error occurs removing some non transactional data a
   505  // StoreRemovalError is returned.
   506  func (s *Store) RemoveACI(key string) error {
   507  	imageKeyLock, err := lock.ExclusiveKeyLock(s.imageLockDir, key)
   508  	if err != nil {
   509  		return errwrap.Wrap(errors.New("error locking image"), err)
   510  	}
   511  	defer imageKeyLock.Close()
   512  
   513  	// Firstly remove aciinfo and remote from the db in an unique transaction.
   514  	// remote needs to be removed or a GetRemote will return a blobKey not
   515  	// referenced by any ACIInfo.
   516  	err = s.db.Do(func(tx *sql.Tx) error {
   517  		if _, found, err := GetACIInfoWithBlobKey(tx, key); err != nil {
   518  			return errwrap.Wrap(errors.New("error getting aciinfo"), err)
   519  		} else if !found {
   520  			return fmt.Errorf("cannot find image with key: %s", key)
   521  		}
   522  
   523  		if err := RemoveACIInfo(tx, key); err != nil {
   524  			return err
   525  		}
   526  		if err := RemoveRemote(tx, key); err != nil {
   527  			return err
   528  		}
   529  		return nil
   530  	})
   531  	if err != nil {
   532  		return errwrap.Wrap(fmt.Errorf("cannot remove image with ID: %s from db", key), err)
   533  	}
   534  
   535  	// Then remove non transactional entries from the blob, imageManifest
   536  	// and tree store.
   537  	// TODO(sgotti). Now that the ACIInfo is removed the image doesn't
   538  	// exists anymore, but errors removing non transactional entries can
   539  	// leave stale data that will require a cas GC to be implemented.
   540  	var storeErrors []error
   541  	for _, ds := range s.stores {
   542  		if err := ds.Erase(key); err != nil {
   543  			// If there's an error save it and continue with the other stores
   544  			storeErrors = append(storeErrors, err)
   545  		}
   546  	}
   547  	if len(storeErrors) > 0 {
   548  		return &StoreRemovalError{errors: storeErrors}
   549  	}
   550  	return nil
   551  }
   552  
   553  // GetRemote tries to retrieve a remote with the given ACIURL.
   554  // If remote doesn't exist, it returns ErrRemoteNotFound error.
   555  func (s *Store) GetRemote(aciURL string) (*Remote, error) {
   556  	var remote *Remote
   557  
   558  	err := s.db.Do(func(tx *sql.Tx) error {
   559  		var err error
   560  
   561  		remote, err = GetRemote(tx, aciURL)
   562  
   563  		return err
   564  	})
   565  	if err != nil {
   566  		return nil, err
   567  	}
   568  
   569  	return remote, nil
   570  }
   571  
   572  // WriteRemote adds or updates the provided Remote.
   573  func (s *Store) WriteRemote(remote *Remote) error {
   574  	err := s.db.Do(func(tx *sql.Tx) error {
   575  		return WriteRemote(tx, remote)
   576  	})
   577  	return err
   578  }
   579  
   580  // GetImageManifestJSON gets the ImageManifest JSON bytes with the
   581  // specified key.
   582  func (s *Store) GetImageManifestJSON(key string) ([]byte, error) {
   583  	key, err := s.ResolveKey(key)
   584  	if err != nil {
   585  		return nil, errwrap.Wrap(errors.New("error resolving image ID"), err)
   586  	}
   587  	keyLock, err := lock.SharedKeyLock(s.imageLockDir, key)
   588  	if err != nil {
   589  		return nil, errwrap.Wrap(errors.New("error locking image"), err)
   590  	}
   591  	defer keyLock.Close()
   592  
   593  	imj, err := s.stores[imageManifestType].Read(key)
   594  	if err != nil {
   595  		return nil, errwrap.Wrap(errors.New("error retrieving image manifest"), err)
   596  	}
   597  	return imj, nil
   598  }
   599  
   600  // GetImageManifest gets the ImageManifest with the specified key.
   601  func (s *Store) GetImageManifest(key string) (*schema.ImageManifest, error) {
   602  	imj, err := s.GetImageManifestJSON(key)
   603  	if err != nil {
   604  		return nil, err
   605  	}
   606  	var im *schema.ImageManifest
   607  	if err = json.Unmarshal(imj, &im); err != nil {
   608  		return nil, errwrap.Wrap(errors.New("error unmarshalling image manifest"), err)
   609  	}
   610  	return im, nil
   611  }
   612  
   613  // GetACI retrieves the ACI that best matches the provided app name and labels.
   614  // The returned value is the blob store key of the retrieved ACI.
   615  // If there are multiple matching ACIs choose the latest one (defined as the
   616  // last one imported in the store).
   617  // If no version label is requested, ACIs marked as latest in the ACIInfo are
   618  // preferred.
   619  func (s *Store) GetACI(name types.ACIdentifier, labels types.Labels) (string, error) {
   620  	var curaciinfo *ACIInfo
   621  	versionRequested := false
   622  	if _, ok := labels.Get("version"); ok {
   623  		versionRequested = true
   624  	}
   625  
   626  	var aciinfos []*ACIInfo
   627  	err := s.db.Do(func(tx *sql.Tx) error {
   628  		var err error
   629  		aciinfos, _, err = GetACIInfosWithName(tx, name.String())
   630  		return err
   631  	})
   632  	if err != nil {
   633  		return "", err
   634  	}
   635  
   636  nextKey:
   637  	for _, aciinfo := range aciinfos {
   638  		im, err := s.GetImageManifest(aciinfo.BlobKey)
   639  		if err != nil {
   640  			return "", errwrap.Wrap(errors.New("error getting image manifest"), err)
   641  		}
   642  
   643  		// The image manifest must have all the requested labels
   644  		for _, l := range labels {
   645  			ok := false
   646  			for _, rl := range im.Labels {
   647  				if l.Name == rl.Name && l.Value == rl.Value {
   648  					ok = true
   649  					break
   650  				}
   651  			}
   652  			if !ok {
   653  				continue nextKey
   654  			}
   655  		}
   656  
   657  		if curaciinfo != nil {
   658  			// If no version is requested prefer the acis marked as latest
   659  			if !versionRequested {
   660  				if !curaciinfo.Latest && aciinfo.Latest {
   661  					curaciinfo = aciinfo
   662  					continue nextKey
   663  				}
   664  				if curaciinfo.Latest && !aciinfo.Latest {
   665  					continue nextKey
   666  				}
   667  			}
   668  			// If multiple matching image manifests are found, choose the latest imported in the cas.
   669  			if aciinfo.ImportTime.After(curaciinfo.ImportTime) {
   670  				curaciinfo = aciinfo
   671  			}
   672  		} else {
   673  			curaciinfo = aciinfo
   674  		}
   675  	}
   676  
   677  	if curaciinfo != nil {
   678  		return curaciinfo.BlobKey, nil
   679  	}
   680  	return "", ACINotFoundError{name: name, labels: labels}
   681  }
   682  
   683  func (s *Store) GetAllRemotes() ([]*Remote, error) {
   684  	var remotes []*Remote
   685  	err := s.db.Do(func(tx *sql.Tx) error {
   686  		var err error
   687  		remotes, err = GetAllRemotes(tx)
   688  		return err
   689  	})
   690  	if err != nil {
   691  		return nil, err
   692  	}
   693  
   694  	return remotes, nil
   695  }
   696  
   697  func (s *Store) GetAllACIInfos(sortfields []string, ascending bool) ([]*ACIInfo, error) {
   698  	var aciInfos []*ACIInfo
   699  	err := s.db.Do(func(tx *sql.Tx) error {
   700  		var err error
   701  		aciInfos, err = GetAllACIInfos(tx, sortfields, ascending)
   702  		return err
   703  	})
   704  	return aciInfos, err
   705  }
   706  
   707  func (s *Store) GetACIInfoWithBlobKey(blobKey string) (*ACIInfo, error) {
   708  	var aciInfo *ACIInfo
   709  	var found bool
   710  	err := s.db.Do(func(tx *sql.Tx) error {
   711  		var err error
   712  		aciInfo, found, err = GetACIInfoWithBlobKey(tx, blobKey)
   713  		return err
   714  	})
   715  	if !found {
   716  		if err != nil {
   717  			err = errwrap.Wrap(fmt.Errorf("ACI info not found with blob key %q", blobKey), err)
   718  		} else {
   719  			err = fmt.Errorf("ACI info not found with blob key %q", blobKey)
   720  		}
   721  	}
   722  	return aciInfo, err
   723  }
   724  
   725  func (s *Store) Dump(hex bool) {
   726  	for _, ds := range s.stores {
   727  		var keyCount int
   728  		for key := range ds.Keys(nil) {
   729  			val, err := ds.Read(key)
   730  			if err != nil {
   731  				panic(fmt.Sprintf("key %s had no value", key))
   732  			}
   733  			if len(val) > 128 {
   734  				val = val[:128]
   735  			}
   736  			out := string(val)
   737  			if hex {
   738  				out = fmt.Sprintf("%x", val)
   739  			}
   740  			fmt.Printf("%s/%s: %s\n", ds.BasePath, key, out)
   741  			keyCount++
   742  		}
   743  		fmt.Printf("%d total image ID(s)\n", keyCount)
   744  	}
   745  }
   746  
   747  // HashToKey takes a hash.Hash (which currently _MUST_ represent a full SHA512),
   748  // calculates its sum, and returns a string which should be used as the key to
   749  // store the data matching the hash.
   750  func (s *Store) HashToKey(h hash.Hash) string {
   751  	return hashToKey(h)
   752  }
   753  
   754  // HasFullKey returns whether the image with the given key exists on the disk by
   755  // checking if the image manifest kv store contains the key.
   756  func (s *Store) HasFullKey(key string) bool {
   757  	return s.stores[imageManifestType].Has(key)
   758  }
   759  
   760  func hashToKey(h hash.Hash) string {
   761  	s := h.Sum(nil)
   762  	return keyToString(s)
   763  }
   764  
   765  // keyToString takes a key and returns a shortened and prefixed hexadecimal string version
   766  func keyToString(k []byte) string {
   767  	if len(k) != lenHash {
   768  		panic(fmt.Sprintf("bad hash passed to hashToKey: %x", k))
   769  	}
   770  	return fmt.Sprintf("%s%x", hashPrefix, k)[0:lenKey]
   771  }