@@ -2747,6 +2747,11 @@ func TestOrchestratorNotReady(t *testing.T) {
27472747 t .Errorf ("Expected AddStorageClass to return an error." )
27482748 }
27492749
2750+ storageClass , err = orchestrator .UpdateStorageClass (ctx (), nil )
2751+ if storageClass != nil || ! errors .IsNotReadyError (err ) {
2752+ t .Errorf ("Expected UpdateStorageClass to return an error." )
2753+ }
2754+
27502755 storageClass , err = orchestrator .GetStorageClass (ctx (), "" )
27512756 if storageClass != nil || ! errors .IsNotReadyError (err ) {
27522757 t .Errorf ("Expected GetStorageClass to return an error." )
@@ -4175,6 +4180,243 @@ func TestGetProtocol(t *testing.T) {
41754180 }
41764181}
41774182
4183+ func TestAddStorageClass (t * testing.T ) {
4184+ orchestrator := getOrchestrator (t , false )
4185+ defer cleanup (t , orchestrator )
4186+
4187+ storageClassConfig := & storageclass.Config {
4188+ Name : "test-storage-class" ,
4189+ Attributes : map [string ]sa.Request {
4190+ sa .IOPS : sa .NewIntRequest (1000 ),
4191+ },
4192+ }
4193+
4194+ // First add should work
4195+ storageClassExt , err := orchestrator .AddStorageClass (ctx (), storageClassConfig )
4196+ if err != nil {
4197+ t .Errorf ("Failed to add storage class: %v" , err )
4198+ }
4199+
4200+ if _ , ok := orchestrator .storageClasses [storageClassExt .GetName ()]; ! ok {
4201+ t .Errorf ("Storage class %s not found in orchestrator" , storageClassExt .GetName ())
4202+ }
4203+
4204+ // Second add should fail
4205+ storageClassExt , err = orchestrator .AddStorageClass (ctx (), storageClassConfig )
4206+
4207+ assert .Nil (t , storageClassExt , "Value should be nil" )
4208+ assert .Error (t , err , "Add should have failed" )
4209+ }
4210+
4211+ func TestAddStorageClass_PersistentStoreError (t * testing.T ) {
4212+ mockCtrl := gomock .NewController (t )
4213+ defer mockCtrl .Finish ()
4214+
4215+ mockStoreClient := mockpersistentstore .NewMockStoreClient (mockCtrl )
4216+ orchestrator , err := NewTridentOrchestrator (mockStoreClient )
4217+ orchestrator .bootstrapped = true
4218+ orchestrator .bootstrapError = nil
4219+
4220+ mockStoreClient .EXPECT ().AddBackend (gomock .Any (), gomock .Any ()).Return (nil )
4221+ mockStoreClient .EXPECT ().AddStorageClass (gomock .Any (), gomock .Any ()).Return (errors .New ("failed" ))
4222+
4223+ // Add backend with two pools
4224+ mockPools := tu .GetFakePools ()
4225+ pools := map [string ]* fake.StoragePool {
4226+ "fast-small" : mockPools [tu .FastSmall ],
4227+ "slow-snapshots" : mockPools [tu .SlowSnapshots ],
4228+ }
4229+ cfg , err := fakedriver .NewFakeStorageDriverConfigJSON ("backendWithTwoPools" , config .File , pools , make ([]fake.Volume , 0 ))
4230+ if err != nil {
4231+ t .Fatalf ("Unable to generate cfg JSON: %v" , err )
4232+ }
4233+ _ , err = orchestrator .AddBackend (ctx (), cfg , "" )
4234+ if err != nil {
4235+ t .Fatalf ("Unable to add backend: %v" , err )
4236+ }
4237+
4238+ // Add initial storage class
4239+ initialStorageClassConfig := & storageclass.Config {
4240+ Name : "initial-storage-class" ,
4241+ Attributes : map [string ]sa.Request {
4242+ sa .IOPS : sa .NewIntRequest (2000 ),
4243+ },
4244+ }
4245+ _ , err = orchestrator .AddStorageClass (ctx (), initialStorageClassConfig )
4246+ assert .Error (t , err , "AddStorageClass should have failed" )
4247+ }
4248+
4249+ func TestUpdateStorageClass (t * testing.T ) {
4250+ mockCtrl := gomock .NewController (t )
4251+ defer mockCtrl .Finish ()
4252+
4253+ mockStoreClient := mockpersistentstore .NewMockStoreClient (mockCtrl )
4254+ orchestrator , err := NewTridentOrchestrator (mockStoreClient )
4255+ orchestrator .bootstrapError = nil
4256+
4257+ // Test 1: Update existing storage class
4258+ scConfig := & storageclass.Config {Name : "testSC" }
4259+ sc := storageclass .New (scConfig )
4260+ orchestrator .storageClasses [sc .GetName ()] = sc
4261+
4262+ mockStoreClient .EXPECT ().UpdateStorageClass (gomock .Any (), sc ).Return (nil ).Times (1 )
4263+ _ , err = orchestrator .UpdateStorageClass (ctx (), scConfig )
4264+ assert .NoError (t , err , "should not return an error when updating an existing storage class" )
4265+
4266+ // Test 2: Update non-existing storage class
4267+ scConfig = & storageclass.Config {Name : "nonExistentSC" }
4268+ _ , err = orchestrator .UpdateStorageClass (ctx (), scConfig )
4269+ assert .Error (t , err , "should return an error when updating a non-existing storage class" )
4270+
4271+ // Test 3: Error updating storage class in store
4272+ scConfig = & storageclass.Config {Name : "testSC" }
4273+ sc = storageclass .New (scConfig )
4274+ orchestrator .storageClasses [sc .GetName ()] = sc
4275+
4276+ mockStoreClient .EXPECT ().UpdateStorageClass (gomock .Any (), sc ).Return (fmt .Errorf ("store error" )).Times (1 )
4277+ _ , err = orchestrator .UpdateStorageClass (ctx (), scConfig )
4278+ assert .Error (t , err , "should return an error when store client fails to update storage class" )
4279+ }
4280+
4281+ func TestUpdateStorageClassWithBackends (t * testing.T ) {
4282+ // Ensure backend-to-storageclass mapping is updated
4283+
4284+ mockCtrl := gomock .NewController (t )
4285+ defer mockCtrl .Finish ()
4286+
4287+ mockStoreClient := mockpersistentstore .NewMockStoreClient (mockCtrl )
4288+ orchestrator , err := NewTridentOrchestrator (mockStoreClient )
4289+ orchestrator .bootstrapped = true
4290+ orchestrator .bootstrapError = nil
4291+
4292+ mockStoreClient .EXPECT ().AddBackend (gomock .Any (), gomock .Any ()).Return (nil )
4293+ mockStoreClient .EXPECT ().AddStorageClass (gomock .Any (), gomock .Any ()).Return (nil )
4294+ mockStoreClient .EXPECT ().UpdateStorageClass (gomock .Any (), gomock .Any ()).Return (nil )
4295+
4296+ // Add backend with two pools
4297+ mockPools := tu .GetFakePools ()
4298+ pools := map [string ]* fake.StoragePool {
4299+ "fast-small" : mockPools [tu .FastSmall ],
4300+ "slow-snapshots" : mockPools [tu .SlowSnapshots ],
4301+ }
4302+ cfg , err := fakedriver .NewFakeStorageDriverConfigJSON ("backendWithTwoPools" , config .File , pools , make ([]fake.Volume , 0 ))
4303+ if err != nil {
4304+ t .Fatalf ("Unable to generate cfg JSON: %v" , err )
4305+ }
4306+ _ , err = orchestrator .AddBackend (ctx (), cfg , "" )
4307+ if err != nil {
4308+ t .Fatalf ("Unable to add backend: %v" , err )
4309+ }
4310+
4311+ // Add initial storage class
4312+ initialStorageClassConfig := & storageclass.Config {
4313+ Name : "initial-storage-class" ,
4314+ Attributes : map [string ]sa.Request {
4315+ sa .IOPS : sa .NewIntRequest (2000 ),
4316+ },
4317+ }
4318+ _ , err = orchestrator .AddStorageClass (ctx (), initialStorageClassConfig )
4319+ if err != nil {
4320+ t .Fatalf ("Unable to add initial storage class: %v" , err )
4321+ }
4322+
4323+ // Validate initial storage class matches pool1
4324+ mockStoreClient .EXPECT ().GetStorageClass (gomock .Any (), gomock .Any ()).Return (
4325+ orchestrator .storageClasses ["initial-storage-class" ].ConstructPersistent (), nil )
4326+
4327+ validateStorageClass (t , orchestrator , initialStorageClassConfig .Name , []* tu.PoolMatch {
4328+ {Backend : "backendWithTwoPools" , Pool : tu .FastSmall },
4329+ })
4330+
4331+ // Update storage class to match pool2
4332+ updatedStorageClassConfig := & storageclass.Config {
4333+ Name : "initial-storage-class" ,
4334+ Attributes : map [string ]sa.Request {
4335+ sa .IOPS : sa .NewIntRequest (40 ),
4336+ },
4337+ }
4338+ _ , err = orchestrator .UpdateStorageClass (ctx (), updatedStorageClassConfig )
4339+ if err != nil {
4340+ t .Fatalf ("Unable to update storage class: %v" , err )
4341+ }
4342+
4343+ // Validate updated storage class matches pool2
4344+ mockStoreClient .EXPECT ().GetStorageClass (gomock .Any (), gomock .Any ()).Return (
4345+ orchestrator .storageClasses ["initial-storage-class" ].ConstructPersistent (), nil )
4346+
4347+ validateStorageClass (t , orchestrator , updatedStorageClassConfig .Name , []* tu.PoolMatch {
4348+ {Backend : "backendWithTwoPools" , Pool : tu .SlowSnapshots },
4349+ })
4350+ }
4351+
4352+ func TestUpdateStorageClassWithBackends_PersistentStoreError (t * testing.T ) {
4353+ // Ensure backend-to-storageclass mapping is not updated if persistent store update fails
4354+ mockCtrl := gomock .NewController (t )
4355+ defer mockCtrl .Finish ()
4356+
4357+ mockStoreClient := mockpersistentstore .NewMockStoreClient (mockCtrl )
4358+ orchestrator , err := NewTridentOrchestrator (mockStoreClient )
4359+ orchestrator .bootstrapped = true
4360+ orchestrator .bootstrapError = nil
4361+
4362+ mockStoreClient .EXPECT ().AddBackend (gomock .Any (), gomock .Any ()).Return (nil )
4363+ mockStoreClient .EXPECT ().AddStorageClass (gomock .Any (), gomock .Any ()).Return (nil )
4364+ mockStoreClient .EXPECT ().UpdateStorageClass (gomock .Any (), gomock .Any ()).Return (errors .New ("failed" ))
4365+
4366+ // Add backend with two pools
4367+ mockPools := tu .GetFakePools ()
4368+ pools := map [string ]* fake.StoragePool {
4369+ "fast-small" : mockPools [tu .FastSmall ],
4370+ "slow-snapshots" : mockPools [tu .SlowSnapshots ],
4371+ }
4372+ cfg , err := fakedriver .NewFakeStorageDriverConfigJSON ("backendWithTwoPools" , config .File , pools , make ([]fake.Volume , 0 ))
4373+ if err != nil {
4374+ t .Fatalf ("Unable to generate cfg JSON: %v" , err )
4375+ }
4376+ _ , err = orchestrator .AddBackend (ctx (), cfg , "" )
4377+ if err != nil {
4378+ t .Fatalf ("Unable to add backend: %v" , err )
4379+ }
4380+
4381+ // Add initial storage class
4382+ initialStorageClassConfig := & storageclass.Config {
4383+ Name : "initial-storage-class" ,
4384+ Attributes : map [string ]sa.Request {
4385+ sa .IOPS : sa .NewIntRequest (2000 ),
4386+ },
4387+ }
4388+ _ , err = orchestrator .AddStorageClass (ctx (), initialStorageClassConfig )
4389+ if err != nil {
4390+ t .Fatalf ("Unable to add initial storage class: %v" , err )
4391+ }
4392+
4393+ // Validate initial storage class matches pool1
4394+ mockStoreClient .EXPECT ().GetStorageClass (gomock .Any (), gomock .Any ()).Return (
4395+ orchestrator .storageClasses ["initial-storage-class" ].ConstructPersistent (), nil )
4396+
4397+ validateStorageClass (t , orchestrator , initialStorageClassConfig .Name , []* tu.PoolMatch {
4398+ {Backend : "backendWithTwoPools" , Pool : tu .FastSmall },
4399+ })
4400+
4401+ // Update storage class to match pool2
4402+ updatedStorageClassConfig := & storageclass.Config {
4403+ Name : "initial-storage-class" ,
4404+ Attributes : map [string ]sa.Request {
4405+ sa .IOPS : sa .NewIntRequest (40 ),
4406+ },
4407+ }
4408+ _ , err = orchestrator .UpdateStorageClass (ctx (), updatedStorageClassConfig )
4409+ assert .Error (t , err , "UpdateStorageClass should have failed" )
4410+
4411+ // Validate storage class still matches pool1
4412+ mockStoreClient .EXPECT ().GetStorageClass (gomock .Any (), gomock .Any ()).Return (
4413+ orchestrator .storageClasses ["initial-storage-class" ].ConstructPersistent (), nil )
4414+
4415+ validateStorageClass (t , orchestrator , updatedStorageClassConfig .Name , []* tu.PoolMatch {
4416+ {Backend : "backendWithTwoPools" , Pool : tu .FastSmall },
4417+ })
4418+ }
4419+
41784420func TestGetBackend (t * testing.T ) {
41794421 // Boilerplate mocking code
41804422 mockCtrl := gomock .NewController (t )
0 commit comments