@@ -749,17 +749,15 @@ func volumeClaimReap(srv RPCServer, volID, namespace, region, leaderACL string,
749
749
return err
750
750
}
751
751
752
- gcClaims , nodeClaims := collectClaimsToGCImpl (vol , runningAllocs )
752
+ nodeClaims := collectClaimsToGCImpl (vol , runningAllocs )
753
753
754
754
var result * multierror.Error
755
- for _ , claim := range gcClaims {
755
+ for _ , claim := range vol . PastClaims {
756
756
nodeClaims , err = volumeClaimReapImpl (srv ,
757
757
& volumeClaimReapArgs {
758
758
vol : vol ,
759
759
plug : plug ,
760
- allocID : claim .allocID ,
761
- nodeID : claim .nodeID ,
762
- mode : claim .mode ,
760
+ claim : claim ,
763
761
namespace : namespace ,
764
762
region : region ,
765
763
leaderACL : leaderACL ,
@@ -775,48 +773,47 @@ func volumeClaimReap(srv RPCServer, volID, namespace, region, leaderACL string,
775
773
776
774
}
777
775
778
- type gcClaimRequest struct {
779
- allocID string
780
- nodeID string
781
- mode structs.CSIVolumeClaimMode
782
- }
783
-
784
- func collectClaimsToGCImpl (vol * structs.CSIVolume , runningAllocs bool ) ([]gcClaimRequest , map [string ]int ) {
785
- gcAllocs := []gcClaimRequest {}
776
+ func collectClaimsToGCImpl (vol * structs.CSIVolume , runningAllocs bool ) map [string ]int {
786
777
nodeClaims := map [string ]int {} // node IDs -> count
787
778
788
779
collectFunc := func (allocs map [string ]* structs.Allocation ,
789
- mode structs.CSIVolumeClaimMode ) {
790
- for _ , alloc := range allocs {
791
- // we call denormalize on the volume above to populate
792
- // Allocation pointers. But the alloc might have been
793
- // garbage collected concurrently, so if the alloc is
794
- // still nil we can safely skip it.
795
- if alloc == nil {
796
- continue
780
+ claims map [string ]* structs.CSIVolumeClaim ) {
781
+
782
+ for allocID , alloc := range allocs {
783
+ claim , ok := claims [allocID ]
784
+ if ! ok {
785
+ // COMPAT(1.0): the CSIVolumeClaim fields were added
786
+ // after 0.11.1, so claims made before that may be
787
+ // missing this value. note that we'll have non-nil
788
+ // allocs here because we called denormalize on the
789
+ // value.
790
+ claim = & structs.CSIVolumeClaim {
791
+ AllocationID : allocID ,
792
+ NodeID : alloc .NodeID ,
793
+ State : structs .CSIVolumeClaimStateTaken ,
794
+ }
797
795
}
798
- nodeClaims [alloc .NodeID ]++
796
+ nodeClaims [claim .NodeID ]++
799
797
if runningAllocs || alloc .Terminated () {
800
- gcAllocs = append (gcAllocs , gcClaimRequest {
801
- allocID : alloc .ID ,
802
- nodeID : alloc .NodeID ,
803
- mode : mode ,
804
- })
798
+ // only overwrite the PastClaim if this is new,
799
+ // so that we can track state between subsequent calls
800
+ if _ , exists := vol .PastClaims [claim .AllocationID ]; ! exists {
801
+ claim .State = structs .CSIVolumeClaimStateTaken
802
+ vol .PastClaims [claim .AllocationID ] = claim
803
+ }
805
804
}
806
805
}
807
806
}
808
807
809
- collectFunc (vol .WriteAllocs , structs . CSIVolumeClaimWrite )
810
- collectFunc (vol .ReadAllocs , structs . CSIVolumeClaimRead )
811
- return gcAllocs , nodeClaims
808
+ collectFunc (vol .WriteAllocs , vol . WriteClaims )
809
+ collectFunc (vol .ReadAllocs , vol . ReadClaims )
810
+ return nodeClaims
812
811
}
813
812
814
813
type volumeClaimReapArgs struct {
815
814
vol * structs.CSIVolume
816
815
plug * structs.CSIPlugin
817
- allocID string
818
- nodeID string
819
- mode structs.CSIVolumeClaimMode
816
+ claim * structs.CSIVolumeClaim
820
817
region string
821
818
namespace string
822
819
leaderACL string
@@ -825,42 +822,78 @@ type volumeClaimReapArgs struct {
825
822
826
823
func volumeClaimReapImpl (srv RPCServer , args * volumeClaimReapArgs ) (map [string ]int , error ) {
827
824
vol := args .vol
828
- nodeID := args .nodeID
825
+ claim := args .claim
826
+
827
+ var err error
828
+ var nReq * cstructs.ClientCSINodeDetachVolumeRequest
829
+
830
+ checkpoint := func (claimState structs.CSIVolumeClaimState ) error {
831
+ req := & structs.CSIVolumeClaimRequest {
832
+ VolumeID : vol .ID ,
833
+ AllocationID : claim .AllocationID ,
834
+ Claim : structs .CSIVolumeClaimRelease ,
835
+ WriteRequest : structs.WriteRequest {
836
+ Region : args .region ,
837
+ Namespace : args .namespace ,
838
+ AuthToken : args .leaderACL ,
839
+ },
840
+ }
841
+ return srv .RPC ("CSIVolume.Claim" , req , & structs.CSIVolumeClaimResponse {})
842
+ }
843
+
844
+ // previous checkpoints may have set the past claim state already.
845
+ // in practice we should never see CSIVolumeClaimStateControllerDetached
846
+ // but having an option for the state makes it easy to add a checkpoint
847
+ // in a backwards compatible way if we need one later
848
+ switch claim .State {
849
+ case structs .CSIVolumeClaimStateNodeDetached :
850
+ goto NODE_DETACHED
851
+ case structs .CSIVolumeClaimStateControllerDetached :
852
+ goto RELEASE_CLAIM
853
+ case structs .CSIVolumeClaimStateReadyToFree :
854
+ goto RELEASE_CLAIM
855
+ }
829
856
830
857
// (1) NodePublish / NodeUnstage must be completed before controller
831
858
// operations or releasing the claim.
832
- nReq : = & cstructs.ClientCSINodeDetachVolumeRequest {
859
+ nReq = & cstructs.ClientCSINodeDetachVolumeRequest {
833
860
PluginID : args .plug .ID ,
834
861
VolumeID : vol .ID ,
835
862
ExternalID : vol .RemoteID (),
836
- AllocID : args . allocID ,
837
- NodeID : nodeID ,
863
+ AllocID : claim . AllocationID ,
864
+ NodeID : claim . NodeID ,
838
865
AttachmentMode : vol .AttachmentMode ,
839
866
AccessMode : vol .AccessMode ,
840
- ReadOnly : args . mode == structs .CSIVolumeClaimRead ,
867
+ ReadOnly : claim . Mode == structs .CSIVolumeClaimRead ,
841
868
}
842
- err : = srv .RPC ("ClientCSI.NodeDetachVolume" , nReq ,
869
+ err = srv .RPC ("ClientCSI.NodeDetachVolume" , nReq ,
843
870
& cstructs.ClientCSINodeDetachVolumeResponse {})
844
871
if err != nil {
845
872
return args .nodeClaims , err
846
873
}
847
- args .nodeClaims [nodeID ]--
874
+ err = checkpoint (structs .CSIVolumeClaimStateNodeDetached )
875
+ if err != nil {
876
+ return args .nodeClaims , err
877
+ }
878
+
879
+ NODE_DETACHED:
880
+ args .nodeClaims [claim .NodeID ]--
848
881
849
882
// (2) we only emit the controller unpublish if no other allocs
850
883
// on the node need it, but we also only want to make this
851
884
// call at most once per node
852
- if vol .ControllerRequired && args .nodeClaims [nodeID ] < 1 {
885
+ if vol .ControllerRequired && args .nodeClaims [claim . NodeID ] < 1 {
853
886
854
887
// we need to get the CSI Node ID, which is not the same as
855
888
// the Nomad Node ID
856
889
ws := memdb .NewWatchSet ()
857
- targetNode , err := srv .State ().NodeByID (ws , nodeID )
890
+ targetNode , err := srv .State ().NodeByID (ws , claim . NodeID )
858
891
if err != nil {
859
892
return args .nodeClaims , err
860
893
}
861
894
if targetNode == nil {
862
895
return args .nodeClaims , fmt .Errorf ("%s: %s" ,
863
- structs .ErrUnknownNodePrefix , nodeID )
896
+ structs .ErrUnknownNodePrefix , claim . NodeID )
864
897
}
865
898
targetCSIInfo , ok := targetNode .CSINodePlugins [args .plug .ID ]
866
899
if ! ok {
@@ -879,18 +912,9 @@ func volumeClaimReapImpl(srv RPCServer, args *volumeClaimReapArgs) (map[string]i
879
912
}
880
913
}
881
914
915
+ RELEASE_CLAIM:
882
916
// (3) release the claim from the state store, allowing it to be rescheduled
883
- req := & structs.CSIVolumeClaimRequest {
884
- VolumeID : vol .ID ,
885
- AllocationID : args .allocID ,
886
- Claim : structs .CSIVolumeClaimRelease ,
887
- WriteRequest : structs.WriteRequest {
888
- Region : args .region ,
889
- Namespace : args .namespace ,
890
- AuthToken : args .leaderACL ,
891
- },
892
- }
893
- err = srv .RPC ("CSIVolume.Claim" , req , & structs.CSIVolumeClaimResponse {})
917
+ err = checkpoint (structs .CSIVolumeClaimStateReadyToFree )
894
918
if err != nil {
895
919
return args .nodeClaims , err
896
920
}
0 commit comments