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

     1  // Copyright 2015 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  	"database/sql"
    19  	"fmt"
    20  	"io/ioutil"
    21  	"os"
    22  	"path/filepath"
    23  	"reflect"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/davecgh/go-spew/spew"
    28  	"github.com/jonboulle/clockwork"
    29  	"github.com/rkt/rkt/store/db"
    30  )
    31  
    32  type testdb interface {
    33  	version() int
    34  	populate(db *db.DB) error
    35  	load(db *db.DB) error
    36  	compare(db testdb) bool
    37  }
    38  
    39  type DBV0 struct {
    40  	aciinfos []*ACIInfoV0_2
    41  	remotes  []*RemoteV0_1
    42  }
    43  
    44  func (d *DBV0) version() int {
    45  	return 0
    46  }
    47  
    48  func (d *DBV0) populate(db *db.DB) error {
    49  	// As DBV0 and DBV1 have the same schema use a common populate
    50  	// function.
    51  	return populateDBV0_1(db, d.version(), d.aciinfos, d.remotes)
    52  }
    53  
    54  // load populates the given struct with the data in db.
    55  // the given struct d should be empty
    56  func (d *DBV0) load(db *db.DB) error {
    57  	fn := func(tx *sql.Tx) error {
    58  		var err error
    59  		d.aciinfos, err = getAllACIInfosV0_2(tx)
    60  		if err != nil {
    61  			return err
    62  		}
    63  		d.remotes, err = getAllRemoteV0_1(tx)
    64  		if err != nil {
    65  			return err
    66  		}
    67  		return nil
    68  	}
    69  	if err := db.Do(fn); err != nil {
    70  		return err
    71  	}
    72  	return nil
    73  }
    74  
    75  func (d *DBV0) compare(td testdb) bool {
    76  	d2, ok := td.(*DBV0)
    77  	if !ok {
    78  		return false
    79  	}
    80  	if !compareSlicesNoOrder(d.aciinfos, d2.aciinfos) {
    81  		return false
    82  	}
    83  	if !compareSlicesNoOrder(d.remotes, d2.remotes) {
    84  		return false
    85  	}
    86  	return true
    87  }
    88  
    89  type DBV1 struct {
    90  	aciinfos []*ACIInfoV0_2
    91  	remotes  []*RemoteV0_1
    92  }
    93  
    94  func (d *DBV1) version() int {
    95  	return 1
    96  }
    97  func (d *DBV1) populate(db *db.DB) error {
    98  	return populateDBV0_1(db, d.version(), d.aciinfos, d.remotes)
    99  }
   100  
   101  func (d *DBV1) load(db *db.DB) error {
   102  	fn := func(tx *sql.Tx) error {
   103  		var err error
   104  		d.aciinfos, err = getAllACIInfosV0_2(tx)
   105  		if err != nil {
   106  			return err
   107  		}
   108  		d.remotes, err = getAllRemoteV0_1(tx)
   109  		if err != nil {
   110  			return err
   111  		}
   112  		return nil
   113  	}
   114  	if err := db.Do(fn); err != nil {
   115  		return err
   116  	}
   117  	return nil
   118  }
   119  
   120  func (d *DBV1) compare(td testdb) bool {
   121  	d2, ok := td.(*DBV1)
   122  	if !ok {
   123  		return false
   124  	}
   125  	if !compareSlicesNoOrder(d.aciinfos, d2.aciinfos) {
   126  		return false
   127  	}
   128  	if !compareSlicesNoOrder(d.remotes, d2.remotes) {
   129  		return false
   130  	}
   131  	return true
   132  }
   133  
   134  type DBV2 struct {
   135  	aciinfos []*ACIInfoV0_2
   136  	remotes  []*RemoteV2_7
   137  }
   138  
   139  func (d *DBV2) version() int {
   140  	return 2
   141  }
   142  func (d *DBV2) populate(db *db.DB) error {
   143  	return populateDBV2(db, d.version(), d.aciinfos, d.remotes)
   144  }
   145  
   146  func (d *DBV2) load(db *db.DB) error {
   147  	fn := func(tx *sql.Tx) error {
   148  		var err error
   149  		d.aciinfos, err = getAllACIInfosV0_2(tx)
   150  		if err != nil {
   151  			return err
   152  		}
   153  		d.remotes, err = getAllRemoteV2_7(tx)
   154  		if err != nil {
   155  			return err
   156  		}
   157  		return nil
   158  	}
   159  	if err := db.Do(fn); err != nil {
   160  		return err
   161  	}
   162  	return nil
   163  }
   164  
   165  func (d *DBV2) compare(td testdb) bool {
   166  	d2, ok := td.(*DBV2)
   167  	if !ok {
   168  		return false
   169  	}
   170  	if !compareSlicesNoOrder(d.aciinfos, d2.aciinfos) {
   171  		return false
   172  	}
   173  	if !compareSlicesNoOrder(d.remotes, d2.remotes) {
   174  		return false
   175  	}
   176  	return true
   177  }
   178  
   179  type DBV3 struct {
   180  	aciinfos []*ACIInfoV3
   181  	remotes  []*RemoteV2_7
   182  }
   183  
   184  func (d *DBV3) version() int {
   185  	return 3
   186  }
   187  func (d *DBV3) populate(db *db.DB) error {
   188  	return populateDBV3(db, d.version(), d.aciinfos, d.remotes)
   189  }
   190  
   191  func (d *DBV3) load(db *db.DB) error {
   192  	fn := func(tx *sql.Tx) error {
   193  		var err error
   194  		d.aciinfos, err = getAllACIInfosV3(tx)
   195  		if err != nil {
   196  			return err
   197  		}
   198  		d.remotes, err = getAllRemoteV2_7(tx)
   199  		if err != nil {
   200  			return err
   201  		}
   202  		return nil
   203  	}
   204  	if err := db.Do(fn); err != nil {
   205  		return err
   206  	}
   207  	return nil
   208  }
   209  
   210  func (d *DBV3) compare(td testdb) bool {
   211  	d3, ok := td.(*DBV3)
   212  	if !ok {
   213  		return false
   214  	}
   215  	if !compareSlicesNoOrder(d.aciinfos, d3.aciinfos) {
   216  		return false
   217  	}
   218  	if !compareSlicesNoOrder(d.remotes, d3.remotes) {
   219  		return false
   220  	}
   221  	return true
   222  }
   223  
   224  type DBV4 struct {
   225  	aciinfos []*ACIInfoV4
   226  	remotes  []*RemoteV2_7
   227  }
   228  
   229  func (d *DBV4) version() int {
   230  	return 4
   231  }
   232  func (d *DBV4) populate(db *db.DB) error {
   233  	return populateDBV4(db, d.version(), d.aciinfos, d.remotes)
   234  }
   235  
   236  func (d *DBV4) load(db *db.DB) error {
   237  	fn := func(tx *sql.Tx) error {
   238  		var err error
   239  		d.aciinfos, err = getAllACIInfosV4(tx)
   240  		if err != nil {
   241  			return err
   242  		}
   243  		d.remotes, err = getAllRemoteV2_7(tx)
   244  		if err != nil {
   245  			return err
   246  		}
   247  		return nil
   248  	}
   249  	if err := db.Do(fn); err != nil {
   250  		return err
   251  	}
   252  	return nil
   253  }
   254  
   255  func (d *DBV4) compare(td testdb) bool {
   256  	d4, ok := td.(*DBV4)
   257  	if !ok {
   258  		return false
   259  	}
   260  	if !compareSlicesNoOrder(d.aciinfos, d4.aciinfos) {
   261  		return false
   262  	}
   263  	if !compareSlicesNoOrder(d.remotes, d4.remotes) {
   264  		return false
   265  	}
   266  	return true
   267  }
   268  
   269  type DBV5 struct {
   270  	aciinfos []*ACIInfoV5
   271  	remotes  []*RemoteV2_7
   272  }
   273  
   274  func (d *DBV5) version() int {
   275  	return 5
   276  }
   277  func (d *DBV5) populate(db *db.DB) error {
   278  	return populateDBV5(db, d.version(), d.aciinfos, d.remotes)
   279  }
   280  
   281  func (d *DBV5) load(db *db.DB) error {
   282  	fn := func(tx *sql.Tx) error {
   283  		var err error
   284  		d.aciinfos, err = getAllACIInfosV5(tx)
   285  		if err != nil {
   286  			return err
   287  		}
   288  		d.remotes, err = getAllRemoteV2_7(tx)
   289  		if err != nil {
   290  			return err
   291  		}
   292  		return nil
   293  	}
   294  	if err := db.Do(fn); err != nil {
   295  		return err
   296  	}
   297  	return nil
   298  }
   299  
   300  func (d *DBV5) compare(td testdb) bool {
   301  	d5, ok := td.(*DBV5)
   302  	if !ok {
   303  		return false
   304  	}
   305  	if !compareSlicesNoOrder(d.aciinfos, d5.aciinfos) {
   306  		return false
   307  	}
   308  	if !compareSlicesNoOrder(d.remotes, d5.remotes) {
   309  		return false
   310  	}
   311  	return true
   312  }
   313  
   314  type DBV6 struct {
   315  	aciinfos []*ACIInfoV6
   316  	remotes  []*RemoteV2_7
   317  }
   318  
   319  func (d *DBV6) version() int {
   320  	return 6
   321  }
   322  func (d *DBV6) populate(db *db.DB) error {
   323  	return populateDBV6(db, d.version(), d.aciinfos, d.remotes)
   324  }
   325  
   326  func (d *DBV6) load(db *db.DB) error {
   327  	fn := func(tx *sql.Tx) error {
   328  		var err error
   329  		d.aciinfos, err = getAllACIInfosV6(tx)
   330  		if err != nil {
   331  			return err
   332  		}
   333  		d.remotes, err = getAllRemoteV2_7(tx)
   334  		if err != nil {
   335  			return err
   336  		}
   337  		return nil
   338  	}
   339  	if err := db.Do(fn); err != nil {
   340  		return err
   341  	}
   342  	return nil
   343  }
   344  
   345  func (d *DBV6) compare(td testdb) bool {
   346  	d6, ok := td.(*DBV6)
   347  	if !ok {
   348  		return false
   349  	}
   350  	if !compareSlicesNoOrder(d.aciinfos, d6.aciinfos) {
   351  		return false
   352  	}
   353  	if !compareSlicesNoOrder(d.remotes, d6.remotes) {
   354  		return false
   355  	}
   356  	return true
   357  }
   358  
   359  type DBV7 struct {
   360  	aciinfos []*ACIInfoV7
   361  	remotes  []*RemoteV2_7
   362  }
   363  
   364  func (d *DBV7) version() int {
   365  	return 7
   366  }
   367  func (d *DBV7) populate(db *db.DB) error {
   368  	return populateDBV7(db, d.version(), d.aciinfos, d.remotes)
   369  }
   370  
   371  func (d *DBV7) load(db *db.DB) error {
   372  	fn := func(tx *sql.Tx) error {
   373  		var err error
   374  		d.aciinfos, err = getAllACIInfosV7(tx)
   375  		if err != nil {
   376  			return err
   377  		}
   378  		d.remotes, err = getAllRemoteV2_7(tx)
   379  		if err != nil {
   380  			return err
   381  		}
   382  		return nil
   383  	}
   384  	if err := db.Do(fn); err != nil {
   385  		return err
   386  	}
   387  	return nil
   388  }
   389  
   390  func (d *DBV7) compare(td testdb) bool {
   391  	d7, ok := td.(*DBV7)
   392  	if !ok {
   393  		return false
   394  	}
   395  	if !compareSlicesNoOrder(d.aciinfos, d7.aciinfos) {
   396  		return false
   397  	}
   398  	if !compareSlicesNoOrder(d.remotes, d7.remotes) {
   399  		return false
   400  	}
   401  	return true
   402  }
   403  
   404  // The ACIInfo struct for different db versions. The ending VX_Y represent the
   405  // first and the last version where the format isn't changed
   406  // The latest existing struct should be updated when updating the db version
   407  // without changing the struct format (ex. V0_1 to V0_2).
   408  // A new struct and its relative function should be added if the format is changed.
   409  // The same applies for all of the other structs.
   410  type ACIInfoV0_2 struct {
   411  	BlobKey    string
   412  	AppName    string
   413  	ImportTime time.Time
   414  	Latest     bool
   415  }
   416  
   417  type ACIInfoV3 struct {
   418  	BlobKey    string
   419  	Name       string
   420  	ImportTime time.Time
   421  	Latest     bool
   422  }
   423  
   424  type ACIInfoV4 struct {
   425  	BlobKey    string
   426  	Name       string
   427  	ImportTime time.Time
   428  	LastUsed   time.Time
   429  	Latest     bool
   430  }
   431  
   432  type ACIInfoV5 struct {
   433  	BlobKey       string
   434  	Name          string
   435  	ImportTime    time.Time
   436  	LastUsed      time.Time
   437  	Latest        bool
   438  	Size          int64
   439  	TreeStoreSize int64
   440  }
   441  
   442  type ACIInfoV6 struct {
   443  	BlobKey         string
   444  	Name            string
   445  	ImportTime      time.Time
   446  	LastUsed        time.Time
   447  	Latest          bool
   448  	Size            int64
   449  	TreeStoreSize   int64
   450  	InsecureOptions int64
   451  }
   452  
   453  type ACIInfoV7 struct {
   454  	BlobKey       string
   455  	Name          string
   456  	ImportTime    time.Time
   457  	LastUsed      time.Time
   458  	Latest        bool
   459  	Size          int64
   460  	TreeStoreSize int64
   461  }
   462  
   463  func getAllACIInfosV0_2(tx *sql.Tx) ([]*ACIInfoV0_2, error) {
   464  	var aciinfos []*ACIInfoV0_2
   465  	rows, err := tx.Query("SELECT * FROM aciinfo")
   466  	if err != nil {
   467  		return nil, err
   468  	}
   469  	defer rows.Close()
   470  	for rows.Next() {
   471  		aciinfo := &ACIInfoV0_2{}
   472  		if err := rows.Scan(&aciinfo.BlobKey, &aciinfo.AppName, &aciinfo.ImportTime, &aciinfo.Latest); err != nil {
   473  			return nil, err
   474  		}
   475  		aciinfos = append(aciinfos, aciinfo)
   476  	}
   477  	if err := rows.Err(); err != nil {
   478  		return nil, err
   479  	}
   480  	return aciinfos, nil
   481  }
   482  
   483  func getAllACIInfosV3(tx *sql.Tx) ([]*ACIInfoV3, error) {
   484  	var aciinfos []*ACIInfoV3
   485  	rows, err := tx.Query("SELECT * FROM aciinfo")
   486  	if err != nil {
   487  		return nil, err
   488  	}
   489  	defer rows.Close()
   490  	for rows.Next() {
   491  		aciinfo := &ACIInfoV3{}
   492  		if rows.Scan(&aciinfo.BlobKey, &aciinfo.Name, &aciinfo.ImportTime, &aciinfo.Latest); err != nil {
   493  			return nil, err
   494  		}
   495  		aciinfos = append(aciinfos, aciinfo)
   496  	}
   497  	if err := rows.Err(); err != nil {
   498  		return nil, err
   499  	}
   500  	return aciinfos, nil
   501  }
   502  
   503  func getAllACIInfosV4(tx *sql.Tx) ([]*ACIInfoV4, error) {
   504  	var aciinfos []*ACIInfoV4
   505  	rows, err := tx.Query("SELECT * FROM aciinfo")
   506  	if err != nil {
   507  		return nil, err
   508  	}
   509  	defer rows.Close()
   510  	for rows.Next() {
   511  		aciinfo := &ACIInfoV4{}
   512  		if rows.Scan(&aciinfo.BlobKey, &aciinfo.Name, &aciinfo.ImportTime, &aciinfo.LastUsed, &aciinfo.Latest); err != nil {
   513  			return nil, err
   514  		}
   515  		aciinfos = append(aciinfos, aciinfo)
   516  	}
   517  	if err := rows.Err(); err != nil {
   518  		return nil, err
   519  	}
   520  	return aciinfos, nil
   521  }
   522  
   523  func getAllACIInfosV5(tx *sql.Tx) ([]*ACIInfoV5, error) {
   524  	var aciinfos []*ACIInfoV5
   525  	rows, err := tx.Query("SELECT * FROM aciinfo")
   526  	if err != nil {
   527  		return nil, err
   528  	}
   529  	defer rows.Close()
   530  	for rows.Next() {
   531  		aciinfo := &ACIInfoV5{}
   532  		if rows.Scan(&aciinfo.BlobKey, &aciinfo.Name, &aciinfo.ImportTime, &aciinfo.LastUsed, &aciinfo.Latest, &aciinfo.Size, &aciinfo.TreeStoreSize); err != nil {
   533  			return nil, err
   534  		}
   535  		aciinfos = append(aciinfos, aciinfo)
   536  	}
   537  	if err := rows.Err(); err != nil {
   538  		return nil, err
   539  	}
   540  	return aciinfos, nil
   541  }
   542  
   543  func getAllACIInfosV6(tx *sql.Tx) ([]*ACIInfoV6, error) {
   544  	var aciinfos []*ACIInfoV6
   545  	rows, err := tx.Query("SELECT * FROM aciinfo")
   546  	if err != nil {
   547  		return nil, err
   548  	}
   549  	defer rows.Close()
   550  	for rows.Next() {
   551  		aciinfo := &ACIInfoV6{}
   552  		if rows.Scan(&aciinfo.BlobKey, &aciinfo.Name, &aciinfo.ImportTime, &aciinfo.LastUsed, &aciinfo.Latest, &aciinfo.Size, &aciinfo.TreeStoreSize, &aciinfo.InsecureOptions); err != nil {
   553  			return nil, err
   554  		}
   555  		aciinfos = append(aciinfos, aciinfo)
   556  	}
   557  	if err := rows.Err(); err != nil {
   558  		return nil, err
   559  	}
   560  	return aciinfos, nil
   561  }
   562  
   563  func getAllACIInfosV7(tx *sql.Tx) ([]*ACIInfoV7, error) {
   564  	var aciinfos []*ACIInfoV7
   565  	rows, err := tx.Query("SELECT * FROM aciinfo")
   566  	if err != nil {
   567  		return nil, err
   568  	}
   569  	defer rows.Close()
   570  	for rows.Next() {
   571  		aciinfo := &ACIInfoV7{}
   572  		if rows.Scan(&aciinfo.BlobKey, &aciinfo.Name, &aciinfo.ImportTime, &aciinfo.LastUsed, &aciinfo.Latest, &aciinfo.Size, &aciinfo.TreeStoreSize); err != nil {
   573  			return nil, err
   574  		}
   575  		aciinfos = append(aciinfos, aciinfo)
   576  	}
   577  	if err := rows.Err(); err != nil {
   578  		return nil, err
   579  	}
   580  	return aciinfos, nil
   581  }
   582  
   583  type RemoteV0_1 struct {
   584  	ACIURL  string
   585  	SigURL  string
   586  	ETag    string
   587  	BlobKey string
   588  }
   589  
   590  func getAllRemoteV0_1(tx *sql.Tx) ([]*RemoteV0_1, error) {
   591  	var remotes []*RemoteV0_1
   592  	rows, err := tx.Query("SELECT * FROM remote")
   593  	if err != nil {
   594  		return nil, err
   595  	}
   596  	defer rows.Close()
   597  	for rows.Next() {
   598  		remote := &RemoteV0_1{}
   599  		if err := rows.Scan(&remote.ACIURL, &remote.SigURL, &remote.ETag, &remote.BlobKey); err != nil {
   600  			return nil, err
   601  		}
   602  		remotes = append(remotes, remote)
   603  	}
   604  	if err := rows.Err(); err != nil {
   605  		return nil, err
   606  	}
   607  	return remotes, nil
   608  }
   609  
   610  type RemoteV2_7 struct {
   611  	ACIURL       string
   612  	SigURL       string
   613  	ETag         string
   614  	BlobKey      string
   615  	CacheMaxAge  int
   616  	DownloadTime time.Time
   617  }
   618  
   619  func getAllRemoteV2_7(tx *sql.Tx) ([]*RemoteV2_7, error) {
   620  	var remotes []*RemoteV2_7
   621  	rows, err := tx.Query("SELECT * FROM remote")
   622  	if err != nil {
   623  		return nil, err
   624  	}
   625  	defer rows.Close()
   626  	for rows.Next() {
   627  		remote := &RemoteV2_7{}
   628  		if err := rows.Scan(&remote.ACIURL, &remote.SigURL, &remote.ETag, &remote.BlobKey, &remote.CacheMaxAge, &remote.DownloadTime); err != nil {
   629  			return nil, err
   630  		}
   631  		remotes = append(remotes, remote)
   632  	}
   633  	if err := rows.Err(); err != nil {
   634  		return nil, err
   635  	}
   636  	return remotes, nil
   637  }
   638  
   639  func populateDBV0_1(db *db.DB, dbVersion int, aciInfos []*ACIInfoV0_2, remotes []*RemoteV0_1) error {
   640  	var dbCreateStmts = [...]string{
   641  		// version table
   642  		"CREATE TABLE IF NOT EXISTS version (version int);",
   643  		fmt.Sprintf("INSERT INTO version VALUES (%d)", dbVersion),
   644  
   645  		// remote table. The primary key is "aciurl".
   646  		"CREATE TABLE IF NOT EXISTS remote (aciurl string, sigurl string, etag string, blobkey string);",
   647  		"CREATE UNIQUE INDEX IF NOT EXISTS aciurlidx ON remote (aciurl)",
   648  
   649  		// aciinfo table. The primary key is "blobkey" and it matches the key used to save that aci in the blob store
   650  		"CREATE TABLE IF NOT EXISTS aciinfo (blobkey string, appname string, importtime time, latest bool);",
   651  		"CREATE UNIQUE INDEX IF NOT EXISTS blobkeyidx ON aciinfo (blobkey)",
   652  		"CREATE INDEX IF NOT EXISTS appnameidx ON aciinfo (appname)",
   653  	}
   654  
   655  	fn := func(tx *sql.Tx) error {
   656  		for _, stmt := range dbCreateStmts {
   657  			_, err := tx.Exec(stmt)
   658  			if err != nil {
   659  				return err
   660  			}
   661  		}
   662  		return nil
   663  	}
   664  	if err := db.Do(fn); err != nil {
   665  		return err
   666  	}
   667  
   668  	fn = func(tx *sql.Tx) error {
   669  		for _, aciinfo := range aciInfos {
   670  			_, err := tx.Exec("INSERT into aciinfo values ($1, $2, $3, $4)", aciinfo.BlobKey, aciinfo.AppName, aciinfo.ImportTime, aciinfo.Latest)
   671  			if err != nil {
   672  				return err
   673  			}
   674  		}
   675  		return nil
   676  	}
   677  	if err := db.Do(fn); err != nil {
   678  		return err
   679  	}
   680  
   681  	fn = func(tx *sql.Tx) error {
   682  		for _, remote := range remotes {
   683  			_, err := tx.Exec("INSERT into remote values ($1, $2, $3, $4)", remote.ACIURL, remote.SigURL, remote.ETag, remote.BlobKey)
   684  			if err != nil {
   685  				return err
   686  			}
   687  		}
   688  		return nil
   689  	}
   690  	if err := db.Do(fn); err != nil {
   691  		return err
   692  	}
   693  
   694  	return nil
   695  }
   696  
   697  func populateDBV2(db *db.DB, dbVersion int, aciInfos []*ACIInfoV0_2, remotes []*RemoteV2_7) error {
   698  	var dbCreateStmts = [...]string{
   699  		// version table
   700  		"CREATE TABLE IF NOT EXISTS version (version int);",
   701  		fmt.Sprintf("INSERT INTO version VALUES (%d)", dbVersion),
   702  
   703  		// remote table. The primary key is "aciurl".
   704  		"CREATE TABLE IF NOT EXISTS remote (aciurl string, sigurl string, etag string, blobkey string, cachemaxage int, downloadtime time);",
   705  		"CREATE UNIQUE INDEX IF NOT EXISTS aciurlidx ON remote (aciurl)",
   706  
   707  		// aciinfo table. The primary key is "blobkey" and it matches the key used to save that aci in the blob store
   708  		"CREATE TABLE IF NOT EXISTS aciinfo (blobkey string, appname string, importtime time, latest bool);",
   709  		"CREATE UNIQUE INDEX IF NOT EXISTS blobkeyidx ON aciinfo (blobkey)",
   710  		"CREATE INDEX IF NOT EXISTS appnameidx ON aciinfo (appname)",
   711  	}
   712  
   713  	fn := func(tx *sql.Tx) error {
   714  		for _, stmt := range dbCreateStmts {
   715  			_, err := tx.Exec(stmt)
   716  			if err != nil {
   717  				return err
   718  			}
   719  		}
   720  		return nil
   721  	}
   722  	if err := db.Do(fn); err != nil {
   723  		return err
   724  	}
   725  
   726  	fn = func(tx *sql.Tx) error {
   727  		for _, aciinfo := range aciInfos {
   728  			_, err := tx.Exec("INSERT into aciinfo values ($1, $2, $3, $4)", aciinfo.BlobKey, aciinfo.AppName, aciinfo.ImportTime, aciinfo.Latest)
   729  			if err != nil {
   730  				return err
   731  			}
   732  		}
   733  		return nil
   734  	}
   735  	if err := db.Do(fn); err != nil {
   736  		return err
   737  	}
   738  
   739  	fn = func(tx *sql.Tx) error {
   740  		for _, remote := range remotes {
   741  			_, err := tx.Exec("INSERT into remote values ($1, $2, $3, $4, $5, $6)", remote.ACIURL, remote.SigURL, remote.ETag, remote.BlobKey, remote.CacheMaxAge, remote.DownloadTime)
   742  			if err != nil {
   743  				return err
   744  			}
   745  		}
   746  		return nil
   747  	}
   748  	if err := db.Do(fn); err != nil {
   749  		return err
   750  	}
   751  
   752  	return nil
   753  }
   754  
   755  func populateDBV3(db *db.DB, dbVersion int, aciInfos []*ACIInfoV3, remotes []*RemoteV2_7) error {
   756  	var dbCreateStmts = [...]string{
   757  		// version table
   758  		"CREATE TABLE IF NOT EXISTS version (version int);",
   759  		fmt.Sprintf("INSERT INTO version VALUES (%d)", dbVersion),
   760  
   761  		// remote table. The primary key is "aciurl".
   762  		"CREATE TABLE IF NOT EXISTS remote (aciurl string, sigurl string, etag string, blobkey string, cachemaxage int, downloadtime time);",
   763  		"CREATE UNIQUE INDEX IF NOT EXISTS aciurlidx ON remote (aciurl)",
   764  
   765  		// aciinfo table. The primary key is "blobkey" and it matches the key used to save that aci in the blob store
   766  		"CREATE TABLE IF NOT EXISTS aciinfo (blobkey string, importtime time, latest bool, name string);",
   767  		"CREATE UNIQUE INDEX IF NOT EXISTS blobkeyidx ON aciinfo (blobkey)",
   768  		"CREATE INDEX IF NOT EXISTS nameidx ON aciinfo (name)",
   769  	}
   770  	fn := func(tx *sql.Tx) error {
   771  		for _, stmt := range dbCreateStmts {
   772  			_, err := tx.Exec(stmt)
   773  			if err != nil {
   774  				return err
   775  			}
   776  		}
   777  		return nil
   778  	}
   779  	if err := db.Do(fn); err != nil {
   780  		return err
   781  	}
   782  
   783  	fn = func(tx *sql.Tx) error {
   784  		for _, aciinfo := range aciInfos {
   785  			_, err := tx.Exec("INSERT into aciinfo values ($1, $2, $3, $4)", aciinfo.BlobKey, aciinfo.ImportTime, aciinfo.Latest, aciinfo.Name)
   786  			if err != nil {
   787  				return err
   788  			}
   789  		}
   790  		return nil
   791  	}
   792  	if err := db.Do(fn); err != nil {
   793  		return err
   794  	}
   795  
   796  	fn = func(tx *sql.Tx) error {
   797  		for _, remote := range remotes {
   798  			_, err := tx.Exec("INSERT into remote values ($1, $2, $3, $4, $5, $6)", remote.ACIURL, remote.SigURL, remote.ETag, remote.BlobKey, remote.CacheMaxAge, remote.DownloadTime)
   799  			if err != nil {
   800  				return err
   801  			}
   802  		}
   803  		return nil
   804  	}
   805  	if err := db.Do(fn); err != nil {
   806  		return err
   807  	}
   808  
   809  	return nil
   810  }
   811  
   812  func populateDBV4(db *db.DB, dbVersion int, aciInfos []*ACIInfoV4, remotes []*RemoteV2_7) error {
   813  	var dbCreateStmts = [...]string{
   814  		// version table
   815  		"CREATE TABLE IF NOT EXISTS version (version int);",
   816  		fmt.Sprintf("INSERT INTO version VALUES (%d)", dbVersion),
   817  
   818  		// remote table. The primary key is "aciurl".
   819  		"CREATE TABLE IF NOT EXISTS remote (aciurl string, sigurl string, etag string, blobkey string, cachemaxage int, downloadtime time);",
   820  		"CREATE UNIQUE INDEX IF NOT EXISTS aciurlidx ON remote (aciurl)",
   821  
   822  		// aciinfo table. The primary key is "blobkey" and it matches the key used to save that aci in the blob store
   823  		"CREATE TABLE IF NOT EXISTS aciinfo (blobkey string, name string, importtime time, lastusedtime time, latest bool);",
   824  		"CREATE UNIQUE INDEX IF NOT EXISTS blobkeyidx ON aciinfo (blobkey)",
   825  		"CREATE INDEX IF NOT EXISTS nameidx ON aciinfo (name)",
   826  	}
   827  	fn := func(tx *sql.Tx) error {
   828  		for _, stmt := range dbCreateStmts {
   829  			_, err := tx.Exec(stmt)
   830  			if err != nil {
   831  				return err
   832  			}
   833  		}
   834  		return nil
   835  	}
   836  	if err := db.Do(fn); err != nil {
   837  		return err
   838  	}
   839  
   840  	fn = func(tx *sql.Tx) error {
   841  		for _, aciinfo := range aciInfos {
   842  			_, err := tx.Exec("INSERT INTO aciinfo (blobkey, name, importtime, lastusedtime, latest) VALUES ($1, $2, $3, $4, $5)", aciinfo.BlobKey, aciinfo.Name, aciinfo.ImportTime, aciinfo.LastUsed, aciinfo.Latest)
   843  			if err != nil {
   844  				return err
   845  			}
   846  		}
   847  		return nil
   848  	}
   849  	if err := db.Do(fn); err != nil {
   850  		return err
   851  	}
   852  
   853  	fn = func(tx *sql.Tx) error {
   854  		for _, remote := range remotes {
   855  			_, err := tx.Exec("INSERT into remote values ($1, $2, $3, $4, $5, $6)", remote.ACIURL, remote.SigURL, remote.ETag, remote.BlobKey, remote.CacheMaxAge, remote.DownloadTime)
   856  			if err != nil {
   857  				return err
   858  			}
   859  		}
   860  		return nil
   861  	}
   862  	if err := db.Do(fn); err != nil {
   863  		return err
   864  	}
   865  
   866  	return nil
   867  }
   868  
   869  func populateDBV5(db *db.DB, dbVersion int, aciInfos []*ACIInfoV5, remotes []*RemoteV2_7) error {
   870  	var dbCreateStmts = [...]string{
   871  		// version table
   872  		"CREATE TABLE IF NOT EXISTS version (version int);",
   873  		fmt.Sprintf("INSERT INTO version VALUES (%d)", dbVersion),
   874  
   875  		// remote table. The primary key is "aciurl".
   876  		"CREATE TABLE IF NOT EXISTS remote (aciurl string, sigurl string, etag string, blobkey string, cachemaxage int, downloadtime time);",
   877  		"CREATE UNIQUE INDEX IF NOT EXISTS aciurlidx ON remote (aciurl)",
   878  
   879  		// aciinfo table. The primary key is "blobkey" and it matches the key used to save that aci in the blob store
   880  		"CREATE TABLE IF NOT EXISTS aciinfo (blobkey string, name string, importtime time, lastused time, latest bool, size int64, treestoresize int64);",
   881  		"CREATE UNIQUE INDEX IF NOT EXISTS blobkeyidx ON aciinfo (blobkey)",
   882  		"CREATE INDEX IF NOT EXISTS nameidx ON aciinfo (name)",
   883  	}
   884  	fn := func(tx *sql.Tx) error {
   885  		for _, stmt := range dbCreateStmts {
   886  			_, err := tx.Exec(stmt)
   887  			if err != nil {
   888  				return err
   889  			}
   890  		}
   891  		return nil
   892  	}
   893  	if err := db.Do(fn); err != nil {
   894  		return err
   895  	}
   896  
   897  	fn = func(tx *sql.Tx) error {
   898  		for _, aciinfo := range aciInfos {
   899  			_, err := tx.Exec("INSERT INTO aciinfo (blobkey, name, importtime, lastused, latest, size, treestoresize) VALUES ($1, $2, $3, $4, $5, $6, $7)", aciinfo.BlobKey, aciinfo.Name, aciinfo.ImportTime, aciinfo.LastUsed, aciinfo.Latest, aciinfo.Size, aciinfo.TreeStoreSize)
   900  			if err != nil {
   901  				return err
   902  			}
   903  		}
   904  		return nil
   905  	}
   906  	if err := db.Do(fn); err != nil {
   907  		return err
   908  	}
   909  
   910  	fn = func(tx *sql.Tx) error {
   911  		for _, remote := range remotes {
   912  			_, err := tx.Exec("INSERT into remote values ($1, $2, $3, $4, $5, $6)", remote.ACIURL, remote.SigURL, remote.ETag, remote.BlobKey, remote.CacheMaxAge, remote.DownloadTime)
   913  			if err != nil {
   914  				return err
   915  			}
   916  		}
   917  		return nil
   918  	}
   919  	if err := db.Do(fn); err != nil {
   920  		return err
   921  	}
   922  
   923  	return nil
   924  }
   925  
   926  func populateDBV6(db *db.DB, dbVersion int, aciInfos []*ACIInfoV6, remotes []*RemoteV2_7) error {
   927  	var dbCreateStmts = [...]string{
   928  		// version table
   929  		"CREATE TABLE IF NOT EXISTS version (version int);",
   930  		fmt.Sprintf("INSERT INTO version VALUES (%d)", dbVersion),
   931  
   932  		// remote table. The primary key is "aciurl".
   933  		"CREATE TABLE IF NOT EXISTS remote (aciurl string, sigurl string, etag string, blobkey string, cachemaxage int, downloadtime time);",
   934  		"CREATE UNIQUE INDEX IF NOT EXISTS aciurlidx ON remote (aciurl)",
   935  
   936  		// aciinfo table. The primary key is "blobkey" and it matches the key used to save that aci in the blob store
   937  		"CREATE TABLE IF NOT EXISTS aciinfo (blobkey string, name string, importtime time, lastused time, latest bool, size int64, treestoresize int64, insecureoptions int64 DEFAULT 0);",
   938  		"CREATE UNIQUE INDEX IF NOT EXISTS blobkeyidx ON aciinfo (blobkey)",
   939  		"CREATE INDEX IF NOT EXISTS nameidx ON aciinfo (name)",
   940  	}
   941  	fn := func(tx *sql.Tx) error {
   942  		for _, stmt := range dbCreateStmts {
   943  			_, err := tx.Exec(stmt)
   944  			if err != nil {
   945  				return err
   946  			}
   947  		}
   948  		return nil
   949  	}
   950  	if err := db.Do(fn); err != nil {
   951  		return err
   952  	}
   953  
   954  	fn = func(tx *sql.Tx) error {
   955  		for _, aciinfo := range aciInfos {
   956  			_, err := tx.Exec("INSERT INTO aciinfo (blobkey, name, importtime, lastused, latest, size, treestoresize, insecureoptions) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", aciinfo.BlobKey, aciinfo.Name, aciinfo.ImportTime, aciinfo.LastUsed, aciinfo.Latest, aciinfo.Size, aciinfo.TreeStoreSize, aciinfo.InsecureOptions)
   957  			if err != nil {
   958  				return err
   959  			}
   960  		}
   961  		return nil
   962  	}
   963  	if err := db.Do(fn); err != nil {
   964  		return err
   965  	}
   966  
   967  	fn = func(tx *sql.Tx) error {
   968  		for _, remote := range remotes {
   969  			_, err := tx.Exec("INSERT into remote values ($1, $2, $3, $4, $5, $6)", remote.ACIURL, remote.SigURL, remote.ETag, remote.BlobKey, remote.CacheMaxAge, remote.DownloadTime)
   970  			if err != nil {
   971  				return err
   972  			}
   973  		}
   974  		return nil
   975  	}
   976  	if err := db.Do(fn); err != nil {
   977  		return err
   978  	}
   979  
   980  	return nil
   981  }
   982  
   983  func populateDBV7(db *db.DB, dbVersion int, aciInfos []*ACIInfoV7, remotes []*RemoteV2_7) error {
   984  	var dbCreateStmts = [...]string{
   985  		// version table
   986  		"CREATE TABLE IF NOT EXISTS version (version int);",
   987  		fmt.Sprintf("INSERT INTO version VALUES (%d)", dbVersion),
   988  
   989  		// remote table. The primary key is "aciurl".
   990  		"CREATE TABLE IF NOT EXISTS remote (aciurl string, sigurl string, etag string, blobkey string, cachemaxage int, downloadtime time);",
   991  		"CREATE UNIQUE INDEX IF NOT EXISTS aciurlidx ON remote (aciurl)",
   992  
   993  		// aciinfo table. The primary key is "blobkey" and it matches the key used to save that aci in the blob store
   994  		"CREATE TABLE IF NOT EXISTS aciinfo (blobkey string, name string, importtime time, lastused time, latest bool, size int64, treestoresize int64);",
   995  		"CREATE UNIQUE INDEX IF NOT EXISTS blobkeyidx ON aciinfo (blobkey)",
   996  		"CREATE INDEX IF NOT EXISTS nameidx ON aciinfo (name)",
   997  	}
   998  	fn := func(tx *sql.Tx) error {
   999  		for _, stmt := range dbCreateStmts {
  1000  			_, err := tx.Exec(stmt)
  1001  			if err != nil {
  1002  				return err
  1003  			}
  1004  		}
  1005  		return nil
  1006  	}
  1007  	if err := db.Do(fn); err != nil {
  1008  		return err
  1009  	}
  1010  
  1011  	fn = func(tx *sql.Tx) error {
  1012  		for _, aciinfo := range aciInfos {
  1013  			_, err := tx.Exec("INSERT INTO aciinfo (blobkey, name, importtime, lastused, latest, size, treestoresize) VALUES ($1, $2, $3, $4, $5, $6, $7)", aciinfo.BlobKey, aciinfo.Name, aciinfo.ImportTime, aciinfo.LastUsed, aciinfo.Latest, aciinfo.Size, aciinfo.TreeStoreSize)
  1014  			if err != nil {
  1015  				return err
  1016  			}
  1017  		}
  1018  		return nil
  1019  	}
  1020  	if err := db.Do(fn); err != nil {
  1021  		return err
  1022  	}
  1023  
  1024  	fn = func(tx *sql.Tx) error {
  1025  		for _, remote := range remotes {
  1026  			_, err := tx.Exec("INSERT into remote values ($1, $2, $3, $4, $5, $6)", remote.ACIURL, remote.SigURL, remote.ETag, remote.BlobKey, remote.CacheMaxAge, remote.DownloadTime)
  1027  			if err != nil {
  1028  				return err
  1029  			}
  1030  		}
  1031  		return nil
  1032  	}
  1033  	if err := db.Do(fn); err != nil {
  1034  		return err
  1035  	}
  1036  
  1037  	return nil
  1038  }
  1039  
  1040  type migrateTest struct {
  1041  	predb  testdb
  1042  	postdb testdb
  1043  	// Needed to have the right DB type to load from
  1044  	curdb testdb
  1045  }
  1046  
  1047  func testMigrate(tt migrateTest) error {
  1048  	dir, err := ioutil.TempDir("", tstprefix)
  1049  	if err != nil {
  1050  		return fmt.Errorf("error creating tempdir: %v", err)
  1051  	}
  1052  	defer os.RemoveAll(dir)
  1053  
  1054  	storeDir := filepath.Join(dir, "store")
  1055  	db, err := db.NewDB(filepath.Join(storeDir, "db"))
  1056  	if err != nil {
  1057  		return err
  1058  	}
  1059  	if err = tt.predb.populate(db); err != nil {
  1060  		return err
  1061  	}
  1062  
  1063  	fn := func(tx *sql.Tx) error {
  1064  		err := migrate(tx, tt.postdb.version())
  1065  		if err != nil {
  1066  			return err
  1067  		}
  1068  		return nil
  1069  	}
  1070  	if err = db.Do(fn); err != nil {
  1071  		return err
  1072  	}
  1073  
  1074  	var curDBVersion int
  1075  	fn = func(tx *sql.Tx) error {
  1076  		var err error
  1077  		curDBVersion, err = getDBVersion(tx)
  1078  		if err != nil {
  1079  			return err
  1080  		}
  1081  		return nil
  1082  	}
  1083  	if err = db.Do(fn); err != nil {
  1084  		return err
  1085  	}
  1086  	if curDBVersion != tt.postdb.version() {
  1087  		return fmt.Errorf("wrong db version: got %#v, want %#v", curDBVersion, tt.postdb.version())
  1088  	}
  1089  
  1090  	if err := tt.curdb.load(db); err != nil {
  1091  		return err
  1092  	}
  1093  
  1094  	if !tt.curdb.compare(tt.postdb) {
  1095  		return spew.Errorf("while comparing DBs:\n\tgot %#v\n\twant %#v\n", tt.curdb, tt.postdb)
  1096  	}
  1097  
  1098  	return nil
  1099  }
  1100  
  1101  func TestMigrate(t *testing.T) {
  1102  	dir, err := ioutil.TempDir("", tstprefix)
  1103  	if err != nil {
  1104  		t.Fatalf("error creating tempdir: %v", err)
  1105  	}
  1106  	defer os.RemoveAll(dir)
  1107  
  1108  	blobKeys := []string{
  1109  		"sha512-aaaaaaaa",
  1110  		"sha512-bbbbbbbb",
  1111  	}
  1112  
  1113  	names := []string{
  1114  		"example.com/app01",
  1115  		"example.com/app02",
  1116  	}
  1117  
  1118  	sizes := []int64{
  1119  		0,
  1120  		2 << 8,
  1121  	}
  1122  
  1123  	treeStoreSizes := []int64{
  1124  		0,
  1125  		2 << 8,
  1126  		2 << 16,
  1127  	}
  1128  
  1129  	insecureOptions := []int64{
  1130  		0,
  1131  		2,
  1132  	}
  1133  
  1134  	now := time.Now().UTC()
  1135  
  1136  	// Injects a fake clock to store pkg clock
  1137  	clock = clockwork.NewFakeClockAt(now)
  1138  
  1139  	tests := []migrateTest{
  1140  		{
  1141  			// Test migration from V0 to V1 with an empty database
  1142  			&DBV0{},
  1143  			&DBV1{},
  1144  			&DBV1{},
  1145  		},
  1146  
  1147  		{
  1148  			// Test migration from V0 to V1
  1149  			&DBV0{
  1150  				[]*ACIInfoV0_2{
  1151  					{blobKeys[0], names[0], now, false},
  1152  					{blobKeys[1], names[1], now, true},
  1153  				},
  1154  				[]*RemoteV0_1{
  1155  					{"http://example.com/app01.aci", "http://example.com/app01.aci.asc", "", blobKeys[0]},
  1156  					{"http://example.com/app02.aci", "http://example.com/app02.aci.asc", "", blobKeys[1]},
  1157  				},
  1158  			},
  1159  			&DBV1{
  1160  				[]*ACIInfoV0_2{
  1161  					{blobKeys[0], names[0], now, false},
  1162  					{blobKeys[1], names[1], now, true},
  1163  				},
  1164  				[]*RemoteV0_1{
  1165  					{"http://example.com/app01.aci", "http://example.com/app01.aci.asc", "", blobKeys[0]},
  1166  					{"http://example.com/app02.aci", "http://example.com/app02.aci.asc", "", blobKeys[1]},
  1167  				},
  1168  			},
  1169  			&DBV1{},
  1170  		},
  1171  
  1172  		{
  1173  			// Test migration from V1 to V2
  1174  			&DBV1{
  1175  				[]*ACIInfoV0_2{
  1176  					{blobKeys[0], "example.com/app01", now, false},
  1177  					{blobKeys[1], "example.com/app02", now, true},
  1178  				},
  1179  				[]*RemoteV0_1{
  1180  					{"http://example.com/app01.aci", "http://example.com/app01.aci.asc", "", blobKeys[0]},
  1181  					{"http://example.com/app02.aci", "http://example.com/app02.aci.asc", "", blobKeys[1]},
  1182  				},
  1183  			},
  1184  			&DBV2{
  1185  				[]*ACIInfoV0_2{
  1186  					{blobKeys[0], "example.com/app01", now, false},
  1187  					{blobKeys[1], "example.com/app02", now, true},
  1188  				},
  1189  				[]*RemoteV2_7{
  1190  					{"http://example.com/app01.aci", "http://example.com/app01.aci.asc", "", blobKeys[0], 0, time.Time{}.UTC()},
  1191  					{"http://example.com/app02.aci", "http://example.com/app02.aci.asc", "", blobKeys[1], 0, time.Time{}.UTC()},
  1192  				},
  1193  			},
  1194  			&DBV2{},
  1195  		},
  1196  
  1197  		{
  1198  			// Test migration from V2 to V3
  1199  			&DBV2{
  1200  				[]*ACIInfoV0_2{
  1201  					{blobKeys[0], "example.com/app01", now, false},
  1202  					{blobKeys[1], "example.com/app02", now, true},
  1203  				},
  1204  				[]*RemoteV2_7{
  1205  					{"http://example.com/app01.aci", "http://example.com/app01.aci.asc", "", blobKeys[0], 0, time.Time{}.UTC()},
  1206  					{"http://example.com/app02.aci", "http://example.com/app02.aci.asc", "", blobKeys[1], 0, time.Time{}.UTC()},
  1207  				},
  1208  			},
  1209  			&DBV3{
  1210  				[]*ACIInfoV3{
  1211  					{blobKeys[0], "example.com/app01", now, false},
  1212  					{blobKeys[1], "example.com/app02", now, true},
  1213  				},
  1214  				[]*RemoteV2_7{
  1215  					{"http://example.com/app01.aci", "http://example.com/app01.aci.asc", "", blobKeys[0], 0, time.Time{}.UTC()},
  1216  					{"http://example.com/app02.aci", "http://example.com/app02.aci.asc", "", blobKeys[1], 0, time.Time{}.UTC()},
  1217  				},
  1218  			},
  1219  			&DBV3{},
  1220  		},
  1221  
  1222  		{
  1223  			// Test migration from V3 to V4
  1224  			&DBV3{
  1225  				[]*ACIInfoV3{
  1226  					{BlobKey: blobKeys[0], Name: names[0], ImportTime: now, Latest: false},
  1227  					{BlobKey: blobKeys[1], Name: names[1], ImportTime: now, Latest: true},
  1228  				},
  1229  				[]*RemoteV2_7{
  1230  					{"http://example.com/app01.aci", "http://example.com/app01.aci.asc", "", blobKeys[0], 0, time.Time{}.UTC()},
  1231  					{"http://example.com/app02.aci", "http://example.com/app02.aci.asc", "", blobKeys[1], 0, time.Time{}.UTC()},
  1232  				},
  1233  			},
  1234  			&DBV4{
  1235  				[]*ACIInfoV4{
  1236  					{BlobKey: blobKeys[0], Name: names[0], ImportTime: now, LastUsed: now, Latest: false},
  1237  					{BlobKey: blobKeys[1], Name: names[1], ImportTime: now, LastUsed: now, Latest: true},
  1238  				},
  1239  				[]*RemoteV2_7{
  1240  					{"http://example.com/app01.aci", "http://example.com/app01.aci.asc", "", blobKeys[0], 0, time.Time{}.UTC()},
  1241  					{"http://example.com/app02.aci", "http://example.com/app02.aci.asc", "", blobKeys[1], 0, time.Time{}.UTC()},
  1242  				},
  1243  			},
  1244  			&DBV4{},
  1245  		},
  1246  
  1247  		{
  1248  			// Test migration from V4 to V5
  1249  			&DBV4{
  1250  				[]*ACIInfoV4{
  1251  					{BlobKey: blobKeys[0], Name: names[0], ImportTime: now, LastUsed: now, Latest: false},
  1252  					{BlobKey: blobKeys[1], Name: names[1], ImportTime: now, LastUsed: now, Latest: true},
  1253  				},
  1254  				[]*RemoteV2_7{
  1255  					{"http://example.com/app01.aci", "http://example.com/app01.aci.asc", "", blobKeys[0], 0, time.Time{}.UTC()},
  1256  					{"http://example.com/app02.aci", "http://example.com/app02.aci.asc", "", blobKeys[1], 0, time.Time{}.UTC()},
  1257  				},
  1258  			},
  1259  			&DBV5{
  1260  				[]*ACIInfoV5{
  1261  					{BlobKey: blobKeys[0], Name: names[0], ImportTime: now, LastUsed: now, Latest: false},
  1262  					{BlobKey: blobKeys[1], Name: names[1], ImportTime: now, LastUsed: now, Latest: true},
  1263  				},
  1264  				[]*RemoteV2_7{
  1265  					{"http://example.com/app01.aci", "http://example.com/app01.aci.asc", "", blobKeys[0], 0, time.Time{}.UTC()},
  1266  					{"http://example.com/app02.aci", "http://example.com/app02.aci.asc", "", blobKeys[1], 0, time.Time{}.UTC()},
  1267  				},
  1268  			},
  1269  			&DBV5{},
  1270  		},
  1271  
  1272  		{
  1273  			// Test migration from V5 to V6
  1274  			&DBV5{
  1275  				[]*ACIInfoV5{
  1276  					{BlobKey: blobKeys[0], Name: names[0], ImportTime: now, LastUsed: now, Latest: false, Size: sizes[0], TreeStoreSize: treeStoreSizes[0]},
  1277  					{BlobKey: blobKeys[1], Name: names[1], ImportTime: now, LastUsed: now, Latest: true, Size: sizes[1], TreeStoreSize: treeStoreSizes[1]},
  1278  				},
  1279  				[]*RemoteV2_7{
  1280  					{"http://example.com/app01.aci", "http://example.com/app01.aci.asc", "", blobKeys[0], 0, time.Time{}.UTC()},
  1281  					{"http://example.com/app02.aci", "http://example.com/app02.aci.asc", "", blobKeys[1], 0, time.Time{}.UTC()},
  1282  				},
  1283  			},
  1284  			&DBV6{
  1285  				[]*ACIInfoV6{
  1286  					{BlobKey: blobKeys[0], Name: names[0], ImportTime: now, LastUsed: now, Latest: false, Size: sizes[0], TreeStoreSize: treeStoreSizes[0]},
  1287  					{BlobKey: blobKeys[1], Name: names[1], ImportTime: now, LastUsed: now, Latest: true, Size: sizes[1], TreeStoreSize: treeStoreSizes[1]},
  1288  				},
  1289  				[]*RemoteV2_7{
  1290  					{"http://example.com/app01.aci", "http://example.com/app01.aci.asc", "", blobKeys[0], 0, time.Time{}.UTC()},
  1291  					{"http://example.com/app02.aci", "http://example.com/app02.aci.asc", "", blobKeys[1], 0, time.Time{}.UTC()},
  1292  				},
  1293  			},
  1294  			&DBV6{},
  1295  		},
  1296  		{
  1297  			// Test migration from V6 to V7
  1298  			&DBV6{
  1299  				[]*ACIInfoV6{
  1300  					{BlobKey: blobKeys[0], Name: names[0], ImportTime: now, LastUsed: now, Latest: false, Size: sizes[0], TreeStoreSize: treeStoreSizes[0], InsecureOptions: insecureOptions[0]},
  1301  					{BlobKey: blobKeys[1], Name: names[1], ImportTime: now, LastUsed: now, Latest: true, Size: sizes[1], TreeStoreSize: treeStoreSizes[1], InsecureOptions: insecureOptions[1]},
  1302  				},
  1303  				[]*RemoteV2_7{
  1304  					{"http://example.com/app01.aci", "http://example.com/app01.aci.asc", "", blobKeys[0], 0, time.Time{}.UTC()},
  1305  					{"http://example.com/app02.aci", "http://example.com/app02.aci.asc", "", blobKeys[1], 0, time.Time{}.UTC()},
  1306  				},
  1307  			},
  1308  			&DBV7{
  1309  				[]*ACIInfoV7{
  1310  					{BlobKey: blobKeys[0], Name: names[0], ImportTime: now, LastUsed: now, Latest: false, Size: sizes[0], TreeStoreSize: treeStoreSizes[0]},
  1311  					{BlobKey: blobKeys[1], Name: names[1], ImportTime: now, LastUsed: now, Latest: true, Size: sizes[1], TreeStoreSize: treeStoreSizes[1]},
  1312  				},
  1313  				[]*RemoteV2_7{
  1314  					{"http://example.com/app01.aci", "http://example.com/app01.aci.asc", "", blobKeys[0], 0, time.Time{}.UTC()},
  1315  					{"http://example.com/app02.aci", "http://example.com/app02.aci.asc", "", blobKeys[1], 0, time.Time{}.UTC()},
  1316  				},
  1317  			},
  1318  			&DBV7{},
  1319  		},
  1320  	}
  1321  
  1322  	for i, tt := range tests {
  1323  		if err := testMigrate(tt); err != nil {
  1324  			t.Errorf("#%d: unexpected error: %v", i, err)
  1325  		}
  1326  	}
  1327  }
  1328  
  1329  // compareSlices compare slices regardless of the slice elements order
  1330  func compareSlicesNoOrder(i1 interface{}, i2 interface{}) bool {
  1331  	s1 := interfaceToSlice(i1)
  1332  	s2 := interfaceToSlice(i2)
  1333  
  1334  	if len(s1) != len(s2) {
  1335  		return false
  1336  	}
  1337  
  1338  	seen := map[int]bool{}
  1339  	for _, v1 := range s1 {
  1340  		found := false
  1341  		for i2, v2 := range s2 {
  1342  			if _, ok := seen[i2]; ok {
  1343  				continue
  1344  			}
  1345  
  1346  			if reflect.DeepEqual(v1, v2) {
  1347  				found = true
  1348  				seen[i2] = true
  1349  				continue
  1350  			}
  1351  		}
  1352  
  1353  		if !found {
  1354  			return false
  1355  		}
  1356  
  1357  	}
  1358  	return true
  1359  }
  1360  
  1361  func interfaceToSlice(s interface{}) []interface{} {
  1362  	v := reflect.ValueOf(s)
  1363  	if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
  1364  		panic(fmt.Errorf("Expected slice or array, got %T", s))
  1365  	}
  1366  	l := v.Len()
  1367  	m := make([]interface{}, l)
  1368  	for i := 0; i < l; i++ {
  1369  		m[i] = v.Index(i).Interface()
  1370  	}
  1371  	return m
  1372  }