zotregistry.io/zot@v1.4.4-0.20231124084042-02a8ed785457/pkg/meta/version/version_test.go (about)

     1  package version_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"os"
     7  	"path"
     8  	"testing"
     9  
    10  	"github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
    11  	"github.com/aws/aws-sdk-go-v2/service/dynamodb"
    12  	"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
    13  	"github.com/aws/aws-sdk-go/aws"
    14  	guuid "github.com/gofrs/uuid"
    15  	. "github.com/smartystreets/goconvey/convey"
    16  	"go.etcd.io/bbolt"
    17  
    18  	"zotregistry.io/zot/pkg/log"
    19  	"zotregistry.io/zot/pkg/meta/boltdb"
    20  	mdynamodb "zotregistry.io/zot/pkg/meta/dynamodb"
    21  	"zotregistry.io/zot/pkg/meta/version"
    22  	tskip "zotregistry.io/zot/pkg/test/skip"
    23  )
    24  
    25  var ErrTestError = errors.New("test error")
    26  
    27  func TestVersioningBoltDB(t *testing.T) {
    28  	Convey("Tests", t, func() {
    29  		tmpDir := t.TempDir()
    30  		boltDBParams := boltdb.DBParameters{RootDir: tmpDir}
    31  		boltDriver, err := boltdb.GetBoltDriver(boltDBParams)
    32  		So(err, ShouldBeNil)
    33  
    34  		log := log.NewLogger("debug", "")
    35  
    36  		boltdbWrapper, err := boltdb.New(boltDriver, log)
    37  		defer os.Remove(path.Join(boltDBParams.RootDir, "meta.db"))
    38  		So(boltdbWrapper, ShouldNotBeNil)
    39  		So(err, ShouldBeNil)
    40  
    41  		boltdbWrapper.Patches = []func(DB *bbolt.DB) error{
    42  			func(DB *bbolt.DB) error {
    43  				return nil
    44  			},
    45  		}
    46  
    47  		Convey("success", func() {
    48  			boltdbWrapper.Patches = []func(DB *bbolt.DB) error{
    49  				func(DB *bbolt.DB) error { // V1 to V2
    50  					return nil
    51  				},
    52  			}
    53  
    54  			err := setBoltDBVersion(boltdbWrapper.DB, version.Version1)
    55  			So(err, ShouldBeNil)
    56  
    57  			err = boltdbWrapper.PatchDB()
    58  			So(err, ShouldBeNil)
    59  		})
    60  
    61  		Convey("DBVersion is empty", func() {
    62  			err := boltdbWrapper.DB.Update(func(tx *bbolt.Tx) error {
    63  				versionBuck := tx.Bucket([]byte(boltdb.VersionBucket))
    64  
    65  				return versionBuck.Put([]byte(version.DBVersionKey), []byte(""))
    66  			})
    67  			So(err, ShouldBeNil)
    68  
    69  			err = boltdbWrapper.PatchDB()
    70  			So(err, ShouldNotBeNil)
    71  		})
    72  
    73  		Convey("iterate patches with skip", func() {
    74  			boltdbWrapper.Patches = []func(DB *bbolt.DB) error{
    75  				func(DB *bbolt.DB) error { // V1 to V2
    76  					return nil
    77  				},
    78  				func(DB *bbolt.DB) error { // V2 to V3
    79  					return nil
    80  				},
    81  				func(DB *bbolt.DB) error { // V3 to V4
    82  					return nil
    83  				},
    84  			}
    85  
    86  			err := setBoltDBVersion(boltdbWrapper.DB, version.Version1)
    87  			So(err, ShouldBeNil)
    88  			// we should skip the first patch
    89  
    90  			err = boltdbWrapper.PatchDB()
    91  			So(err, ShouldBeNil)
    92  		})
    93  
    94  		Convey("patch has error", func() {
    95  			boltdbWrapper.Patches = []func(DB *bbolt.DB) error{
    96  				func(DB *bbolt.DB) error { // V1 to V2
    97  					return ErrTestError
    98  				},
    99  			}
   100  
   101  			err = boltdbWrapper.PatchDB()
   102  			So(err, ShouldNotBeNil)
   103  		})
   104  	})
   105  }
   106  
   107  func setBoltDBVersion(db *bbolt.DB, vers string) error {
   108  	err := db.Update(func(tx *bbolt.Tx) error {
   109  		versionBuck := tx.Bucket([]byte(boltdb.VersionBucket))
   110  
   111  		return versionBuck.Put([]byte(version.DBVersionKey), []byte(vers))
   112  	})
   113  
   114  	return err
   115  }
   116  
   117  func TestVersioningDynamoDB(t *testing.T) {
   118  	tskip.SkipDynamo(t)
   119  
   120  	uuid, err := guuid.NewV4()
   121  	if err != nil {
   122  		panic(err)
   123  	}
   124  
   125  	Convey("Tests", t, func() {
   126  		params := mdynamodb.DBDriverParameters{
   127  			Endpoint:               os.Getenv("DYNAMODBMOCK_ENDPOINT"),
   128  			Region:                 "us-east-2",
   129  			RepoMetaTablename:      "RepoMetadataTable" + uuid.String(),
   130  			RepoBlobsInfoTablename: "RepoBlobsInfoTablename" + uuid.String(),
   131  			ImageMetaTablename:     "ImageMetaTablename" + uuid.String(),
   132  			UserDataTablename:      "UserDataTable" + uuid.String(),
   133  			APIKeyTablename:        "ApiKeyTable" + uuid.String(),
   134  			VersionTablename:       "Version" + uuid.String(),
   135  		}
   136  
   137  		dynamoClient, err := mdynamodb.GetDynamoClient(params)
   138  		So(err, ShouldBeNil)
   139  
   140  		log := log.NewLogger("debug", "")
   141  
   142  		dynamoWrapper, err := mdynamodb.New(dynamoClient, params, log)
   143  		So(err, ShouldBeNil)
   144  
   145  		So(dynamoWrapper.ResetTable(dynamoWrapper.RepoMetaTablename), ShouldBeNil)
   146  
   147  		Convey("dbVersion is empty", func() {
   148  			err := setDynamoDBVersion(dynamoWrapper.Client, params.VersionTablename, "")
   149  			So(err, ShouldBeNil)
   150  
   151  			err = dynamoWrapper.PatchDB()
   152  			So(err, ShouldNotBeNil)
   153  		})
   154  
   155  		Convey("iterate patches with skip", func() {
   156  			dynamoWrapper.Patches = []func(client *dynamodb.Client, tableNames map[string]string) error{
   157  				func(client *dynamodb.Client, tableNames map[string]string) error { // V1 to V2
   158  					return nil
   159  				},
   160  				func(client *dynamodb.Client, tableNames map[string]string) error { // V2 to V3
   161  					return nil
   162  				},
   163  				func(client *dynamodb.Client, tableNames map[string]string) error { // V3 to V4
   164  					return nil
   165  				},
   166  			}
   167  
   168  			err := setDynamoDBVersion(dynamoWrapper.Client, params.VersionTablename, version.Version1)
   169  			So(err, ShouldBeNil)
   170  			// we should skip the first patch
   171  
   172  			err = dynamoWrapper.PatchDB()
   173  			So(err, ShouldBeNil)
   174  		})
   175  
   176  		Convey("patch has error", func() {
   177  			dynamoWrapper.Patches = []func(client *dynamodb.Client, tableNames map[string]string) error{
   178  				func(client *dynamodb.Client, tableNames map[string]string) error { // V1 to V2
   179  					return ErrTestError
   180  				},
   181  			}
   182  
   183  			err = dynamoWrapper.PatchDB()
   184  			So(err, ShouldNotBeNil)
   185  		})
   186  	})
   187  }
   188  
   189  func setDynamoDBVersion(client *dynamodb.Client, versTable, vers string) error {
   190  	mdAttributeValue, err := attributevalue.Marshal(vers)
   191  	if err != nil {
   192  		return err
   193  	}
   194  
   195  	_, err = client.UpdateItem(context.TODO(), &dynamodb.UpdateItemInput{
   196  		ExpressionAttributeNames: map[string]string{
   197  			"#V": "Version",
   198  		},
   199  		ExpressionAttributeValues: map[string]types.AttributeValue{
   200  			":Version": mdAttributeValue,
   201  		},
   202  		Key: map[string]types.AttributeValue{
   203  			"TableKey": &types.AttributeValueMemberS{
   204  				Value: version.DBVersionKey,
   205  			},
   206  		},
   207  		TableName:        aws.String(versTable),
   208  		UpdateExpression: aws.String("SET #V = :Version"),
   209  	})
   210  
   211  	return err
   212  }