@@ -606,6 +606,91 @@ func TestNodeStageISCSIVolume(t *testing.T) {
606606 }
607607}
608608
609+ func TestGetChapInfoFromController (t * testing.T ) {
610+ type args struct {
611+ ctx context.Context
612+ volume string
613+ node string
614+ }
615+
616+ type data struct {
617+ chapInfo * models.IscsiChapInfo
618+ }
619+
620+ type assertions struct {
621+ Error assert.ErrorAssertionFunc
622+ Empty assert.ValueAssertionFunc
623+ Equal assert.ComparisonAssertionFunc
624+ }
625+
626+ tt := map [string ]struct {
627+ args args
628+ data data
629+ assert assertions
630+ mocks func (mockAPI * mockControllerAPI.MockTridentController , args args , data data )
631+ }{
632+ "Successfully gets CHAP credentials" : {
633+ args : args {
634+ ctx : context .Background (),
635+ volume : "foo" ,
636+ node : "bar" ,
637+ },
638+ data : data {
639+ chapInfo : & models.IscsiChapInfo {
640+ UseCHAP : true ,
641+ IscsiUsername : "user" ,
642+ IscsiInitiatorSecret : "pass" ,
643+ IscsiTargetUsername : "user2" ,
644+ IscsiTargetSecret : "pass2" ,
645+ },
646+ },
647+ assert : assertions {
648+ Error : assert .NoError ,
649+ Empty : assert .NotEmpty ,
650+ Equal : assert .Equal ,
651+ },
652+ mocks : func (mockAPI * mockControllerAPI.MockTridentController , args args , data data ) {
653+ mockAPI .EXPECT ().GetChap (args .ctx , args .volume , args .node ).Return (data .chapInfo , nil )
654+ },
655+ },
656+ "Fails to get CHAP credentials" : {
657+ args : args {
658+ ctx : context .Background (),
659+ volume : "foo" ,
660+ node : "bar" ,
661+ },
662+ data : data {
663+ chapInfo : nil ,
664+ },
665+ assert : assertions {
666+ Error : assert .Error ,
667+ Empty : assert .Empty ,
668+ Equal : assert .Equal ,
669+ },
670+ mocks : func (mockAPI * mockControllerAPI.MockTridentController , args args , data data ) {
671+ mockAPI .EXPECT ().GetChap (args .ctx , args .volume , args .node ).Return (data .chapInfo , errors .New ("api error" ))
672+ },
673+ },
674+ }
675+
676+ for name , test := range tt {
677+ t .Run (name , func (t * testing.T ) {
678+ mockCtrl := gomock .NewController (t )
679+ mockAPI := mockControllerAPI .NewMockTridentController (mockCtrl )
680+ test .mocks (mockAPI , test .args , test .data )
681+
682+ plugin := & Plugin {
683+ restClient : mockAPI ,
684+ }
685+
686+ info , err := plugin .getChapInfoFromController (test .args .ctx , test .args .volume , test .args .node )
687+ test .assert .Error (t , err )
688+ test .assert .Empty (t , info )
689+ test .assert .Equal (t , info , test .data .chapInfo )
690+ })
691+ }
692+ }
693+
609694func TestUpdateChapInfoFromController_Success (t * testing.T ) {
610695 testCtx := context .Background ()
611696 volumeName := "foo"
@@ -665,6 +750,129 @@ func TestUpdateChapInfoFromController_Error(t *testing.T) {
665750 assert .EqualValues (t , models.IscsiChapInfo {}, testPublishInfo .IscsiAccessInfo .IscsiChapInfo )
666751}
667752
753+ func TestUpdateChapInfoForSessions (t * testing.T ) {
754+ // Populate sessions with only the state that matters. The rest of the fields are not relevant for this test.
755+ publishedSessions := & models.ISCSISessions {}
756+ currentSessions := & models.ISCSISessions {}
757+
758+ // CHAP portals
759+ chapPortals := []string {"0.0.0.0" , "4.4.4.4" , "5.5.5.5" }
760+
761+ // Unique CHAP portal
762+ uniqueChapPortal := "9.9.9.9"
763+ uniqueIQN := "iqn.9999-99.com.netapp:target"
764+
765+ // non-CHAP portals
766+ nonChapPortals := []string {"1.1.1.1" , "2.2.2.2" , "3.3.3.3" }
767+
768+ sessionID := "0"
769+
770+ // Shared CHAP credentials
771+ sharedCHAPInfo := models.IscsiChapInfo {
772+ UseCHAP : true ,
773+ IscsiUsername : "user" ,
774+ IscsiInitiatorSecret : "pass" ,
775+ IscsiTargetUsername : "user2" ,
776+ IscsiTargetSecret : "pass2" ,
777+ }
778+
779+ // Add CHAP sessions to both maps.
780+ for _ , portal := range chapPortals {
781+ err := publishedSessions .AddPortal (portal , models.PortalInfo {
782+ ISCSITargetIQN : "iqn.2020-01.com.netapp:target" ,
783+ Credentials : sharedCHAPInfo ,
784+ })
785+ assert .NoError (t , err )
786+
787+ err = currentSessions .AddPortal (portal , models.PortalInfo {
788+ ISCSITargetIQN : "iqn.2020-01.com.netapp:target" ,
789+ SessionNumber : sessionID ,
790+ })
791+ assert .NoError (t , err )
792+ }
793+
794+ // Add a CHAP session with a unique IQN.
795+ err := publishedSessions .AddPortal (uniqueChapPortal , models.PortalInfo {
796+ // Use a different IQN here to prove we cache returned values from the REST API.
797+ ISCSITargetIQN : uniqueIQN ,
798+ Credentials : sharedCHAPInfo ,
799+ })
800+ assert .NoError (t , err )
801+ err = currentSessions .AddPortal (uniqueChapPortal , models.PortalInfo {
802+ ISCSITargetIQN : uniqueIQN ,
803+ SessionNumber : sessionID ,
804+ })
805+ assert .NoError (t , err )
806+
807+ // Add non-CHAP session
808+ for _ , portal := range nonChapPortals {
809+ err := publishedSessions .AddPortal (portal , models.PortalInfo {
810+ ISCSITargetIQN : "iqn.2020-01.com.netapp:target" ,
811+ Credentials : models.IscsiChapInfo {},
812+ })
813+ assert .NoError (t , err )
814+
815+ err = currentSessions .AddPortal (portal , models.PortalInfo {
816+ ISCSITargetIQN : "iqn.2020-01.com.netapp:target" ,
817+ Credentials : models.IscsiChapInfo {},
818+ SessionNumber : sessionID ,
819+ })
820+ assert .NoError (t , err )
821+ }
822+
823+ // Populate a single of LUN and volume in each session.
824+ volume := "foo"
825+ node := "bar"
826+ for _ , sessionData := range publishedSessions .Info {
827+ sessionData .LUNs .Info [0 ] = volume
828+ }
829+
830+ // Create a mock controller client that will return the expected CHAP info.
831+ mockCtrl := gomock .NewController (t )
832+ mockISCSI := mock_iscsi .NewMockISCSI (mockCtrl )
833+ mockClient := mockControllerAPI .NewMockTridentController (mockCtrl )
834+
835+ plugin := & Plugin {
836+ nodeName : node ,
837+ iscsi : mockISCSI ,
838+ restClient : mockClient ,
839+ }
840+
841+ // Expect calls on the mock client for all sessions that use CHAP.
842+ freshCHAPInfo := & models.IscsiChapInfo {
843+ UseCHAP : true ,
844+ IscsiUsername : "user2" ,
845+ IscsiInitiatorSecret : "pass2" ,
846+ IscsiTargetUsername : "user" ,
847+ IscsiTargetSecret : "pass" ,
848+ }
849+
850+ // Mock calls to the iSCSI client
851+ count := len (currentSessions .Info )
852+ mockISCSI .EXPECT ().IsSessionStale (gomock .Any (), sessionID ).Return (true ).Times (count )
853+
854+ // Mock API calls
855+ count = len (chapPortals ) - (len (chapPortals ) - 1 ) + 1 // Expect one more call for the unique CHAP portal.
856+ mockClient .EXPECT ().GetChap (
857+ gomock .Any (), volume , node ,
858+ ).Return (freshCHAPInfo , nil ).Times (count )
859+
860+ err = plugin .updateCHAPInfoForSessions (ctx , publishedSessions , currentSessions )
861+ assert .NoError (t , err )
862+
863+ // Verify that the CHAP info was updated in all sessions that use CHAP.
864+ for _ , portal := range chapPortals {
865+ chapInfoInSession := publishedSessions .Info [portal ].PortalInfo .Credentials
866+ assert .EqualValues (t , * freshCHAPInfo , chapInfoInSession )
867+ }
868+
869+ // Verify that the non-CHAP portals were not updated.
870+ for _ , portal := range nonChapPortals {
871+ chapInfoInSession := publishedSessions .Info [portal ].PortalInfo .Credentials
872+ assert .EqualValues (t , models.IscsiChapInfo {}, chapInfoInSession )
873+ }
874+ }
875+
668876type PortalAction struct {
669877 Portal string
670878 Action models.ISCSIAction
0 commit comments