func testMigrate(tt migrateTest) error { dir, err := ioutil.TempDir("", tstprefix) if err != nil { return fmt.Errorf("error creating tempdir: %v", err) } defer os.RemoveAll(dir) storeDir := filepath.Join(dir, "store") db, err := db.NewDB(filepath.Join(storeDir, "db")) if err != nil { return err } if err = tt.predb.populate(db); err != nil { return err } fn := func(tx *sql.Tx) error { err := migrate(tx, tt.postdb.version()) if err != nil { return err } return nil } if err = db.Do(fn); err != nil { return err } var curDBVersion int fn = func(tx *sql.Tx) error { var err error curDBVersion, err = getDBVersion(tx) if err != nil { return err } return nil } if err = db.Do(fn); err != nil { return err } if curDBVersion != tt.postdb.version() { return fmt.Errorf("wrong db version: got %#v, want %#v", curDBVersion, tt.postdb.version()) } if err := tt.curdb.load(db); err != nil { return err } if !tt.curdb.compare(tt.postdb) { return spew.Errorf("while comparing DBs:\n\tgot %#v\n\twant %#v\n", tt.curdb, tt.postdb) } return nil }
func NewStore(dir string) (*Store, 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) s := &Store{ dir: dir, stores: make([]*diskv.Diskv, len(diskvStores)), } s.imageLockDir = filepath.Join(dir, "imagelocks") err := os.MkdirAll(s.imageLockDir, defaultPathPerm) if err != nil { return nil, err } // Take a shared cas lock s.storeLock, err = lock.NewLock(dir, lock.Dir) if err != nil { return nil, err } if err := s.storeLock.SharedLock(); err != nil { return nil, err } for i, p := range diskvStores { s.stores[i] = diskv.New(diskv.Options{ PathPerm: defaultPathPerm, FilePerm: defaultFilePerm, BasePath: filepath.Join(dir, p), Transform: blockTransform, }) } db, err := db.NewDB(s.dbDir()) if err != nil { return nil, err } s.db = db needsMigrate := false needsSizePopulation := false fn := func(tx *sql.Tx) error { var err error ok, err := dbIsPopulated(tx) if err != nil { return err } // populate the db if !ok { for _, stmt := range dbCreateStmts { _, err = tx.Exec(stmt) if err != nil { return err } } return nil } // if db is populated check its version version, err := getDBVersion(tx) if err != nil { return err } if version < dbVersion { needsMigrate = true } if version > dbVersion { return fmt.Errorf("current store db version: %d (greater than the current rkt expected version: %d)", version, dbVersion) } if version < 5 { needsSizePopulation = true } return nil } if err = db.Do(fn); err != nil { return nil, err } // migration is done in another transaction as it must take an exclusive // store lock. If, in the meantime, another process has already done the // migration, between the previous db version check and the below // migration code, the migration will do nothing as it'll start // migration from the current version. if needsMigrate { // Take an exclusive store lock err := s.storeLock.ExclusiveLock() if err != nil { return nil, err } if err := s.backupDB(); err != nil { return nil, err } fn := func(tx *sql.Tx) error { return migrate(tx, dbVersion) } if err = db.Do(fn); err != nil { return nil, err } if needsSizePopulation { if err := s.populateSize(); err != nil { return nil, err } } } return s, nil }