@@ -22,6 +22,7 @@ import (
22
22
"strings"
23
23
24
24
"k8s.io/api/core/v1"
25
+ storage "k8s.io/api/storage/v1"
25
26
"k8s.io/apimachinery/pkg/util/sets"
26
27
cloudvolume "k8s.io/cloud-provider/volume"
27
28
)
@@ -32,6 +33,9 @@ const (
32
33
// GCEPDInTreePluginName is the name of the intree plugin for GCE PD
33
34
GCEPDInTreePluginName = "kubernetes.io/gce-pd"
34
35
36
+ // GCEPDTopologyKey is the zonal topology key for GCE PD CSI Driver
37
+ GCEPDTopologyKey = "topology.gke.io/zone"
38
+
35
39
// Volume ID Expected Format
36
40
// "projects/{projectName}/zones/{zoneName}/disks/{diskName}"
37
41
volIDZonalFmt = "projects/%s/zones/%s/disks/%s"
@@ -55,9 +59,102 @@ func NewGCEPersistentDiskCSITranslator() InTreePlugin {
55
59
return & gcePersistentDiskCSITranslator {}
56
60
}
57
61
62
+ func translateAllowedTopologies (terms []v1.TopologySelectorTerm ) ([]v1.TopologySelectorTerm , error ) {
63
+ if terms == nil {
64
+ return nil , nil
65
+ }
66
+
67
+ newTopologies := []v1.TopologySelectorTerm {}
68
+ for _ , term := range terms {
69
+ newTerm := v1.TopologySelectorTerm {}
70
+ for _ , exp := range term .MatchLabelExpressions {
71
+ var newExp v1.TopologySelectorLabelRequirement
72
+ if exp .Key == v1 .LabelZoneFailureDomain {
73
+ newExp = v1.TopologySelectorLabelRequirement {
74
+ Key : GCEPDTopologyKey ,
75
+ Values : exp .Values ,
76
+ }
77
+ } else if exp .Key == GCEPDTopologyKey {
78
+ newExp = exp
79
+ } else {
80
+ return nil , fmt .Errorf ("unknown topology key: %v" , exp .Key )
81
+ }
82
+ newTerm .MatchLabelExpressions = append (newTerm .MatchLabelExpressions , newExp )
83
+ }
84
+ newTopologies = append (newTopologies , newTerm )
85
+ }
86
+ return newTopologies , nil
87
+ }
88
+
89
+ func generateToplogySelectors (key string , values []string ) []v1.TopologySelectorTerm {
90
+ return []v1.TopologySelectorTerm {
91
+ {
92
+ MatchLabelExpressions : []v1.TopologySelectorLabelRequirement {
93
+ {
94
+ Key : key ,
95
+ Values : values ,
96
+ },
97
+ },
98
+ },
99
+ }
100
+ }
101
+
58
102
// TranslateInTreeStorageClassParametersToCSI translates InTree GCE storage class parameters to CSI storage class
59
- func (g * gcePersistentDiskCSITranslator ) TranslateInTreeStorageClassParametersToCSI (scParameters map [string ]string ) (map [string ]string , error ) {
60
- return scParameters , nil
103
+ func (g * gcePersistentDiskCSITranslator ) TranslateInTreeStorageClassToCSI (sc * storage.StorageClass ) (* storage.StorageClass , error ) {
104
+ var generatedTopologies []v1.TopologySelectorTerm
105
+
106
+ np := map [string ]string {}
107
+ for k , v := range sc .Parameters {
108
+ switch strings .ToLower (k ) {
109
+ case "fstype" :
110
+ // prefixed fstype parameter is stripped out by external provisioner
111
+ np ["csi.storage.k8s.io/fstype" ] = v
112
+ // Strip out zone and zones parameters and translate them into topologies instead
113
+ case "zone" :
114
+ generatedTopologies = generateToplogySelectors (GCEPDTopologyKey , []string {v })
115
+ case "zones" :
116
+ generatedTopologies = generateToplogySelectors (GCEPDTopologyKey , strings .Split (v , "," ))
117
+ default :
118
+ np [k ] = v
119
+ }
120
+ }
121
+
122
+ if len (generatedTopologies ) > 0 && len (sc .AllowedTopologies ) > 0 {
123
+ return nil , fmt .Errorf ("cannot simultaneously set allowed topologies and zone/zones parameters" )
124
+ } else if len (generatedTopologies ) > 0 {
125
+ sc .AllowedTopologies = generatedTopologies
126
+ } else if len (sc .AllowedTopologies ) > 0 {
127
+ newTopologies , err := translateAllowedTopologies (sc .AllowedTopologies )
128
+ if err != nil {
129
+ return nil , fmt .Errorf ("failed translating allowed topologies: %v" , err )
130
+ }
131
+ sc .AllowedTopologies = newTopologies
132
+ }
133
+
134
+ sc .Parameters = np
135
+
136
+ return sc , nil
137
+ }
138
+
139
+ // backwardCompatibleAccessModes translates all instances of ReadWriteMany
140
+ // access mode from the in-tree plugin to ReadWriteOnce. This is because in-tree
141
+ // plugin never supported ReadWriteMany but also did not validate or enforce
142
+ // this access mode for pre-provisioned volumes. The GCE PD CSI Driver validates
143
+ // and enforces (fails) ReadWriteMany. Therefore we treat all in-tree
144
+ // ReadWriteMany as ReadWriteOnce volumes to not break legacy volumes.
145
+ func backwardCompatibleAccessModes (ams []v1.PersistentVolumeAccessMode ) []v1.PersistentVolumeAccessMode {
146
+ if ams == nil {
147
+ return nil
148
+ }
149
+ newAM := []v1.PersistentVolumeAccessMode {}
150
+ for _ , am := range ams {
151
+ if am == v1 .ReadWriteMany {
152
+ newAM = append (newAM , v1 .ReadWriteOnce )
153
+ } else {
154
+ newAM = append (newAM , am )
155
+ }
156
+ }
157
+ return newAM
61
158
}
62
159
63
160
// TranslateInTreePVToCSI takes a PV with GCEPersistentDisk set from in-tree
@@ -105,6 +202,7 @@ func (g *gcePersistentDiskCSITranslator) TranslateInTreePVToCSI(pv *v1.Persisten
105
202
106
203
pv .Spec .PersistentVolumeSource .GCEPersistentDisk = nil
107
204
pv .Spec .PersistentVolumeSource .CSI = csiSource
205
+ pv .Spec .AccessModes = backwardCompatibleAccessModes (pv .Spec .AccessModes )
108
206
109
207
return pv , nil
110
208
}
0 commit comments