1
|
1
|
new file mode 100644
|
...
|
...
|
@@ -0,0 +1,4206 @@
|
|
0
|
+diff -uNr --no-dereference kubernetes-1.8.1/api/swagger-spec/apps_v1alpha1.json cascade-kubernetes/api/swagger-spec/apps_v1alpha1.json
|
|
1
|
+--- kubernetes-1.8.1/api/swagger-spec/apps_v1alpha1.json 2018-01-23 22:47:25.146819339 +0000
|
|
2
|
+@@ -1459,6 +1459,10 @@
|
|
3
|
+ "photonPersistentDisk": {
|
|
4
|
+ "$ref": "v1.PhotonPersistentDiskVolumeSource",
|
|
5
|
+ "description": "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine"
|
|
6
|
++ },
|
|
7
|
++ "cascadeDisk": {
|
|
8
|
++ "$ref": "v1.CascadeDiskVolumeSource",
|
|
9
|
++ "description": "CascadeDisk represents a Cascade persistent disk attached and mounted on kubelets host machine"
|
|
10
|
+ }
|
|
11
|
+ }
|
|
12
|
+ },
|
|
13
|
+@@ -2105,6 +2109,23 @@
|
|
14
|
+ },
|
|
15
|
+ "fsType": {
|
|
16
|
+ "type": "string",
|
|
17
|
++ "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
|
18
|
++ }
|
|
19
|
++ }
|
|
20
|
++ },
|
|
21
|
++ "v1.CascadeDiskVolumeSource": {
|
|
22
|
++ "id": "v1.CascadeDiskVolumeSource",
|
|
23
|
++ "description": "Represents a Cascade persistent disk resource.",
|
|
24
|
++ "required": [
|
|
25
|
++ "diskID"
|
|
26
|
++ ],
|
|
27
|
++ "properties": {
|
|
28
|
++ "diskID": {
|
|
29
|
++ "type": "string",
|
|
30
|
++ "description": "ID that identifies Cascade persistent disk"
|
|
31
|
++ },
|
|
32
|
++ "fsType": {
|
|
33
|
++ "type": "string",
|
|
34
|
+ "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
|
35
|
+ }
|
|
36
|
+ }
|
|
37
|
+diff -uNr --no-dereference kubernetes-1.8.1/api/swagger-spec/apps_v1beta1.json cascade-kubernetes/api/swagger-spec/apps_v1beta1.json
|
|
38
|
+--- kubernetes-1.8.1/api/swagger-spec/apps_v1beta1.json 2018-01-23 22:47:25.146819339 +0000
|
|
39
|
+@@ -4400,6 +4400,10 @@
|
|
40
|
+ "$ref": "v1.PhotonPersistentDiskVolumeSource",
|
|
41
|
+ "description": "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine"
|
|
42
|
+ },
|
|
43
|
++ "cascadeDisk": {
|
|
44
|
++ "$ref": "v1.CascadeDiskVolumeSource",
|
|
45
|
++ "description": "CascadeDisk represents a Cascade persistent disk attached and mounted on kubelets host machine"
|
|
46
|
++ },
|
|
47
|
+ "projected": {
|
|
48
|
+ "$ref": "v1.ProjectedVolumeSource",
|
|
49
|
+ "description": "Items for all in one resources secrets, configmaps, and downward API"
|
|
50
|
+@@ -5123,6 +5127,23 @@
|
|
51
|
+ },
|
|
52
|
+ "fsType": {
|
|
53
|
+ "type": "string",
|
|
54
|
++ "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
|
55
|
++ }
|
|
56
|
++ }
|
|
57
|
++ },
|
|
58
|
++ "v1.CascadeDiskVolumeSource": {
|
|
59
|
++ "id": "v1.CascadeDiskVolumeSource",
|
|
60
|
++ "description": "Represents a Cascade persistent disk resource.",
|
|
61
|
++ "required": [
|
|
62
|
++ "diskID"
|
|
63
|
++ ],
|
|
64
|
++ "properties": {
|
|
65
|
++ "diskID": {
|
|
66
|
++ "type": "string",
|
|
67
|
++ "description": "ID that identifies Cascade persistent disk"
|
|
68
|
++ },
|
|
69
|
++ "fsType": {
|
|
70
|
++ "type": "string",
|
|
71
|
+ "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
|
72
|
+ }
|
|
73
|
+ }
|
|
74
|
+diff -uNr --no-dereference kubernetes-1.8.1/api/swagger-spec/apps_v1beta2.json cascade-kubernetes/api/swagger-spec/apps_v1beta2.json
|
|
75
|
+--- kubernetes-1.8.1/api/swagger-spec/apps_v1beta2.json 2018-01-23 22:47:25.146819339 +0000
|
|
76
|
+@@ -6730,6 +6730,10 @@
|
|
77
|
+ "$ref": "v1.PhotonPersistentDiskVolumeSource",
|
|
78
|
+ "description": "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine"
|
|
79
|
+ },
|
|
80
|
++ "cascadeDisk": {
|
|
81
|
++ "$ref": "v1.CascadeDiskVolumeSource",
|
|
82
|
++ "description": "CascadeDisk represents a Cascade persistent disk attached and mounted on kubelets host machine"
|
|
83
|
++ },
|
|
84
|
+ "projected": {
|
|
85
|
+ "$ref": "v1.ProjectedVolumeSource",
|
|
86
|
+ "description": "Items for all in one resources secrets, configmaps, and downward API"
|
|
87
|
+@@ -7453,6 +7457,23 @@
|
|
88
|
+ },
|
|
89
|
+ "fsType": {
|
|
90
|
+ "type": "string",
|
|
91
|
++ "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
|
92
|
++ }
|
|
93
|
++ }
|
|
94
|
++ },
|
|
95
|
++ "v1.CascadeDiskVolumeSource": {
|
|
96
|
++ "id": "v1.CascadeDiskVolumeSource",
|
|
97
|
++ "description": "Represents a Cascade persistent disk resource.",
|
|
98
|
++ "required": [
|
|
99
|
++ "diskID"
|
|
100
|
++ ],
|
|
101
|
++ "properties": {
|
|
102
|
++ "diskID": {
|
|
103
|
++ "type": "string",
|
|
104
|
++ "description": "ID that identifies Cascade persistent disk"
|
|
105
|
++ },
|
|
106
|
++ "fsType": {
|
|
107
|
++ "type": "string",
|
|
108
|
+ "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
|
109
|
+ }
|
|
110
|
+ }
|
|
111
|
+diff -uNr --no-dereference kubernetes-1.8.1/api/swagger-spec/batch_v1beta1.json cascade-kubernetes/api/swagger-spec/batch_v1beta1.json
|
|
112
|
+--- kubernetes-1.8.1/api/swagger-spec/batch_v1beta1.json 2018-01-23 22:47:25.150819339 +0000
|
|
113
|
+@@ -1850,6 +1850,10 @@
|
|
114
|
+ "$ref": "v1.PhotonPersistentDiskVolumeSource",
|
|
115
|
+ "description": "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine"
|
|
116
|
+ },
|
|
117
|
++ "cascadeDisk": {
|
|
118
|
++ "$ref": "v1.CascadeDiskVolumeSource",
|
|
119
|
++ "description": "CascadeDisk represents a Cascade persistent disk attached and mounted on kubelets host machine"
|
|
120
|
++ },
|
|
121
|
+ "projected": {
|
|
122
|
+ "$ref": "v1.ProjectedVolumeSource",
|
|
123
|
+ "description": "Items for all in one resources secrets, configmaps, and downward API"
|
|
124
|
+@@ -2573,6 +2577,23 @@
|
|
125
|
+ },
|
|
126
|
+ "fsType": {
|
|
127
|
+ "type": "string",
|
|
128
|
++ "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
|
129
|
++ }
|
|
130
|
++ }
|
|
131
|
++ },
|
|
132
|
++ "v1.CascadeDiskVolumeSource": {
|
|
133
|
++ "id": "v1.CascadeDiskVolumeSource",
|
|
134
|
++ "description": "Represents a Cascade persistent disk resource.",
|
|
135
|
++ "required": [
|
|
136
|
++ "diskID"
|
|
137
|
++ ],
|
|
138
|
++ "properties": {
|
|
139
|
++ "diskID": {
|
|
140
|
++ "type": "string",
|
|
141
|
++ "description": "ID that identifies Cascade persistent disk"
|
|
142
|
++ },
|
|
143
|
++ "fsType": {
|
|
144
|
++ "type": "string",
|
|
145
|
+ "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
|
146
|
+ }
|
|
147
|
+ }
|
|
148
|
+diff -uNr --no-dereference kubernetes-1.8.1/api/swagger-spec/batch_v1.json cascade-kubernetes/api/swagger-spec/batch_v1.json
|
|
149
|
+--- kubernetes-1.8.1/api/swagger-spec/batch_v1.json 2018-01-23 22:47:25.150819339 +0000
|
|
150
|
+@@ -1795,6 +1795,10 @@
|
|
151
|
+ "$ref": "v1.PhotonPersistentDiskVolumeSource",
|
|
152
|
+ "description": "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine"
|
|
153
|
+ },
|
|
154
|
++ "cascadeDisk": {
|
|
155
|
++ "$ref": "v1.CascadeDiskVolumeSource",
|
|
156
|
++ "description": "CascadeDisk represents a Cascade persistent disk attached and mounted on kubelets host machine"
|
|
157
|
++ },
|
|
158
|
+ "projected": {
|
|
159
|
+ "$ref": "v1.ProjectedVolumeSource",
|
|
160
|
+ "description": "Items for all in one resources secrets, configmaps, and downward API"
|
|
161
|
+@@ -2518,6 +2522,23 @@
|
|
162
|
+ },
|
|
163
|
+ "fsType": {
|
|
164
|
+ "type": "string",
|
|
165
|
++ "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
|
166
|
++ }
|
|
167
|
++ }
|
|
168
|
++ },
|
|
169
|
++ "v1.CascadeDiskVolumeSource": {
|
|
170
|
++ "id": "v1.CascadeDiskVolumeSource",
|
|
171
|
++ "description": "Represents a Cascade persistent disk resource.",
|
|
172
|
++ "required": [
|
|
173
|
++ "diskID"
|
|
174
|
++ ],
|
|
175
|
++ "properties": {
|
|
176
|
++ "diskID": {
|
|
177
|
++ "type": "string",
|
|
178
|
++ "description": "ID that identifies Cascade persistent disk"
|
|
179
|
++ },
|
|
180
|
++ "fsType": {
|
|
181
|
++ "type": "string",
|
|
182
|
+ "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
|
183
|
+ }
|
|
184
|
+ }
|
|
185
|
+diff -uNr --no-dereference kubernetes-1.8.1/api/swagger-spec/batch_v2alpha1.json cascade-kubernetes/api/swagger-spec/batch_v2alpha1.json
|
|
186
|
+--- kubernetes-1.8.1/api/swagger-spec/batch_v2alpha1.json 2018-01-23 22:47:25.150819339 +0000
|
|
187
|
+@@ -1865,6 +1865,10 @@
|
|
188
|
+ "storageos": {
|
|
189
|
+ "$ref": "v1.StorageOSVolumeSource",
|
|
190
|
+ "description": "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes."
|
|
191
|
++ },
|
|
192
|
++ "cascadeDisk": {
|
|
193
|
++ "$ref": "v1.CascadeDiskVolumeSource",
|
|
194
|
++ "description": "CascadeDisk represents a Cascade persistent disk attached and mounted on kubelets host machine"
|
|
195
|
+ }
|
|
196
|
+ }
|
|
197
|
+ },
|
|
198
|
+@@ -2769,6 +2773,23 @@
|
|
199
|
+ }
|
|
200
|
+ }
|
|
201
|
+ },
|
|
202
|
++ "v1.CascadeDiskVolumeSource": {
|
|
203
|
++ "id": "v1.CascadeDiskVolumeSource",
|
|
204
|
++ "description": "Represents a Cascade persistent disk resource.",
|
|
205
|
++ "required": [
|
|
206
|
++ "diskID"
|
|
207
|
++ ],
|
|
208
|
++ "properties": {
|
|
209
|
++ "diskID": {
|
|
210
|
++ "type": "string",
|
|
211
|
++ "description": "ID that identifies Cascade persistent disk"
|
|
212
|
++ },
|
|
213
|
++ "fsType": {
|
|
214
|
++ "type": "string",
|
|
215
|
++ "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
|
216
|
++ }
|
|
217
|
++ }
|
|
218
|
++ },
|
|
219
|
+ "v1.Container": {
|
|
220
|
+ "id": "v1.Container",
|
|
221
|
+ "description": "A single application container that you want to run within a pod.",
|
|
222
|
+diff -uNr --no-dereference kubernetes-1.8.1/api/swagger-spec/extensions_v1beta1.json cascade-kubernetes/api/swagger-spec/extensions_v1beta1.json
|
|
223
|
+--- kubernetes-1.8.1/api/swagger-spec/extensions_v1beta1.json 2018-01-23 22:47:25.150819339 +0000
|
|
224
|
+@@ -7363,6 +7363,10 @@
|
|
225
|
+ "storageos": {
|
|
226
|
+ "$ref": "v1.StorageOSVolumeSource",
|
|
227
|
+ "description": "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes."
|
|
228
|
++ },
|
|
229
|
++ "cascadeDisk": {
|
|
230
|
++ "$ref": "v1.CascadeDiskVolumeSource",
|
|
231
|
++ "description": "CascadeDisk represents a Cascade persistent disk attached and mounted on kubelets host machine"
|
|
232
|
+ }
|
|
233
|
+ }
|
|
234
|
+ },
|
|
235
|
+@@ -8071,6 +8075,23 @@
|
|
236
|
+ },
|
|
237
|
+ "fsType": {
|
|
238
|
+ "type": "string",
|
|
239
|
++ "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
|
240
|
++ }
|
|
241
|
++ }
|
|
242
|
++ },
|
|
243
|
++ "v1.CascadeDiskVolumeSource": {
|
|
244
|
++ "id": "v1.CascadeDiskVolumeSource",
|
|
245
|
++ "description": "Represents a Cascade persistent disk resource.",
|
|
246
|
++ "required": [
|
|
247
|
++ "diskID"
|
|
248
|
++ ],
|
|
249
|
++ "properties": {
|
|
250
|
++ "diskID": {
|
|
251
|
++ "type": "string",
|
|
252
|
++ "description": "ID that identifies Cascade persistent disk"
|
|
253
|
++ },
|
|
254
|
++ "fsType": {
|
|
255
|
++ "type": "string",
|
|
256
|
+ "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
|
257
|
+ }
|
|
258
|
+ }
|
|
259
|
+diff -uNr --no-dereference kubernetes-1.8.1/api/swagger-spec/settings.k8s.io_v1alpha1.json cascade-kubernetes/api/swagger-spec/settings.k8s.io_v1alpha1.json
|
|
260
|
+--- kubernetes-1.8.1/api/swagger-spec/settings.k8s.io_v1alpha1.json 2018-01-23 22:47:25.154819339 +0000
|
|
261
|
+@@ -1661,6 +1661,10 @@
|
|
262
|
+ "storageos": {
|
|
263
|
+ "$ref": "v1.StorageOSVolumeSource",
|
|
264
|
+ "description": "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes."
|
|
265
|
++ },
|
|
266
|
++ "cascadeDisk": {
|
|
267
|
++ "$ref": "v1.CascadeDiskVolumeSource",
|
|
268
|
++ "description": "CascadeDisk represents a Cascade persistent disk attached and mounted on kubelets host machine"
|
|
269
|
+ }
|
|
270
|
+ }
|
|
271
|
+ },
|
|
272
|
+@@ -2331,6 +2335,23 @@
|
|
273
|
+ },
|
|
274
|
+ "fsType": {
|
|
275
|
+ "type": "string",
|
|
276
|
++ "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
|
277
|
++ }
|
|
278
|
++ }
|
|
279
|
++ },
|
|
280
|
++ "v1.CascadeDiskVolumeSource": {
|
|
281
|
++ "id": "v1.CascadeDiskVolumeSource",
|
|
282
|
++ "description": "Represents a Cascade persistent disk resource.",
|
|
283
|
++ "required": [
|
|
284
|
++ "diskID"
|
|
285
|
++ ],
|
|
286
|
++ "properties": {
|
|
287
|
++ "diskID": {
|
|
288
|
++ "type": "string",
|
|
289
|
++ "description": "ID that identifies Cascade persistent disk"
|
|
290
|
++ },
|
|
291
|
++ "fsType": {
|
|
292
|
++ "type": "string",
|
|
293
|
+ "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
|
294
|
+ }
|
|
295
|
+ }
|
|
296
|
+diff -uNr --no-dereference kubernetes-1.8.1/api/swagger-spec/v1.json cascade-kubernetes/api/swagger-spec/v1.json
|
|
297
|
+--- kubernetes-1.8.1/api/swagger-spec/v1.json 2018-01-23 22:47:25.154819339 +0000
|
|
298
|
+@@ -20271,6 +20271,10 @@
|
|
299
|
+ "$ref": "v1.PhotonPersistentDiskVolumeSource",
|
|
300
|
+ "description": "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine"
|
|
301
|
+ },
|
|
302
|
++ "cascadeDisk": {
|
|
303
|
++ "$ref": "v1.CascadeDiskVolumeSource",
|
|
304
|
++ "description": "CascadeDisk represents a Cascade persistent disk attached and mounted on kubelets host machine"
|
|
305
|
++ },
|
|
306
|
+ "portworxVolume": {
|
|
307
|
+ "$ref": "v1.PortworxVolumeSource",
|
|
308
|
+ "description": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine"
|
|
309
|
+@@ -20834,6 +20838,23 @@
|
|
310
|
+ }
|
|
311
|
+ }
|
|
312
|
+ },
|
|
313
|
++ "v1.CascadeDiskVolumeSource": {
|
|
314
|
++ "id": "v1.CascadeDiskVolumeSource",
|
|
315
|
++ "description": "Represents a Cascade persistent disk resource.",
|
|
316
|
++ "required": [
|
|
317
|
++ "diskID"
|
|
318
|
++ ],
|
|
319
|
++ "properties": {
|
|
320
|
++ "diskID": {
|
|
321
|
++ "type": "string",
|
|
322
|
++ "description": "ID that identifies Cascade persistent disk"
|
|
323
|
++ },
|
|
324
|
++ "fsType": {
|
|
325
|
++ "type": "string",
|
|
326
|
++ "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
|
327
|
++ }
|
|
328
|
++ }
|
|
329
|
++ },
|
|
330
|
+ "v1.PortworxVolumeSource": {
|
|
331
|
+ "id": "v1.PortworxVolumeSource",
|
|
332
|
+ "description": "PortworxVolumeSource represents a Portworx volume resource.",
|
|
333
|
+@@ -21265,6 +21286,10 @@
|
|
334
|
+ "storageos": {
|
|
335
|
+ "$ref": "v1.StorageOSVolumeSource",
|
|
336
|
+ "description": "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes."
|
|
337
|
++ },
|
|
338
|
++ "cascadeDisk": {
|
|
339
|
++ "$ref": "v1.CascadeDiskVolumeSource",
|
|
340
|
++ "description": "CascadeDisk represents a Cascade persistent disk attached and mounted on kubelets host machine"
|
|
341
|
+ }
|
|
342
|
+ }
|
|
343
|
+ },
|
|
344
|
+diff -uNr --no-dereference kubernetes-1.8.1/cmd/kube-controller-manager/app/BUILD cascade-kubernetes/cmd/kube-controller-manager/app/BUILD
|
|
345
|
+--- kubernetes-1.8.1/cmd/kube-controller-manager/app/BUILD 2018-01-23 22:47:25.198819341 +0000
|
|
346
|
+@@ -42,6 +42,7 @@
|
|
347
|
+ "//pkg/cloudprovider/providers:go_default_library",
|
|
348
|
+ "//pkg/cloudprovider/providers/aws:go_default_library",
|
|
349
|
+ "//pkg/cloudprovider/providers/azure:go_default_library",
|
|
350
|
++ "//pkg/cloudprovider/providers/cascade:go_default_library",
|
|
351
|
+ "//pkg/cloudprovider/providers/gce:go_default_library",
|
|
352
|
+ "//pkg/cloudprovider/providers/openstack:go_default_library",
|
|
353
|
+ "//pkg/cloudprovider/providers/photon:go_default_library",
|
|
354
|
+@@ -84,6 +85,7 @@
|
|
355
|
+ "//pkg/volume/aws_ebs:go_default_library",
|
|
356
|
+ "//pkg/volume/azure_dd:go_default_library",
|
|
357
|
+ "//pkg/volume/azure_file:go_default_library",
|
|
358
|
++ "//pkg/volume/cascade_disk:go_default_library",
|
|
359
|
+ "//pkg/volume/cinder:go_default_library",
|
|
360
|
+ "//pkg/volume/fc:go_default_library",
|
|
361
|
+ "//pkg/volume/flexvolume:go_default_library",
|
|
362
|
+diff -uNr --no-dereference kubernetes-1.8.1/cmd/kube-controller-manager/app/plugins.go cascade-kubernetes/cmd/kube-controller-manager/app/plugins.go
|
|
363
|
+--- kubernetes-1.8.1/cmd/kube-controller-manager/app/plugins.go 2018-01-23 22:47:25.198819341 +0000
|
|
364
|
+@@ -32,6 +32,7 @@
|
|
365
|
+ "k8s.io/kubernetes/pkg/cloudprovider"
|
|
366
|
+ "k8s.io/kubernetes/pkg/cloudprovider/providers/aws"
|
|
367
|
+ "k8s.io/kubernetes/pkg/cloudprovider/providers/azure"
|
|
368
|
++ "k8s.io/kubernetes/pkg/cloudprovider/providers/cascade"
|
|
369
|
+ "k8s.io/kubernetes/pkg/cloudprovider/providers/gce"
|
|
370
|
+ "k8s.io/kubernetes/pkg/cloudprovider/providers/openstack"
|
|
371
|
+ "k8s.io/kubernetes/pkg/cloudprovider/providers/photon"
|
|
372
|
+@@ -58,6 +59,7 @@
|
|
373
|
+ "k8s.io/kubernetes/pkg/volume/storageos"
|
|
374
|
+ volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
|
375
|
+ "k8s.io/kubernetes/pkg/volume/vsphere_volume"
|
|
376
|
++ "k8s.io/kubernetes/pkg/volume/cascade_disk"
|
|
377
|
+ )
|
|
378
|
+
|
|
379
|
+ // ProbeAttachableVolumePlugins collects all volume plugins for the attach/
|
|
380
|
+@@ -78,6 +80,7 @@
|
|
381
|
+ allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...)
|
|
382
|
+ allPlugins = append(allPlugins, fc.ProbeVolumePlugins()...)
|
|
383
|
+ allPlugins = append(allPlugins, iscsi.ProbeVolumePlugins()...)
|
|
384
|
++ allPlugins = append(allPlugins, cascade_disk.ProbeVolumePlugins()...)
|
|
385
|
+ return allPlugins
|
|
386
|
+ }
|
|
387
|
+
|
|
388
|
+@@ -104,6 +107,7 @@
|
|
389
|
+ allPlugins = append(allPlugins, scaleio.ProbeVolumePlugins()...)
|
|
390
|
+ allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...)
|
|
391
|
+ allPlugins = append(allPlugins, fc.ProbeVolumePlugins()...)
|
|
392
|
++ allPlugins = append(allPlugins, cascade_disk.ProbeVolumePlugins()...)
|
|
393
|
+ return allPlugins
|
|
394
|
+ }
|
|
395
|
+
|
|
396
|
+@@ -168,6 +172,8 @@
|
|
397
|
+ allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...)
|
|
398
|
+ case photon.ProviderName == cloud.ProviderName():
|
|
399
|
+ allPlugins = append(allPlugins, photon_pd.ProbeVolumePlugins()...)
|
|
400
|
++ case cascade.ProviderName == cloud.ProviderName():
|
|
401
|
++ allPlugins = append(allPlugins, cascade_disk.ProbeVolumePlugins()...)
|
|
402
|
+ }
|
|
403
|
+ }
|
|
404
|
+
|
|
405
|
+diff -uNr --no-dereference kubernetes-1.8.1/cmd/kubelet/app/BUILD cascade-kubernetes/cmd/kubelet/app/BUILD
|
|
406
|
+--- kubernetes-1.8.1/cmd/kubelet/app/BUILD 2018-01-23 22:47:25.214819342 +0000
|
|
407
|
+@@ -73,6 +73,7 @@
|
|
408
|
+ "//pkg/volume/aws_ebs:go_default_library",
|
|
409
|
+ "//pkg/volume/azure_dd:go_default_library",
|
|
410
|
+ "//pkg/volume/azure_file:go_default_library",
|
|
411
|
++ "//pkg/volume/cascade_disk:go_default_library",
|
|
412
|
+ "//pkg/volume/cephfs:go_default_library",
|
|
413
|
+ "//pkg/volume/cinder:go_default_library",
|
|
414
|
+ "//pkg/volume/configmap:go_default_library",
|
|
415
|
+diff -uNr --no-dereference kubernetes-1.8.1/cmd/kubelet/app/plugins.go cascade-kubernetes/cmd/kubelet/app/plugins.go
|
|
416
|
+--- kubernetes-1.8.1/cmd/kubelet/app/plugins.go 2018-01-23 22:47:25.214819342 +0000
|
|
417
|
+@@ -32,6 +32,7 @@
|
|
418
|
+ "k8s.io/kubernetes/pkg/volume/aws_ebs"
|
|
419
|
+ "k8s.io/kubernetes/pkg/volume/azure_dd"
|
|
420
|
+ "k8s.io/kubernetes/pkg/volume/azure_file"
|
|
421
|
++ "k8s.io/kubernetes/pkg/volume/cascade_disk"
|
|
422
|
+ "k8s.io/kubernetes/pkg/volume/cephfs"
|
|
423
|
+ "k8s.io/kubernetes/pkg/volume/cinder"
|
|
424
|
+ "k8s.io/kubernetes/pkg/volume/configmap"
|
|
425
|
+@@ -96,6 +97,7 @@
|
|
426
|
+ allPlugins = append(allPlugins, scaleio.ProbeVolumePlugins()...)
|
|
427
|
+ allPlugins = append(allPlugins, local.ProbeVolumePlugins()...)
|
|
428
|
+ allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...)
|
|
429
|
++ allPlugins = append(allPlugins, cascade_disk.ProbeVolumePlugins()...)
|
|
430
|
+ return allPlugins
|
|
431
|
+ }
|
|
432
|
+
|
|
433
|
+diff -uNr --no-dereference kubernetes-1.8.1/federation/apis/openapi-spec/swagger.json cascade-kubernetes/federation/apis/openapi-spec/swagger.json
|
|
434
|
+--- kubernetes-1.8.1/federation/apis/openapi-spec/swagger.json 2018-01-23 22:47:25.278819344 +0000
|
|
435
|
+@@ -10540,6 +10540,22 @@
|
|
436
|
+ }
|
|
437
|
+ }
|
|
438
|
+ },
|
|
439
|
++ "io.k8s.api.core.v1.CascadeDiskVolumeSource": {
|
|
440
|
++ "description": "Represents a Cascade persistent disk resource.",
|
|
441
|
++ "required": [
|
|
442
|
++ "diskID"
|
|
443
|
++ ],
|
|
444
|
++ "properties": {
|
|
445
|
++ "fsType": {
|
|
446
|
++ "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.",
|
|
447
|
++ "type": "string"
|
|
448
|
++ },
|
|
449
|
++ "diskID": {
|
|
450
|
++ "description": "ID that identifies Cascade persistent disk",
|
|
451
|
++ "type": "string"
|
|
452
|
++ }
|
|
453
|
++ }
|
|
454
|
++ },
|
|
455
|
+ "io.k8s.api.core.v1.Capabilities": {
|
|
456
|
+ "description": "Adds and removes POSIX capabilities from running containers.",
|
|
457
|
+ "properties": {
|
|
458
|
+@@ -12865,6 +12881,10 @@
|
|
459
|
+ "vsphereVolume": {
|
|
460
|
+ "description": "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine",
|
|
461
|
+ "$ref": "#/definitions/io.k8s.api.core.v1.VsphereVirtualDiskVolumeSource"
|
|
462
|
++ },
|
|
463
|
++ "cascadeDisk": {
|
|
464
|
++ "description": "CascadeDisk represents a Cascade persistent disk attached and mounted on kubelets host machine",
|
|
465
|
++ "$ref": "#/definitions/io.k8s.api.core.v1.CascadeDiskVolumeSource"
|
|
466
|
+ }
|
|
467
|
+ }
|
|
468
|
+ },
|
|
469
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/api/types.go cascade-kubernetes/pkg/api/types.go
|
|
470
|
+--- kubernetes-1.8.1/pkg/api/types.go 2018-01-23 22:47:25.350819346 +0000
|
|
471
|
+@@ -316,6 +316,8 @@
|
|
472
|
+ // StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod
|
|
473
|
+ // +optional
|
|
474
|
+ StorageOS *StorageOSVolumeSource
|
|
475
|
++ // CascadeDisk represents a Cascade persistent disk attached and mounted on kubelets host machine
|
|
476
|
++ CascadeDisk *CascadeDiskVolumeSource
|
|
477
|
+ }
|
|
478
|
+
|
|
479
|
+ // Similar to VolumeSource but meant for the administrator who creates PVs.
|
|
480
|
+@@ -391,6 +393,8 @@
|
|
481
|
+ // More info: https://releases.k8s.io/HEAD/examples/volumes/storageos/README.md
|
|
482
|
+ // +optional
|
|
483
|
+ StorageOS *StorageOSPersistentVolumeSource
|
|
484
|
++ // CascadeDisk represents a Cascade persistent disk attached and mounted on kubelets host machine
|
|
485
|
++ CascadeDisk *CascadeDiskVolumeSource
|
|
486
|
+ }
|
|
487
|
+
|
|
488
|
+ type PersistentVolumeClaimVolumeSource struct {
|
|
489
|
+@@ -1333,6 +1337,16 @@
|
|
490
|
+ SecretRef *ObjectReference
|
|
491
|
+ }
|
|
492
|
+
|
|
493
|
++// Represents a Cascade persistent disk resource.
|
|
494
|
++type CascadeDiskVolumeSource struct {
|
|
495
|
++ // ID that identifies Cascade persistent disk
|
|
496
|
++ DiskID string
|
|
497
|
++ // Filesystem type to mount.
|
|
498
|
++ // Must be a filesystem type supported by the host operating system.
|
|
499
|
++ // Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
|
|
500
|
++ FSType string
|
|
501
|
++}
|
|
502
|
++
|
|
503
|
+ // Adapts a ConfigMap into a volume.
|
|
504
|
+ //
|
|
505
|
+ // The contents of the target ConfigMap's Data field will be presented in a
|
|
506
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/api/validation/validation.go cascade-kubernetes/pkg/api/validation/validation.go
|
|
507
|
+--- kubernetes-1.8.1/pkg/api/validation/validation.go 2018-01-23 22:47:25.354819347 +0000
|
|
508
|
+@@ -612,6 +612,14 @@
|
|
509
|
+ allErrs = append(allErrs, validateScaleIOVolumeSource(source.ScaleIO, fldPath.Child("scaleIO"))...)
|
|
510
|
+ }
|
|
511
|
+ }
|
|
512
|
++ if source.CascadeDisk != nil {
|
|
513
|
++ if numVolumes > 0 {
|
|
514
|
++ allErrs = append(allErrs, field.Forbidden(fldPath.Child("cascadeDisk"), "may not specify more than 1 volume type"))
|
|
515
|
++ } else {
|
|
516
|
++ numVolumes++
|
|
517
|
++ allErrs = append(allErrs, validateCascadeDiskVolumeSource(source.CascadeDisk, fldPath.Child("cascadeDisk"))...)
|
|
518
|
++ }
|
|
519
|
++ }
|
|
520
|
+
|
|
521
|
+ if numVolumes == 0 {
|
|
522
|
+ allErrs = append(allErrs, field.Required(fldPath, "must specify a volume type"))
|
|
523
|
+@@ -1283,6 +1291,14 @@
|
|
524
|
+ return allErrs
|
|
525
|
+ }
|
|
526
|
+
|
|
527
|
++func validateCascadeDiskVolumeSource(cd *api.CascadeDiskVolumeSource, fldPath *field.Path) field.ErrorList {
|
|
528
|
++ allErrs := field.ErrorList{}
|
|
529
|
++ if len(cd.DiskID) == 0 {
|
|
530
|
++ allErrs = append(allErrs, field.Required(fldPath.Child("diskID"), ""))
|
|
531
|
++ }
|
|
532
|
++ return allErrs
|
|
533
|
++}
|
|
534
|
++
|
|
535
|
+ // ValidatePersistentVolumeName checks that a name is appropriate for a
|
|
536
|
+ // PersistentVolumeName object.
|
|
537
|
+ var ValidatePersistentVolumeName = NameIsDNSSubdomain
|
|
538
|
+@@ -1504,6 +1520,14 @@
|
|
539
|
+ allErrs = append(allErrs, validateStorageOSPersistentVolumeSource(pv.Spec.StorageOS, specPath.Child("storageos"))...)
|
|
540
|
+ }
|
|
541
|
+ }
|
|
542
|
++ if pv.Spec.CascadeDisk != nil {
|
|
543
|
++ if numVolumes > 0 {
|
|
544
|
++ allErrs = append(allErrs, field.Forbidden(specPath.Child("cascadeDisk"), "may not specify more than 1 volume type"))
|
|
545
|
++ } else {
|
|
546
|
++ numVolumes++
|
|
547
|
++ allErrs = append(allErrs, validateCascadeDiskVolumeSource(pv.Spec.CascadeDisk, specPath.Child("cascadeDisk"))...)
|
|
548
|
++ }
|
|
549
|
++ }
|
|
550
|
+
|
|
551
|
+ if numVolumes == 0 {
|
|
552
|
+ allErrs = append(allErrs, field.Required(specPath, "must specify a volume type"))
|
|
553
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/apis/extensions/types.go cascade-kubernetes/pkg/apis/extensions/types.go
|
|
554
|
+--- kubernetes-1.8.1/pkg/apis/extensions/types.go 2018-01-23 22:47:25.370819347 +0000
|
|
555
|
+@@ -1002,6 +1002,7 @@
|
|
556
|
+ Projected FSType = "projected"
|
|
557
|
+ PortworxVolume FSType = "portworxVolume"
|
|
558
|
+ ScaleIO FSType = "scaleIO"
|
|
559
|
++ CascadeDisk FSType = "cascadeDisk"
|
|
560
|
+ All FSType = "*"
|
|
561
|
+ )
|
|
562
|
+
|
|
563
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/cloudprovider/providers/BUILD cascade-kubernetes/pkg/cloudprovider/providers/BUILD
|
|
564
|
+--- kubernetes-1.8.1/pkg/cloudprovider/providers/BUILD 2018-01-23 22:47:25.414819349 +0000
|
|
565
|
+@@ -11,6 +11,7 @@
|
|
566
|
+ deps = [
|
|
567
|
+ "//pkg/cloudprovider/providers/aws:go_default_library",
|
|
568
|
+ "//pkg/cloudprovider/providers/azure:go_default_library",
|
|
569
|
++ "//pkg/cloudprovider/providers/cascade:go_default_library",
|
|
570
|
+ "//pkg/cloudprovider/providers/cloudstack:go_default_library",
|
|
571
|
+ "//pkg/cloudprovider/providers/gce:go_default_library",
|
|
572
|
+ "//pkg/cloudprovider/providers/openstack:go_default_library",
|
|
573
|
+@@ -34,6 +35,7 @@
|
|
574
|
+ ":package-srcs",
|
|
575
|
+ "//pkg/cloudprovider/providers/aws:all-srcs",
|
|
576
|
+ "//pkg/cloudprovider/providers/azure:all-srcs",
|
|
577
|
++ "//pkg/cloudprovider/providers/cascade:all-srcs",
|
|
578
|
+ "//pkg/cloudprovider/providers/cloudstack:all-srcs",
|
|
579
|
+ "//pkg/cloudprovider/providers/fake:all-srcs",
|
|
580
|
+ "//pkg/cloudprovider/providers/gce:all-srcs",
|
|
581
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/apitypes.go cascade-kubernetes/pkg/cloudprovider/providers/cascade/apitypes.go
|
|
582
|
+--- kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/apitypes.go 1970-01-01 00:00:00.000000000 +0000
|
|
583
|
+@@ -0,0 +1,224 @@
|
|
584
|
++package cascade
|
|
585
|
++
|
|
586
|
++import "fmt"
|
|
587
|
++
|
|
588
|
++const (
|
|
589
|
++ NotFoundError = 1408
|
|
590
|
++ DiskNotFoundError = 3011
|
|
591
|
++)
|
|
592
|
++
|
|
593
|
++// Represents APIError returned by the API in case of an error.
|
|
594
|
++type APIError struct {
|
|
595
|
++ Code *string `json:"code"`
|
|
596
|
++ Data map[string]string `json:"data"`
|
|
597
|
++ ErrorCode int32 `json:"errorCode,omitempty"`
|
|
598
|
++ Message *string `json:"message"`
|
|
599
|
++ HttpStatusCode int `json:"-"` // Not part of API contract
|
|
600
|
++}
|
|
601
|
++
|
|
602
|
++// Implement Go error interface for ApiError.
|
|
603
|
++func (e APIError) Error() string {
|
|
604
|
++ return fmt.Sprintf(
|
|
605
|
++ "Cascade: { HTTP status: '%d', code: '%s', message: '%s', data: '%v', errorcode: '%d' }",
|
|
606
|
++ e.HttpStatusCode, StringVal(e.Code), StringVal(e.Message), e.Data, e.ErrorCode)
|
|
607
|
++}
|
|
608
|
++
|
|
609
|
++// Used to represent a generic HTTP error, i.e. an unexpected HTTP 500.
|
|
610
|
++type HttpError struct {
|
|
611
|
++ StatusCode int
|
|
612
|
++ Message string
|
|
613
|
++}
|
|
614
|
++
|
|
615
|
++// Implementation of error interface for HttpError.
|
|
616
|
++func (e HttpError) Error() string {
|
|
617
|
++ return fmt.Sprintf("Cascade: HTTP %d: %v", e.StatusCode, e.Message)
|
|
618
|
++}
|
|
619
|
++
|
|
620
|
++// Represents a task which gets returned for long running API calls.
|
|
621
|
++type Task struct {
|
|
622
|
++ EndTime int64 `json:"endTime,omitempty"`
|
|
623
|
++ Entity *Entity `json:"entity,omitempty"`
|
|
624
|
++ ID *string `json:"id"`
|
|
625
|
++ Operation string `json:"operation,omitempty"`
|
|
626
|
++ QueuedTime *int64 `json:"queuedTime"`
|
|
627
|
++ ResourceProperties interface{} `json:"resourceProperties,omitempty"`
|
|
628
|
++ SelfLink string `json:"selfLink,omitempty"`
|
|
629
|
++ StartedTime *int64 `json:"startedTime"`
|
|
630
|
++ State *string `json:"state"`
|
|
631
|
++ Steps []*Step `json:"steps"`
|
|
632
|
++}
|
|
633
|
++
|
|
634
|
++// Represents the entity associated with the task.
|
|
635
|
++type Entity struct {
|
|
636
|
++ ID *string `json:"id"`
|
|
637
|
++ Kind *string `json:"kind"`
|
|
638
|
++}
|
|
639
|
++
|
|
640
|
++// Represents a task that has entered into an error state. Task errors can be caught and type-checked against with the
|
|
641
|
++// usual Go idiom.
|
|
642
|
++type TaskError struct {
|
|
643
|
++ ID string `json:"id"`
|
|
644
|
++ Step Step `json:"step,omitempty"`
|
|
645
|
++}
|
|
646
|
++
|
|
647
|
++// Implement Go error interface for TaskError.
|
|
648
|
++func (e TaskError) Error() string {
|
|
649
|
++ return fmt.Sprintf("Cascade: Task '%s' is in error state: {@step==%s}", e.ID, GetStep(e.Step))
|
|
650
|
++}
|
|
651
|
++
|
|
652
|
++// An error representing a timeout while waiting for a task to complete.
|
|
653
|
++type TaskTimeoutError struct {
|
|
654
|
++ ID string
|
|
655
|
++}
|
|
656
|
++
|
|
657
|
++// Implement Go error interface for TaskTimeoutError.
|
|
658
|
++func (e TaskTimeoutError) Error() string {
|
|
659
|
++ return fmt.Sprintf("Cascade: Timed out waiting for task '%s'. "+
|
|
660
|
++ "Task may not be in error state, examine task for full details.", e.ID)
|
|
661
|
++}
|
|
662
|
++
|
|
663
|
++// Represents a step in a task.
|
|
664
|
++type Step struct {
|
|
665
|
++ EndTime int64 `json:"endTime,omitempty"`
|
|
666
|
++ Errors []*APIError `json:"errors"`
|
|
667
|
++ Operation string `json:"operation,omitempty"`
|
|
668
|
++ Options map[string]string `json:"options,omitempty"`
|
|
669
|
++ QueuedTime *int64 `json:"queuedTime"`
|
|
670
|
++ Sequence int32 `json:"sequence,omitempty"`
|
|
671
|
++ StartedTime *int64 `json:"startedTime"`
|
|
672
|
++ State *string `json:"state"`
|
|
673
|
++ Warnings []*APIError `json:"warnings"`
|
|
674
|
++}
|
|
675
|
++
|
|
676
|
++// Implement Go error interface for Step.
|
|
677
|
++func GetStep(s Step) string {
|
|
678
|
++ return fmt.Sprintf("{\"operation\"=>\"%s\",\"state\"=>\"%s}", s.Operation, StringVal(s.State))
|
|
679
|
++}
|
|
680
|
++
|
|
681
|
++// Represents the VM response returned by the API.
|
|
682
|
++type VM struct {
|
|
683
|
++ AttachedDisks []*AttachedDisk `json:"attachedDisks"`
|
|
684
|
++ Cost []*QuotaLineItem `json:"cost"`
|
|
685
|
++ Flavor *string `json:"flavor"`
|
|
686
|
++ FloatingIP string `json:"floatingIp,omitempty"`
|
|
687
|
++ HighAvailableVMGroupID string `json:"highAvailableVMGroupID,omitempty"`
|
|
688
|
++ ID *string `json:"id"`
|
|
689
|
++ Kind string `json:"kind"`
|
|
690
|
++ Name *string `json:"name"`
|
|
691
|
++ SelfLink string `json:"selfLink,omitempty"`
|
|
692
|
++ SourceImageID string `json:"sourceImageId,omitempty"`
|
|
693
|
++ State *string `json:"state"`
|
|
694
|
++ Subnets []string `json:"subnets"`
|
|
695
|
++ Tags []string `json:"tags"`
|
|
696
|
++}
|
|
697
|
++
|
|
698
|
++// Represents the listVMs response returned by the API.
|
|
699
|
++type VMList struct {
|
|
700
|
++ Items []*VM `json:"items"`
|
|
701
|
++ NextPageLink string `json:"nextPageLink,omitempty"`
|
|
702
|
++ PreviousPageLink string `json:"previousPageLink,omitempty"`
|
|
703
|
++}
|
|
704
|
++
|
|
705
|
++// Represents multiple VMs returned by the API.
|
|
706
|
++type VMs struct {
|
|
707
|
++ Items []VM `json:"items"`
|
|
708
|
++}
|
|
709
|
++
|
|
710
|
++// Represents the disks attached to the VMs.
|
|
711
|
++type AttachedDisk struct {
|
|
712
|
++ BootDisk *bool `json:"bootDisk"`
|
|
713
|
++ CapacityGb *int32 `json:"capacityGb"`
|
|
714
|
++ Flavor *string `json:"flavor"`
|
|
715
|
++ ID *string `json:"id"`
|
|
716
|
++ Kind *string `json:"kind"`
|
|
717
|
++ Name *string `json:"name"`
|
|
718
|
++ State *string `json:"state"`
|
|
719
|
++}
|
|
720
|
++
|
|
721
|
++// Represents an attach disk operation request.
|
|
722
|
++type VMDiskOperation struct {
|
|
723
|
++ Arguments map[string]string `json:"arguments,omitempty"`
|
|
724
|
++ DiskID *string `json:"diskId"`
|
|
725
|
++}
|
|
726
|
++
|
|
727
|
++// Represents the quota line items for the VM.
|
|
728
|
++type QuotaLineItem struct {
|
|
729
|
++ Key *string `json:"key"`
|
|
730
|
++ Unit *string `json:"unit"`
|
|
731
|
++ Value *float64 `json:"value"`
|
|
732
|
++}
|
|
733
|
++
|
|
734
|
++// Represents a persistent disk
|
|
735
|
++type PersistentDisk struct {
|
|
736
|
++ CapacityGB int32 `json:"capacityGb,omitempty"`
|
|
737
|
++ Cost []*QuotaLineItem `json:"cost"`
|
|
738
|
++ Datastore string `json:"datastore,omitempty"`
|
|
739
|
++ Flavor *string `json:"flavor"`
|
|
740
|
++ ID *string `json:"id"`
|
|
741
|
++ Kind string `json:"kind"`
|
|
742
|
++ Name *string `json:"name"`
|
|
743
|
++ SelfLink string `json:"selfLink,omitempty"`
|
|
744
|
++ State *string `json:"state"`
|
|
745
|
++ Tags []string `json:"tags"`
|
|
746
|
++ VM string `json:"vm"`
|
|
747
|
++ MountDevice string `json:"mountDevice,omitempty"`
|
|
748
|
++ Zone *string `json:"zone"`
|
|
749
|
++}
|
|
750
|
++
|
|
751
|
++// Represents the spec for creating a disk.
|
|
752
|
++type DiskCreateSpec struct {
|
|
753
|
++ Affinities []*LocalitySpec `json:"affinities"`
|
|
754
|
++ CapacityGB *int32 `json:"capacityGb"`
|
|
755
|
++ Flavor *string `json:"flavor"`
|
|
756
|
++ Kind *string `json:"kind"`
|
|
757
|
++ Name *string `json:"name"`
|
|
758
|
++ Tags []string `json:"tags"`
|
|
759
|
++ Zone *string `json:"zone"`
|
|
760
|
++}
|
|
761
|
++
|
|
762
|
++// Represents the spec for specifying affinity for a disk with another entity.
|
|
763
|
++type LocalitySpec struct {
|
|
764
|
++ ID *string `json:"id"`
|
|
765
|
++ Kind *string `json:"kind"`
|
|
766
|
++}
|
|
767
|
++
|
|
768
|
++// Represens the LoadBalancer response returned by the API.
|
|
769
|
++type LoadBalancer struct {
|
|
770
|
++ Endpoint *string `json:"endpoint"`
|
|
771
|
++}
|
|
772
|
++
|
|
773
|
++// Represents the spec for creating a LoadBalancer.
|
|
774
|
++type LoadBalancerCreateSpec struct {
|
|
775
|
++ HealthCheck *LoadBalancerHealthCheck `json:"healthCheck"`
|
|
776
|
++ Name *string `json:"name"`
|
|
777
|
++ PortMaps []*LoadBalancerPortMap `json:"portMaps"`
|
|
778
|
++ Type *string `json:"type"`
|
|
779
|
++}
|
|
780
|
++
|
|
781
|
++// Represents the health check spec for a load balancer.
|
|
782
|
++type LoadBalancerHealthCheck struct {
|
|
783
|
++ HealthyThreshold int64 `json:"healthyThreshold,omitempty"`
|
|
784
|
++ IntervalInSeconds int64 `json:"intervalInSeconds,omitempty"`
|
|
785
|
++ Path *string `json:"path,omitempty"`
|
|
786
|
++ Port *int64 `json:"port"`
|
|
787
|
++ Protocol *string `json:"protocol"`
|
|
788
|
++}
|
|
789
|
++
|
|
790
|
++// Represents a port mapping spec for a load balancer.
|
|
791
|
++type LoadBalancerPortMap struct {
|
|
792
|
++ AllowedCidrs []*string `json:"allowedCidrs"`
|
|
793
|
++ InstancePort *int64 `json:"instancePort"`
|
|
794
|
++ InstanceProtocol *string `json:"instanceProtocol"`
|
|
795
|
++ LoadBalancerPort *int64 `json:"loadBalancerPort"`
|
|
796
|
++ LoadBalancerProtocol *string `json:"loadBalancerProtocol"`
|
|
797
|
++}
|
|
798
|
++
|
|
799
|
++// Represents a VM to be registered with or deregistered from the load balancer.
|
|
800
|
++type LoadBalancerVM struct {
|
|
801
|
++ ID *string `json:"id"`
|
|
802
|
++}
|
|
803
|
++
|
|
804
|
++// Represents a list of VMs to be registered with or deregistered from the load balancer.
|
|
805
|
++type LoadBalancerVMUpdate struct {
|
|
806
|
++ VMIds []*LoadBalancerVM `json:"vmIds"`
|
|
807
|
++}
|
|
808
|
+\ No newline at end of file
|
|
809
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/auth.go cascade-kubernetes/pkg/cloudprovider/providers/cascade/auth.go
|
|
810
|
+--- kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/auth.go 1970-01-01 00:00:00.000000000 +0000
|
|
811
|
+@@ -0,0 +1,129 @@
|
|
812
|
++package cascade
|
|
813
|
++
|
|
814
|
++import (
|
|
815
|
++ "fmt"
|
|
816
|
++ "strings"
|
|
817
|
++ "github.com/golang/glog"
|
|
818
|
++ "os/exec"
|
|
819
|
++)
|
|
820
|
++
|
|
821
|
++const (
|
|
822
|
++ tScope = "openid offline_access rs_admin_server at_groups rs_vmdir"
|
|
823
|
++)
|
|
824
|
++
|
|
825
|
++// AuthConfig contains configuration information for the authentication client.
|
|
826
|
++type AuthConfig struct {
|
|
827
|
++ tenantName string
|
|
828
|
++ authEndpoint string
|
|
829
|
++ machineAccountName string
|
|
830
|
++}
|
|
831
|
++
|
|
832
|
++// AuthClient defines functions related to authentication.
|
|
833
|
++type AuthClient struct {
|
|
834
|
++ cfg *AuthConfig
|
|
835
|
++}
|
|
836
|
++
|
|
837
|
++// NewAuthClient creates a new authentication client
|
|
838
|
++func NewAuthClient(cascadeCfg *CascadeConfig) (*AuthClient, error) {
|
|
839
|
++ return &AuthClient{
|
|
840
|
++ cfg: &AuthConfig{
|
|
841
|
++ tenantName: cascadeCfg.Global.TenantName,
|
|
842
|
++ authEndpoint: cascadeCfg.Global.AuthEndpoint,
|
|
843
|
++ machineAccountName: fmt.Sprintf("%s@%s", cascadeCfg.Global.DNSName, cascadeCfg.Global.DomainName),
|
|
844
|
++ },
|
|
845
|
++ }, nil
|
|
846
|
++}
|
|
847
|
++
|
|
848
|
++func (c *AuthClient) GetTokensByMachineAccount() (*TokenOptions, error) {
|
|
849
|
++ // Execute a lwregshell command which gets the machine account password, trims it and un-escapes it.
|
|
850
|
++ cmd := "/opt/likewise/bin/lwregshell list_values '[\\Services\\vmdir]' | grep dcAccountPassword | " +
|
|
851
|
++ "awk '{print $NF}' | rev | cut -c2- | rev | cut -c2-"
|
|
852
|
++ output, err := exec.Command("bash", "-c", cmd).Output()
|
|
853
|
++ if err != nil {
|
|
854
|
++ glog.Errorf("Cascade Cloud Provider: Failed to get machine account credentials. Cannot create Client.")
|
|
855
|
++ return nil, fmt.Errorf("Failed to get machine account credentials, err: %v", err)
|
|
856
|
++ }
|
|
857
|
++ // Unescape the escaped machine account password. This could not be done as a part of the shell command itself
|
|
858
|
++ // because characters like ` are not escaped correctly by Lightwave causing failures while unescaping using shell
|
|
859
|
++ // commands like echo or printf. So this is done by the unescape function below which is written based on
|
|
860
|
++ // recommendation from the Lightwave team.
|
|
861
|
++ escapedPassword := strings.TrimSpace(string(output))
|
|
862
|
++ password := unescape(escapedPassword)
|
|
863
|
++
|
|
864
|
++ return c.GetTokensByCredentials(c.cfg.machineAccountName, password)
|
|
865
|
++}
|
|
866
|
++
|
|
867
|
++// GetTokensByPassword gets tokens using username and password
|
|
868
|
++func (c *AuthClient) GetTokensByCredentials(username, password string) (*TokenOptions, error) {
|
|
869
|
++ // Parse tenant part from username
|
|
870
|
++ parts := strings.Split(username, "@")
|
|
871
|
++ if len(parts) != 2 {
|
|
872
|
++ return nil, fmt.Errorf("Invalid full user name '%s': expected user@tenant", username)
|
|
873
|
++ }
|
|
874
|
++ tenant := parts[1]
|
|
875
|
++
|
|
876
|
++ oidcClient, err := buildOIDCClient(c.cfg.authEndpoint)
|
|
877
|
++ if err != nil {
|
|
878
|
++ return nil, err
|
|
879
|
++ }
|
|
880
|
++
|
|
881
|
++ tokenResponse, err := oidcClient.GetTokenByPasswordGrant(tenant, username, password)
|
|
882
|
++ if err != nil {
|
|
883
|
++ return nil, err
|
|
884
|
++ }
|
|
885
|
++
|
|
886
|
++ return toTokenOptions(tokenResponse), nil
|
|
887
|
++}
|
|
888
|
++
|
|
889
|
++// GetTokensByRefreshToken gets tokens using refresh token
|
|
890
|
++func (c *AuthClient) GetTokensByRefreshToken(refreshtoken string) (*TokenOptions, error) {
|
|
891
|
++ oidcClient, err := buildOIDCClient(c.cfg.authEndpoint)
|
|
892
|
++ if err != nil {
|
|
893
|
++ return nil, err
|
|
894
|
++ }
|
|
895
|
++
|
|
896
|
++ tokenResponse, err := oidcClient.GetTokenByRefreshTokenGrant(c.cfg.tenantName, refreshtoken)
|
|
897
|
++ if err != nil {
|
|
898
|
++ return nil, err
|
|
899
|
++ }
|
|
900
|
++
|
|
901
|
++ return toTokenOptions(tokenResponse), nil
|
|
902
|
++}
|
|
903
|
++
|
|
904
|
++func buildOIDCClient(authEndpoint string) (*OIDCClient, error) {
|
|
905
|
++ options := &OIDCClientOptions{
|
|
906
|
++ IgnoreCertificate: true,
|
|
907
|
++ RootCAs: nil,
|
|
908
|
++ TokenScope: tScope,
|
|
909
|
++ }
|
|
910
|
++
|
|
911
|
++ return NewOIDCClient(authEndpoint, options, nil), nil
|
|
912
|
++}
|
|
913
|
++
|
|
914
|
++func toTokenOptions(response *OIDCTokenResponse) *TokenOptions {
|
|
915
|
++ return &TokenOptions{
|
|
916
|
++ AccessToken: response.AccessToken,
|
|
917
|
++ ExpiresIn: response.ExpiresIn,
|
|
918
|
++ RefreshToken: response.RefreshToken,
|
|
919
|
++ IDToken: response.IDToken,
|
|
920
|
++ TokenType: response.TokenType,
|
|
921
|
++ }
|
|
922
|
++}
|
|
923
|
++
|
|
924
|
++// unescape function unescapes an escaped string. It does so by removing a backslash character and keeping the character
|
|
925
|
++// following the backslash character.
|
|
926
|
++func unescape(input string) string {
|
|
927
|
++ var output string
|
|
928
|
++ escaped := false
|
|
929
|
++ for _, character := range input {
|
|
930
|
++ if character == '\\' && !escaped {
|
|
931
|
++ escaped = true
|
|
932
|
++ } else {
|
|
933
|
++ if escaped {
|
|
934
|
++ escaped = false
|
|
935
|
++ }
|
|
936
|
++ output = output + string(character)
|
|
937
|
++ }
|
|
938
|
++ }
|
|
939
|
++ return output
|
|
940
|
++}
|
|
941
|
+\ No newline at end of file
|
|
942
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/BUILD cascade-kubernetes/pkg/cloudprovider/providers/cascade/BUILD
|
|
943
|
+--- kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/BUILD 1970-01-01 00:00:00.000000000 +0000
|
|
944
|
+@@ -0,0 +1,44 @@
|
|
945
|
++package(default_visibility = ["//visibility:public"])
|
|
946
|
++
|
|
947
|
++load(
|
|
948
|
++ "@io_bazel_rules_go//go:def.bzl",
|
|
949
|
++ "go_library",
|
|
950
|
++)
|
|
951
|
++
|
|
952
|
++go_library(
|
|
953
|
++ name = "go_default_library",
|
|
954
|
++ srcs = [
|
|
955
|
++ "apitypes.go",
|
|
956
|
++ "auth.go",
|
|
957
|
++ "cascade.go",
|
|
958
|
++ "cascade_disks.go",
|
|
959
|
++ "cascade_instances.go",
|
|
960
|
++ "cascade_loadbalancer.go",
|
|
961
|
++ "client.go",
|
|
962
|
++ "oidcclient.go",
|
|
963
|
++ "restclient.go",
|
|
964
|
++ "utils.go"
|
|
965
|
++ ],
|
|
966
|
++ deps = [
|
|
967
|
++ "//pkg/api/v1/helper:go_default_library",
|
|
968
|
++ "//pkg/cloudprovider:go_default_library",
|
|
969
|
++ "//pkg/controller:go_default_library",
|
|
970
|
++ "//vendor/github.com/golang/glog:go_default_library",
|
|
971
|
++ "//vendor/gopkg.in/gcfg.v1:go_default_library",
|
|
972
|
++ "//vendor/k8s.io/api/core/v1:go_default_library",
|
|
973
|
++ "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
|
974
|
++ ],
|
|
975
|
++)
|
|
976
|
++
|
|
977
|
++filegroup(
|
|
978
|
++ name = "package-srcs",
|
|
979
|
++ srcs = glob(["**"]),
|
|
980
|
++ tags = ["automanaged"],
|
|
981
|
++ visibility = ["//visibility:private"],
|
|
982
|
++)
|
|
983
|
++
|
|
984
|
++filegroup(
|
|
985
|
++ name = "all-srcs",
|
|
986
|
++ srcs = [":package-srcs"],
|
|
987
|
++ tags = ["automanaged"],
|
|
988
|
++)
|
|
989
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/cascade_disks.go cascade-kubernetes/pkg/cloudprovider/providers/cascade/cascade_disks.go
|
|
990
|
+--- kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/cascade_disks.go 1970-01-01 00:00:00.000000000 +0000
|
|
991
|
+@@ -0,0 +1,197 @@
|
|
992
|
++package cascade
|
|
993
|
++
|
|
994
|
++import (
|
|
995
|
++ "github.com/golang/glog"
|
|
996
|
++ k8stypes "k8s.io/apimachinery/pkg/types"
|
|
997
|
++ "k8s.io/kubernetes/pkg/volume"
|
|
998
|
++ "k8s.io/apimachinery/pkg/util/sets"
|
|
999
|
++ "k8s.io/kubernetes/pkg/kubelet/apis"
|
|
1000
|
++)
|
|
1001
|
++
|
|
1002
|
++// Attaches given virtual disk volume to the node running kubelet.
|
|
1003
|
++func (cc *CascadeCloud) AttachDisk(diskID string, nodeName k8stypes.NodeName) (string, error) {
|
|
1004
|
++ operation := &VMDiskOperation{
|
|
1005
|
++ DiskID: StringPtr(diskID),
|
|
1006
|
++ }
|
|
1007
|
++
|
|
1008
|
++ vmID, err := cc.InstanceID(nodeName)
|
|
1009
|
++ if err != nil {
|
|
1010
|
++ glog.Errorf("Cascade Cloud Provider: cc.InstanceID failed for AttachDisk. Error[%v]", err)
|
|
1011
|
++ return "", err
|
|
1012
|
++ }
|
|
1013
|
++
|
|
1014
|
++ task, err := cc.apiClient.AttachDisk(vmID, operation)
|
|
1015
|
++ if err != nil {
|
|
1016
|
++ glog.Errorf("Cascade Cloud Provider: Failed to attach disk with ID %s. Error[%v]", diskID, err)
|
|
1017
|
++ return "", err
|
|
1018
|
++ }
|
|
1019
|
++
|
|
1020
|
++ _, err = cc.apiClient.WaitForTask(StringVal(task.ID))
|
|
1021
|
++ if err != nil {
|
|
1022
|
++ glog.Errorf("Cascade Cloud Provider: Failed to wait for task to attach disk with ID %s. Error[%v]",
|
|
1023
|
++ diskID, err)
|
|
1024
|
++ return "", err
|
|
1025
|
++ }
|
|
1026
|
++
|
|
1027
|
++ disk, err := cc.apiClient.GetDisk(diskID)
|
|
1028
|
++ if err != nil {
|
|
1029
|
++ glog.Errorf("Cascade Cloud Provider: Failed to Get disk with diskID %s. Error[%v]", diskID, err)
|
|
1030
|
++ return "", err
|
|
1031
|
++ }
|
|
1032
|
++
|
|
1033
|
++ return disk.MountDevice, nil
|
|
1034
|
++}
|
|
1035
|
++
|
|
1036
|
++// Detaches given virtual disk volume from the node running kubelet.
|
|
1037
|
++func (cc *CascadeCloud) DetachDisk(diskID string, nodeName k8stypes.NodeName) error {
|
|
1038
|
++ operation := &VMDiskOperation{
|
|
1039
|
++ DiskID: StringPtr(diskID),
|
|
1040
|
++ }
|
|
1041
|
++
|
|
1042
|
++ vmID, err := cc.InstanceID(nodeName)
|
|
1043
|
++ if err != nil {
|
|
1044
|
++ glog.Errorf("Cascade Cloud Provider: cc.InstanceID failed for DetachDisk. Error[%v]", err)
|
|
1045
|
++ return err
|
|
1046
|
++ }
|
|
1047
|
++
|
|
1048
|
++ task, err := cc.apiClient.DetachDisk(vmID, operation)
|
|
1049
|
++ if err != nil {
|
|
1050
|
++ glog.Errorf("Cascade Cloud Provider: Failed to detach disk with pdID %s. Error[%v]", diskID, err)
|
|
1051
|
++ return err
|
|
1052
|
++ }
|
|
1053
|
++
|
|
1054
|
++ _, err = cc.apiClient.WaitForTask(StringVal(task.ID))
|
|
1055
|
++ if err != nil {
|
|
1056
|
++ glog.Errorf("Cascade Cloud Provider: Failed to wait for task to detach disk with pdID %s. Error[%v]",
|
|
1057
|
++ diskID, err)
|
|
1058
|
++ return err
|
|
1059
|
++ }
|
|
1060
|
++
|
|
1061
|
++ return nil
|
|
1062
|
++}
|
|
1063
|
++
|
|
1064
|
++// DiskIsAttached returns if disk is attached to the VM using controllers supported by the plugin.
|
|
1065
|
++func (cc *CascadeCloud) DiskIsAttached(diskID string, nodeName k8stypes.NodeName) (bool, error) {
|
|
1066
|
++ disk, err := cc.apiClient.GetDisk(diskID)
|
|
1067
|
++ if err != nil {
|
|
1068
|
++ glog.Errorf("Cascade Cloud Provider: Failed to Get disk with diskID %s. Error[%v]", diskID, err)
|
|
1069
|
++ return false, err
|
|
1070
|
++ }
|
|
1071
|
++
|
|
1072
|
++ vmID, err := cc.InstanceID(nodeName)
|
|
1073
|
++ if err != nil {
|
|
1074
|
++ glog.Errorf("Cascade Cloud Provider: cc.InstanceID failed for DiskIsAttached. Error[%v]", err)
|
|
1075
|
++ return false, err
|
|
1076
|
++ }
|
|
1077
|
++
|
|
1078
|
++ if disk.VM == vmID {
|
|
1079
|
++ return true, nil
|
|
1080
|
++ }
|
|
1081
|
++
|
|
1082
|
++ return false, nil
|
|
1083
|
++}
|
|
1084
|
++
|
|
1085
|
++// DisksAreAttached returns if disks are attached to the VM using controllers supported by the plugin.
|
|
1086
|
++func (cc *CascadeCloud) DisksAreAttached(diskIDs []string, nodeName k8stypes.NodeName) (map[string]bool, error) {
|
|
1087
|
++ attached := make(map[string]bool)
|
|
1088
|
++ for _, diskID := range diskIDs {
|
|
1089
|
++ attached[diskID] = false
|
|
1090
|
++ }
|
|
1091
|
++
|
|
1092
|
++ vmID, err := cc.InstanceID(nodeName)
|
|
1093
|
++ if err != nil {
|
|
1094
|
++ glog.Errorf("Cascade Cloud Provider: cc.InstanceID failed for DiskIsAttached. Error[%v]", err)
|
|
1095
|
++ return attached, err
|
|
1096
|
++ }
|
|
1097
|
++
|
|
1098
|
++ for _, diskID := range diskIDs {
|
|
1099
|
++ disk, err := cc.apiClient.GetDisk(diskID)
|
|
1100
|
++ if err != nil {
|
|
1101
|
++ glog.Warningf("Cascade Cloud Provider: failed to get VMs for persistent disk %s, err [%v]",
|
|
1102
|
++ diskID, err)
|
|
1103
|
++ } else {
|
|
1104
|
++ if disk.VM == vmID {
|
|
1105
|
++ attached[diskID] = true
|
|
1106
|
++ }
|
|
1107
|
++ }
|
|
1108
|
++ }
|
|
1109
|
++
|
|
1110
|
++ return attached, nil
|
|
1111
|
++}
|
|
1112
|
++
|
|
1113
|
++// Create a volume of given size (in GB).
|
|
1114
|
++func (cc *CascadeCloud) CreateDisk(volumeOptions *VolumeOptions) (diskID string, err error) {
|
|
1115
|
++ // Get Zones for the cluster
|
|
1116
|
++ zones, err := cc.apiClient.GetZones()
|
|
1117
|
++ if err != nil {
|
|
1118
|
++ glog.Errorf("Cascade Cloud Provider: Failed to Get zones for the cluster. Error[%v]", err)
|
|
1119
|
++ return "", err
|
|
1120
|
++ }
|
|
1121
|
++
|
|
1122
|
++ // Pick a zone to place the disk in.
|
|
1123
|
++ zoneSet := sets.NewString()
|
|
1124
|
++ for _, zone := range zones {
|
|
1125
|
++ zoneSet.Insert(zone)
|
|
1126
|
++ }
|
|
1127
|
++ zone := volume.ChooseZoneForVolume(zoneSet, volumeOptions.Name)
|
|
1128
|
++
|
|
1129
|
++ diskSpec := DiskCreateSpec{}
|
|
1130
|
++ diskSpec.Name = StringPtr(volumeOptions.Name)
|
|
1131
|
++ diskSpec.Flavor = StringPtr(volumeOptions.Flavor)
|
|
1132
|
++ diskSpec.CapacityGB = Int32Ptr(int32(volumeOptions.CapacityGB))
|
|
1133
|
++ diskSpec.Kind = StringPtr(DiskSpecKind)
|
|
1134
|
++ diskSpec.Zone = StringPtr(zone)
|
|
1135
|
++
|
|
1136
|
++ task, err := cc.apiClient.CreateDisk(&diskSpec)
|
|
1137
|
++ if err != nil {
|
|
1138
|
++ glog.Errorf("Cascade Cloud Provider: Failed to CreateDisk. Error[%v]", err)
|
|
1139
|
++ return "", err
|
|
1140
|
++ }
|
|
1141
|
++
|
|
1142
|
++ waitTask, err := cc.apiClient.WaitForTask(StringVal(task.ID))
|
|
1143
|
++ if err != nil {
|
|
1144
|
++ glog.Errorf("Cascade Cloud Provider: Failed to wait for task to CreateDisk. Error[%v]", err)
|
|
1145
|
++ return "", err
|
|
1146
|
++ }
|
|
1147
|
++
|
|
1148
|
++ return StringVal(waitTask.Entity.ID), nil
|
|
1149
|
++}
|
|
1150
|
++
|
|
1151
|
++// Deletes a volume given volume name.
|
|
1152
|
++func (cc *CascadeCloud) DeleteDisk(diskID string) error {
|
|
1153
|
++ task, err := cc.apiClient.DeleteDisk(diskID)
|
|
1154
|
++ if err != nil {
|
|
1155
|
++ glog.Errorf("Cascade Cloud Provider: Failed to DeleteDisk. Error[%v]", err)
|
|
1156
|
++ // If we get a DiskNotFound error, we assume that the disk is already deleted. So we don't return an error here.
|
|
1157
|
++ switch err.(type) {
|
|
1158
|
++ case APIError:
|
|
1159
|
++ if err.(APIError).ErrorCode == DiskNotFoundError {
|
|
1160
|
++ return nil
|
|
1161
|
++ }
|
|
1162
|
++ }
|
|
1163
|
++ return err
|
|
1164
|
++ }
|
|
1165
|
++
|
|
1166
|
++ _, err = cc.apiClient.WaitForTask(StringVal(task.ID))
|
|
1167
|
++ if err != nil {
|
|
1168
|
++ glog.Errorf("Cascade Cloud Provider: Failed to wait for task to DeleteDisk. Error[%v]", err)
|
|
1169
|
++ return err
|
|
1170
|
++ }
|
|
1171
|
++
|
|
1172
|
++ return nil
|
|
1173
|
++}
|
|
1174
|
++
|
|
1175
|
++// Gets the zone and region for the volume.
|
|
1176
|
++func (cc *CascadeCloud) GetVolumeLabels(diskID string) (map[string]string, error) {
|
|
1177
|
++ disk, err := cc.apiClient.GetDisk(diskID)
|
|
1178
|
++ if err != nil {
|
|
1179
|
++ glog.Errorf("Cascade Cloud Provider: Failed to GetDisk for GetVolumeLabels. Error[%v]", err)
|
|
1180
|
++ return nil, err
|
|
1181
|
++ }
|
|
1182
|
++
|
|
1183
|
++ labels := make(map[string]string)
|
|
1184
|
++ labels[apis.LabelZoneFailureDomain] = StringVal(disk.Zone)
|
|
1185
|
++ labels[apis.LabelZoneRegion] = cc.cfg.Global.Region
|
|
1186
|
++
|
|
1187
|
++ return labels,nil
|
|
1188
|
++}
|
|
1189
|
+\ No newline at end of file
|
|
1190
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/cascade.go cascade-kubernetes/pkg/cloudprovider/providers/cascade/cascade.go
|
|
1191
|
+--- kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/cascade.go 1970-01-01 00:00:00.000000000 +0000
|
|
1192
|
+@@ -0,0 +1,216 @@
|
|
1193
|
++// The use of Cascade cloud provider requires the kubelet, kube-apiserver, and kube-controller-manager to be started
|
|
1194
|
++// with config flag: '--cloud-provider=cascade --cloud-config=[path_to_config_file]'.
|
|
1195
|
++package cascade
|
|
1196
|
++
|
|
1197
|
++import (
|
|
1198
|
++ "errors"
|
|
1199
|
++ "fmt"
|
|
1200
|
++ "io"
|
|
1201
|
++ "os"
|
|
1202
|
++ "github.com/golang/glog"
|
|
1203
|
++ "gopkg.in/gcfg.v1"
|
|
1204
|
++ k8stypes "k8s.io/apimachinery/pkg/types"
|
|
1205
|
++ "k8s.io/kubernetes/pkg/cloudprovider"
|
|
1206
|
++ "k8s.io/kubernetes/pkg/controller"
|
|
1207
|
++ "strings"
|
|
1208
|
++)
|
|
1209
|
++
|
|
1210
|
++const (
|
|
1211
|
++ ProviderName = "cascade"
|
|
1212
|
++ DiskSpecKind = "persistent-disk"
|
|
1213
|
++ MasterPrefix = "master"
|
|
1214
|
++)
|
|
1215
|
++
|
|
1216
|
++// CascadeCloud is an implementation of the cloud provider interface for Cascade Controller.
|
|
1217
|
++type CascadeCloud struct {
|
|
1218
|
++ cfg *CascadeConfig
|
|
1219
|
++ // Authentication client to get token for Cascade API calls
|
|
1220
|
++ authClient *AuthClient
|
|
1221
|
++ // API Client to make Cascade API calls
|
|
1222
|
++ apiClient *Client
|
|
1223
|
++ // local $HOSTNAME
|
|
1224
|
++ localHostname string
|
|
1225
|
++ // hostname from K8S, could be overridden
|
|
1226
|
++ localK8sHostname string
|
|
1227
|
++}
|
|
1228
|
++
|
|
1229
|
++// CascadeCloud represents Cascade cloud provider's configuration.
|
|
1230
|
++type CascadeConfig struct {
|
|
1231
|
++ Global struct {
|
|
1232
|
++ // the Cascade Controller endpoint
|
|
1233
|
++ CloudTarget string `gcfg:"target"`
|
|
1234
|
++ // Cascade Controller tenantName name
|
|
1235
|
++ TenantName string `gcfg:"tenantName"`
|
|
1236
|
++ // Cascade Controller cluster ID
|
|
1237
|
++ ClusterID string `gcfg:"clusterID"`
|
|
1238
|
++ // Authentication server endpoint for Cascade Controller
|
|
1239
|
++ AuthEndpoint string `gcfg:"authEndpoint"`
|
|
1240
|
++ // Lightwave domain name for the node
|
|
1241
|
++ DomainName string `gcfg:"domainName"`
|
|
1242
|
++ // DNS name of the node.
|
|
1243
|
++ DNSName string `gcfg:"dnsName"`
|
|
1244
|
++ // Region in which the cluster is in
|
|
1245
|
++ Region string `gcfg:"region"`
|
|
1246
|
++ // Availability zone in which the cluster is in
|
|
1247
|
++ Zone string `gcfg:"zone"`
|
|
1248
|
++ }
|
|
1249
|
++}
|
|
1250
|
++
|
|
1251
|
++// Disks is interface for manipulation with Cascade Controller Persistent Disks.
|
|
1252
|
++type Disks interface {
|
|
1253
|
++ // AttachDisk attaches given disk to given node. Current node
|
|
1254
|
++ // is used when nodeName is empty string.
|
|
1255
|
++ AttachDisk(diskID string, nodeName k8stypes.NodeName) (string, error)
|
|
1256
|
++
|
|
1257
|
++ // DetachDisk detaches given disk to given node. Current node
|
|
1258
|
++ // is used when nodeName is empty string.
|
|
1259
|
++ DetachDisk(diskID string, nodeName k8stypes.NodeName) error
|
|
1260
|
++
|
|
1261
|
++ // DiskIsAttached checks if a disk is attached to the given node.
|
|
1262
|
++ DiskIsAttached(diskID string, nodeName k8stypes.NodeName) (bool, error)
|
|
1263
|
++
|
|
1264
|
++ // DisksAreAttached is a batch function to check if a list of disks are attached
|
|
1265
|
++ // to the node with the specified NodeName.
|
|
1266
|
++ DisksAreAttached(diskID []string, nodeName k8stypes.NodeName) (map[string]bool, error)
|
|
1267
|
++
|
|
1268
|
++ // CreateDisk creates a new PD with given properties.
|
|
1269
|
++ CreateDisk(volumeOptions *VolumeOptions) (diskID string, err error)
|
|
1270
|
++
|
|
1271
|
++ // DeleteDisk deletes PD.
|
|
1272
|
++ DeleteDisk(diskID string) error
|
|
1273
|
++
|
|
1274
|
++ // Get labels to apply to volume on creation.
|
|
1275
|
++ GetVolumeLabels(diskID string) (map[string]string, error)
|
|
1276
|
++}
|
|
1277
|
++
|
|
1278
|
++// VolumeOptions specifies capacity, tags, name and flavorID for a volume.
|
|
1279
|
++type VolumeOptions struct {
|
|
1280
|
++ CapacityGB int
|
|
1281
|
++ Tags map[string]string
|
|
1282
|
++ Name string
|
|
1283
|
++ Flavor string
|
|
1284
|
++}
|
|
1285
|
++
|
|
1286
|
++func readConfig(config io.Reader) (*CascadeConfig, error) {
|
|
1287
|
++ if config == nil {
|
|
1288
|
++ err := fmt.Errorf("Cascade Cloud Provider: config file is missing. Please restart with " +
|
|
1289
|
++ "--cloud-provider=cascade --cloud-config=[path_to_config_file]")
|
|
1290
|
++ return nil, err
|
|
1291
|
++ }
|
|
1292
|
++
|
|
1293
|
++ var cfg CascadeConfig
|
|
1294
|
++ err := gcfg.ReadInto(&cfg, config)
|
|
1295
|
++ return &cfg, err
|
|
1296
|
++}
|
|
1297
|
++
|
|
1298
|
++func init() {
|
|
1299
|
++ cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) {
|
|
1300
|
++ cfg, err := readConfig(config)
|
|
1301
|
++ if err != nil {
|
|
1302
|
++ glog.Errorf("Cascade Cloud Provider: failed to read in cloud provider config file. Error[%v]", err)
|
|
1303
|
++ return nil, err
|
|
1304
|
++ }
|
|
1305
|
++ return newCascadeCloud(cfg)
|
|
1306
|
++ })
|
|
1307
|
++}
|
|
1308
|
++
|
|
1309
|
++func newCascadeCloud(cfg *CascadeConfig) (*CascadeCloud, error) {
|
|
1310
|
++ if len(cfg.Global.CloudTarget) == 0 {
|
|
1311
|
++ return nil, fmt.Errorf("Cascade Controller endpoint was not specified.")
|
|
1312
|
++ }
|
|
1313
|
++
|
|
1314
|
++ // Get local hostname
|
|
1315
|
++ hostname, err := os.Hostname()
|
|
1316
|
++ if err != nil {
|
|
1317
|
++ glog.Errorf("Cascade Cloud Provider: get hostname failed. Error[%v]", err)
|
|
1318
|
++ return nil, err
|
|
1319
|
++ }
|
|
1320
|
++
|
|
1321
|
++ cc := CascadeCloud{
|
|
1322
|
++ cfg: cfg,
|
|
1323
|
++ localHostname: hostname,
|
|
1324
|
++ localK8sHostname: "",
|
|
1325
|
++ }
|
|
1326
|
++
|
|
1327
|
++ // Instantiate the auth and API clients only on the master nodes. Kubelets running on the workers don't need them as
|
|
1328
|
++ // they are used primarily for making API calls to Cascade.
|
|
1329
|
++ if strings.HasPrefix(hostname, MasterPrefix) {
|
|
1330
|
++ if cc.authClient, err = NewAuthClient(cfg); err != nil {
|
|
1331
|
++ return nil, err
|
|
1332
|
++ }
|
|
1333
|
++
|
|
1334
|
++ if cc.apiClient, err = NewClient(cfg, cc.authClient); err != nil {
|
|
1335
|
++ return nil, err
|
|
1336
|
++ }
|
|
1337
|
++ }
|
|
1338
|
++
|
|
1339
|
++ return &cc, nil
|
|
1340
|
++}
|
|
1341
|
++
|
|
1342
|
++// Initialize passes a Kubernetes clientBuilder interface to the cloud provider
|
|
1343
|
++func (cc *CascadeCloud) Initialize(clientBuilder controller.ControllerClientBuilder) {}
|
|
1344
|
++
|
|
1345
|
++// Instances returns an implementation of Instances for Cascade Controller.
|
|
1346
|
++func (cc *CascadeCloud) Instances() (cloudprovider.Instances, bool) {
|
|
1347
|
++ return cc, true
|
|
1348
|
++}
|
|
1349
|
++
|
|
1350
|
++// List is an implementation of Instances.List.
|
|
1351
|
++func (cc *CascadeCloud) List(filter string) ([]k8stypes.NodeName, error) {
|
|
1352
|
++ return nil, errors.New("unimplemented")
|
|
1353
|
++}
|
|
1354
|
++
|
|
1355
|
++func (cc *CascadeCloud) Clusters() (cloudprovider.Clusters, bool) {
|
|
1356
|
++ return nil, true
|
|
1357
|
++}
|
|
1358
|
++
|
|
1359
|
++// ProviderName returns the cloud provider ID.
|
|
1360
|
++func (cc *CascadeCloud) ProviderName() string {
|
|
1361
|
++ return ProviderName
|
|
1362
|
++}
|
|
1363
|
++
|
|
1364
|
++// LoadBalancer returns an implementation of LoadBalancer for Cascade Controller.
|
|
1365
|
++func (cc *CascadeCloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
|
|
1366
|
++ return cc, true
|
|
1367
|
++}
|
|
1368
|
++
|
|
1369
|
++// Zones returns an implementation of Zones for Cascade Controller.
|
|
1370
|
++func (cc *CascadeCloud) Zones() (cloudprovider.Zones, bool) {
|
|
1371
|
++ return cc, true
|
|
1372
|
++}
|
|
1373
|
++
|
|
1374
|
++func (cc *CascadeCloud) GetZone() (cloudprovider.Zone, error) {
|
|
1375
|
++ return cloudprovider.Zone{
|
|
1376
|
++ Region: cc.cfg.Global.Region,
|
|
1377
|
++ FailureDomain: cc.cfg.Global.Zone,
|
|
1378
|
++ }, nil
|
|
1379
|
++}
|
|
1380
|
++
|
|
1381
|
++// GetZoneByProviderID implements Zones.GetZoneByProviderID
|
|
1382
|
++// This is particularly useful in external cloud providers where the kubelet
|
|
1383
|
++// does not initialize node data.
|
|
1384
|
++func (cc *CascadeCloud) GetZoneByProviderID(providerID string) (cloudprovider.Zone, error) {
|
|
1385
|
++ return cloudprovider.Zone{}, errors.New("unimplemented")
|
|
1386
|
++}
|
|
1387
|
++
|
|
1388
|
++// GetZoneByNodeName implements Zones.GetZoneByNodeName
|
|
1389
|
++// This is particularly useful in external cloud providers where the kubelet
|
|
1390
|
++// does not initialize node data.
|
|
1391
|
++func (cc *CascadeCloud) GetZoneByNodeName(nodeName k8stypes.NodeName) (cloudprovider.Zone, error) {
|
|
1392
|
++ return cloudprovider.Zone{}, errors.New("unimeplemented")
|
|
1393
|
++}
|
|
1394
|
++
|
|
1395
|
++// Routes returns a false since the interface is not supported for Cascade controller.
|
|
1396
|
++func (cc *CascadeCloud) Routes() (cloudprovider.Routes, bool) {
|
|
1397
|
++ return nil, false
|
|
1398
|
++}
|
|
1399
|
++
|
|
1400
|
++// ScrubDNS filters DNS settings for pods.
|
|
1401
|
++func (cc *CascadeCloud) ScrubDNS(nameservers, searches []string) (nsOut, srchOut []string) {
|
|
1402
|
++ return nameservers, searches
|
|
1403
|
++}
|
|
1404
|
++
|
|
1405
|
++// HasClusterID returns true if the cluster has a clusterID
|
|
1406
|
++func (cc *CascadeCloud) HasClusterID() bool {
|
|
1407
|
++ return true
|
|
1408
|
++}
|
|
1409
|
+\ No newline at end of file
|
|
1410
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/cascade_instances.go cascade-kubernetes/pkg/cloudprovider/providers/cascade/cascade_instances.go
|
|
1411
|
+--- kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/cascade_instances.go 1970-01-01 00:00:00.000000000 +0000
|
|
1412
|
+@@ -0,0 +1,90 @@
|
|
1413
|
++package cascade
|
|
1414
|
++
|
|
1415
|
++import (
|
|
1416
|
++ "k8s.io/api/core/v1"
|
|
1417
|
++ k8stypes "k8s.io/apimachinery/pkg/types"
|
|
1418
|
++ "errors"
|
|
1419
|
++ "strings"
|
|
1420
|
++)
|
|
1421
|
++
|
|
1422
|
++// NodeAddresses is an implementation of Instances.NodeAddresses. In the future, private IP address, external IP, etc.
|
|
1423
|
++// will be added based on need.
|
|
1424
|
++func (cc *CascadeCloud) NodeAddresses(nodeName k8stypes.NodeName) ([]v1.NodeAddress, error) {
|
|
1425
|
++ addresses := []v1.NodeAddress{}
|
|
1426
|
++ addresses = append(addresses, v1.NodeAddress{Type: v1.NodeInternalDNS, Address: cc.cfg.Global.DNSName})
|
|
1427
|
++ return addresses, nil
|
|
1428
|
++}
|
|
1429
|
++
|
|
1430
|
++// NodeAddressesByProviderID returns the node addresses of an instances with the specified unique providerID
|
|
1431
|
++// This method will not be called from the node that is requesting this ID. i.e. metadata service
|
|
1432
|
++// and other local methods cannot be used here
|
|
1433
|
++func (cc *CascadeCloud) NodeAddressesByProviderID(providerID string) ([]v1.NodeAddress, error) {
|
|
1434
|
++ // Get the name of the VM using the ID and generate the DNS name based on the VM name.
|
|
1435
|
++ vm, err := cc.apiClient.GetVM(providerID)
|
|
1436
|
++ if err != nil {
|
|
1437
|
++ return nil, err
|
|
1438
|
++ }
|
|
1439
|
++ // Get the DNS name for the master VM and replace the VM name portion with the requested VM name.
|
|
1440
|
++ dnsNameParts := strings.SplitN(cc.cfg.Global.DNSName, ".", 2)
|
|
1441
|
++ if len(dnsNameParts) != 2 {
|
|
1442
|
++ return nil, errors.New("Cascade cloud provider: Invalid DNS name specified in the configuation. " +
|
|
1443
|
++ "Cannot get NodeAddressByProviderID.")
|
|
1444
|
++ }
|
|
1445
|
++ dnsAddress := StringVal(vm.Name) + dnsNameParts[1]
|
|
1446
|
++ addresses := []v1.NodeAddress{}
|
|
1447
|
++ addresses = append(addresses, v1.NodeAddress{Type: v1.NodeInternalDNS, Address: dnsAddress})
|
|
1448
|
++ return addresses, nil
|
|
1449
|
++}
|
|
1450
|
++
|
|
1451
|
++func (cc *CascadeCloud) AddSSHKeyToAllInstances(user string, keyData []byte) error {
|
|
1452
|
++ return errors.New("unimplemented")
|
|
1453
|
++}
|
|
1454
|
++
|
|
1455
|
++// Current node name returns node name based on host name. For Cascade Kubernetes nodes, we will use host name as the
|
|
1456
|
++// node name.
|
|
1457
|
++func (cc *CascadeCloud) CurrentNodeName(hostname string) (k8stypes.NodeName, error) {
|
|
1458
|
++ cc.localK8sHostname = hostname
|
|
1459
|
++ return k8stypes.NodeName(hostname), nil
|
|
1460
|
++}
|
|
1461
|
++
|
|
1462
|
++// ExternalID returns the cloud provider ID of the specified instance (deprecated).
|
|
1463
|
++// Note: We do not call Cascade Controller here to check if the instance is alive or not because that requires the
|
|
1464
|
++// worker nodes to also login to Cascade Controller. That check is used by Kubernetes to proactively remove nodes that
|
|
1465
|
++// the cloud provider believes is no longer available. Even otherwise, Kubernetes will remove those nodes eventually.
|
|
1466
|
++// So we are not losing much by not doing that check.
|
|
1467
|
++func (cc *CascadeCloud) ExternalID(nodeName k8stypes.NodeName) (string, error) {
|
|
1468
|
++ return getInstanceIDFromNodeName(nodeName)
|
|
1469
|
++}
|
|
1470
|
++
|
|
1471
|
++// InstanceExistsByProviderID returns true if the instance with the given provider id still exists and is running.
|
|
1472
|
++// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
|
|
1473
|
++func (cc *CascadeCloud) InstanceExistsByProviderID(providerID string) (bool, error) {
|
|
1474
|
++ return false, errors.New("unimplemented")
|
|
1475
|
++}
|
|
1476
|
++
|
|
1477
|
++// InstanceID returns the cloud provider ID of the specified instance.
|
|
1478
|
++func (cc *CascadeCloud) InstanceID(nodeName k8stypes.NodeName) (string, error) {
|
|
1479
|
++ return getInstanceIDFromNodeName(nodeName)
|
|
1480
|
++}
|
|
1481
|
++
|
|
1482
|
++// This gets the Cascade VM ID from the Kubernetes node name.
|
|
1483
|
++func getInstanceIDFromNodeName(nodeName k8stypes.NodeName) (string, error) {
|
|
1484
|
++ // nodeName is of the format master-instance-id or worker-instance-id. To compute the instance ID, we need to just
|
|
1485
|
++ // get the portion after master- or worker-. That is what we do below.
|
|
1486
|
++ nodeParts := strings.SplitN(string(nodeName), "-", 2)
|
|
1487
|
++ if len(nodeParts) != 2 {
|
|
1488
|
++ return "", errors.New("Cascade cloud provider: Invalid node name. Cannot fetch instance ID.")
|
|
1489
|
++ }
|
|
1490
|
++ return nodeParts[1], nil
|
|
1491
|
++}
|
|
1492
|
++
|
|
1493
|
++// InstanceTypeByProviderID returns the cloudprovider instance type of the node with the specified unique providerID
|
|
1494
|
++// This method will not be called from the node that is requesting this ID. i.e. metadata service
|
|
1495
|
++// and other local methods cannot be used here
|
|
1496
|
++func (cc *CascadeCloud) InstanceTypeByProviderID(providerID string) (string, error) {
|
|
1497
|
++ return "", errors.New("unimplemented")
|
|
1498
|
++}
|
|
1499
|
++
|
|
1500
|
++func (cc *CascadeCloud) InstanceType(nodeName k8stypes.NodeName) (string, error) {
|
|
1501
|
++ return "", nil
|
|
1502
|
++}
|
|
1503
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/cascade_loadbalancer.go cascade-kubernetes/pkg/cloudprovider/providers/cascade/cascade_loadbalancer.go
|
|
1504
|
+--- kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/cascade_loadbalancer.go 1970-01-01 00:00:00.000000000 +0000
|
|
1505
|
+@@ -0,0 +1,283 @@
|
|
1506
|
++package cascade
|
|
1507
|
++
|
|
1508
|
++import (
|
|
1509
|
++ "fmt"
|
|
1510
|
++ "github.com/golang/glog"
|
|
1511
|
++ "k8s.io/api/core/v1"
|
|
1512
|
++ "k8s.io/kubernetes/pkg/api/v1/service"
|
|
1513
|
++ "k8s.io/kubernetes/pkg/cloudprovider"
|
|
1514
|
++ "k8s.io/apimachinery/pkg/types"
|
|
1515
|
++)
|
|
1516
|
++
|
|
1517
|
++const TCP_PROTOCOL = "TCP"
|
|
1518
|
++
|
|
1519
|
++const HTTP_PROTOCOL = "HTTP"
|
|
1520
|
++
|
|
1521
|
++// EnsureLoadBalancer creates or updates a Cascade load balancer
|
|
1522
|
++func (cc *CascadeCloud) EnsureLoadBalancer(clusterName string, k8sService *v1.Service, nodes []*v1.Node) (*v1.LoadBalancerStatus, error) {
|
|
1523
|
++ logger := newLoadBalancerLogger(clusterName, k8sService, "EnsureLoadBalancer")
|
|
1524
|
++
|
|
1525
|
++ loadBalancerName := cloudprovider.GetLoadBalancerName(k8sService)
|
|
1526
|
++ logger.Infof("Load balancer name: %s", loadBalancerName)
|
|
1527
|
++
|
|
1528
|
++ // Sanity checks
|
|
1529
|
++ if k8sService.Spec.SessionAffinity != v1.ServiceAffinityNone {
|
|
1530
|
++ logger.Errorf("Unsupported load balancer session affinity: %+v", k8sService.Spec.SessionAffinity)
|
|
1531
|
++ return nil, fmt.Errorf("Unsupported load balancer session affinity: %+v", k8sService.Spec.SessionAffinity)
|
|
1532
|
++ }
|
|
1533
|
++
|
|
1534
|
++ if len(k8sService.Spec.Ports) == 0 {
|
|
1535
|
++ logger.Errorf("No port mapping is specified")
|
|
1536
|
++ return nil, fmt.Errorf("No port mapping is specified")
|
|
1537
|
++ }
|
|
1538
|
++
|
|
1539
|
++ // Create load balancer port maps
|
|
1540
|
++ portMaps := []*LoadBalancerPortMap{}
|
|
1541
|
++ for _, port := range k8sService.Spec.Ports {
|
|
1542
|
++ if port.Protocol != v1.ProtocolTCP {
|
|
1543
|
++ logger.Warningf("Ignoring port that does not use TCP protocol: %+v", port)
|
|
1544
|
++ continue
|
|
1545
|
++ }
|
|
1546
|
++
|
|
1547
|
++ if port.NodePort == 0 {
|
|
1548
|
++ logger.Warningf("Ignoring port without node port defined: %+v", port)
|
|
1549
|
++ continue
|
|
1550
|
++ }
|
|
1551
|
++
|
|
1552
|
++ // TODO: For now we only support SSL pass through. All port mappings are using TCP protocol.
|
|
1553
|
++ // Also note that we allow all external traffic to access the ports.
|
|
1554
|
++ portMap := &LoadBalancerPortMap{
|
|
1555
|
++ InstancePort: Int64Ptr(int64(port.NodePort)),
|
|
1556
|
++ InstanceProtocol: StringPtr(TCP_PROTOCOL),
|
|
1557
|
++ LoadBalancerPort: Int64Ptr(int64(port.Port)),
|
|
1558
|
++ LoadBalancerProtocol: StringPtr(TCP_PROTOCOL),
|
|
1559
|
++ }
|
|
1560
|
++ portMaps = append(portMaps, portMap)
|
|
1561
|
++ }
|
|
1562
|
++
|
|
1563
|
++ // Create load balancer health check
|
|
1564
|
++ healthCheck := &LoadBalancerHealthCheck{
|
|
1565
|
++ HealthyThreshold: 5,
|
|
1566
|
++ IntervalInSeconds: 10,
|
|
1567
|
++ }
|
|
1568
|
++ if healthCheckPath, healthCheckNodePort := service.GetServiceHealthCheckPathPort(k8sService); healthCheckPath != "" {
|
|
1569
|
++ logger.Infof("HTTP health checks on: %s:%d", healthCheckPath, healthCheckNodePort)
|
|
1570
|
++ healthCheck.Path = StringPtr(healthCheckPath)
|
|
1571
|
++ healthCheck.Port = Int64Ptr(int64(healthCheckNodePort))
|
|
1572
|
++ healthCheck.Protocol = StringPtr(HTTP_PROTOCOL)
|
|
1573
|
++ } else {
|
|
1574
|
++ logger.Infof("TCP health check on port: %d", Int64Val(portMaps[0].InstancePort))
|
|
1575
|
++ healthCheck.Port = portMaps[0].InstancePort
|
|
1576
|
++ healthCheck.Protocol = StringPtr(TCP_PROTOCOL)
|
|
1577
|
++ }
|
|
1578
|
++
|
|
1579
|
++ // Create load balancer
|
|
1580
|
++ createSpec := &LoadBalancerCreateSpec{
|
|
1581
|
++ Name: StringPtr(loadBalancerName),
|
|
1582
|
++ Type: StringPtr("PUBLIC"),
|
|
1583
|
++ PortMaps: portMaps,
|
|
1584
|
++ HealthCheck: healthCheck,
|
|
1585
|
++ }
|
|
1586
|
++ logger.Infof("Load balancer create spec: %+v", *createSpec)
|
|
1587
|
++
|
|
1588
|
++ task, err := cc.apiClient.CreateOrUpdateLoadBalancer(createSpec)
|
|
1589
|
++ if err != nil {
|
|
1590
|
++ logger.Errorf("Failed to create or update load balancer. Error: [%v]", err)
|
|
1591
|
++ return nil, err
|
|
1592
|
++ }
|
|
1593
|
++
|
|
1594
|
++ _, err = cc.apiClient.WaitForTask(StringVal(task.ID))
|
|
1595
|
++ if err != nil {
|
|
1596
|
++ logger.Errorf("Failed to poll task status of creating or updating load balancer. Error: [%v]", err)
|
|
1597
|
++ return nil, err
|
|
1598
|
++ }
|
|
1599
|
++
|
|
1600
|
++ // Apply VM update to load balancer
|
|
1601
|
++ err = cc.updateLoadBalancerVMs(nodes, loadBalancerName, logger)
|
|
1602
|
++ if err != nil {
|
|
1603
|
++ // The private function already did logging. No need to log again.
|
|
1604
|
++ return nil, err
|
|
1605
|
++ }
|
|
1606
|
++
|
|
1607
|
++ // Get load balancer
|
|
1608
|
++ loadBalancer, err := cc.apiClient.GetLoadBalancer(StringPtr(loadBalancerName))
|
|
1609
|
++ if err != nil {
|
|
1610
|
++ glog.Errorf("Failed to get load balancer. Error: [%v]", err)
|
|
1611
|
++ return nil, err
|
|
1612
|
++ }
|
|
1613
|
++
|
|
1614
|
++ return toLoadBalancerStatus(loadBalancer), nil
|
|
1615
|
++}
|
|
1616
|
++
|
|
1617
|
++// GetLoadBalancer returns the information about a Cascade load balancer
|
|
1618
|
++func (cc *CascadeCloud) GetLoadBalancer(clusterName string, k8sService *v1.Service) (*v1.LoadBalancerStatus, bool, error) {
|
|
1619
|
++ logger := newLoadBalancerLogger(clusterName, k8sService, "GetLoadBalancer")
|
|
1620
|
++
|
|
1621
|
++ loadBalancerName := cloudprovider.GetLoadBalancerName(k8sService)
|
|
1622
|
++ logger.Infof("Load balancer name: %s", loadBalancerName)
|
|
1623
|
++
|
|
1624
|
++ // Get load balancer
|
|
1625
|
++ loadBalancer, err := cc.apiClient.GetLoadBalancer(StringPtr(loadBalancerName))
|
|
1626
|
++ if err != nil {
|
|
1627
|
++ logger.Errorf("Failed to get load balancer. Error: [%v]", err)
|
|
1628
|
++ // Do not return error here because we want the caller of this function to determine
|
|
1629
|
++ // what to do with the not-found situation.
|
|
1630
|
++ switch err.(type) {
|
|
1631
|
++ case APIError:
|
|
1632
|
++ if err.(APIError).ErrorCode == NotFoundError {
|
|
1633
|
++ return nil, false, nil
|
|
1634
|
++ }
|
|
1635
|
++ }
|
|
1636
|
++ return nil, false, err
|
|
1637
|
++ }
|
|
1638
|
++
|
|
1639
|
++ return toLoadBalancerStatus(loadBalancer), true, nil
|
|
1640
|
++}
|
|
1641
|
++
|
|
1642
|
++// UpdateLoadBalancer updates the node information of a Cascade load balancer
|
|
1643
|
++func (cc *CascadeCloud) UpdateLoadBalancer(clusterName string, k8sService *v1.Service, nodes []*v1.Node) error {
|
|
1644
|
++ logger := newLoadBalancerLogger(clusterName, k8sService, "UpdateLoadBalancer")
|
|
1645
|
++
|
|
1646
|
++ loadBalancerName := cloudprovider.GetLoadBalancerName(k8sService)
|
|
1647
|
++ logger.Infof("Load balancer name: %s", loadBalancerName)
|
|
1648
|
++
|
|
1649
|
++ err := cc.updateLoadBalancerVMs(nodes, loadBalancerName, logger)
|
|
1650
|
++ if err != nil {
|
|
1651
|
++ // The private function already did logging. No need to log again.
|
|
1652
|
++ return err
|
|
1653
|
++ }
|
|
1654
|
++
|
|
1655
|
++ return nil
|
|
1656
|
++}
|
|
1657
|
++
|
|
1658
|
++// EnsureLoadBalancerDeleted deletes a Cascade load balancer
|
|
1659
|
++func (cc *CascadeCloud) EnsureLoadBalancerDeleted(clusterName string, k8sService *v1.Service) error {
|
|
1660
|
++ logger := newLoadBalancerLogger(clusterName, k8sService, "EnsureLoadBalancerDeleted")
|
|
1661
|
++
|
|
1662
|
++ loadBalancerName := cloudprovider.GetLoadBalancerName(k8sService)
|
|
1663
|
++ logger.Infof("Load balancer name: %s", loadBalancerName)
|
|
1664
|
++
|
|
1665
|
++ task, err := cc.apiClient.DeleteLoadBalancer(StringPtr(loadBalancerName))
|
|
1666
|
++ if err != nil {
|
|
1667
|
++ logger.Errorf("Failed to delete load balancer. Error: [%v]", err)
|
|
1668
|
++ // If we get a NotFound error, we assume that the load balancer is already deleted. So we don't return an error
|
|
1669
|
++ // here.
|
|
1670
|
++ switch err.(type) {
|
|
1671
|
++ case APIError:
|
|
1672
|
++ if err.(APIError).ErrorCode == NotFoundError {
|
|
1673
|
++ return nil
|
|
1674
|
++ }
|
|
1675
|
++ }
|
|
1676
|
++ return err
|
|
1677
|
++ }
|
|
1678
|
++
|
|
1679
|
++ _, err = cc.apiClient.WaitForTask(StringVal(task.ID))
|
|
1680
|
++ if err != nil {
|
|
1681
|
++ logger.Errorf("Failed to poll task status of deleting load balancer. Error: [%v]", err)
|
|
1682
|
++ return err
|
|
1683
|
++ }
|
|
1684
|
++
|
|
1685
|
++ return nil
|
|
1686
|
++}
|
|
1687
|
++
|
|
1688
|
++func (cc *CascadeCloud) updateLoadBalancerVMs(
|
|
1689
|
++ nodes []*v1.Node, loadBalancerName string, logger *loadBalancerLogger) error {
|
|
1690
|
++
|
|
1691
|
++ // Apply VM update to the load balancer
|
|
1692
|
++ loadBalancerVMs := make([]*LoadBalancerVM, 0)
|
|
1693
|
++
|
|
1694
|
++ for _, node := range(nodes) {
|
|
1695
|
++ // If the node does not have a name, we cannot derive its instance ID. Therefore we skip this node.
|
|
1696
|
++ if len(node.Name) == 0 {
|
|
1697
|
++ logger.Warningf("Node %s does not have a name. Skip updating this VM for load balancer", node.UID)
|
|
1698
|
++ continue
|
|
1699
|
++ }
|
|
1700
|
++
|
|
1701
|
++ // If we cannot get the instance ID, something is wrong on the Cascade Controller side.
|
|
1702
|
++ // However, we should tolerate such failure and continue the load balancer VM update
|
|
1703
|
++ // by skipping this VM.
|
|
1704
|
++ instanceID, err := cc.InstanceID(types.NodeName(node.Name))
|
|
1705
|
++ if err != nil {
|
|
1706
|
++ logger.Warningf("Unable to get instance ID for node %s, skip updating this VM for load balancer. Error [%v]", node.Name, err)
|
|
1707
|
++ continue
|
|
1708
|
++ }
|
|
1709
|
++
|
|
1710
|
++ loadBalancerVMs = append(loadBalancerVMs, &LoadBalancerVM{
|
|
1711
|
++ ID: StringPtr(instanceID),
|
|
1712
|
++ })
|
|
1713
|
++ }
|
|
1714
|
++
|
|
1715
|
++ if len(loadBalancerVMs) == 0 {
|
|
1716
|
++ logger.Infof("No nodes to be added to the load balancer. Skip updating load balancer VMs")
|
|
1717
|
++ return nil
|
|
1718
|
++ }
|
|
1719
|
++
|
|
1720
|
++ vmUpdate := &LoadBalancerVMUpdate{
|
|
1721
|
++ VMIds: loadBalancerVMs,
|
|
1722
|
++ }
|
|
1723
|
++ logger.Infof("Load balancer VM update spec: %+v", vmUpdate.VMIds)
|
|
1724
|
++
|
|
1725
|
++ task, err := cc.apiClient.ApplyVMsToLoadBalancer(StringPtr(loadBalancerName), vmUpdate)
|
|
1726
|
++ if err != nil {
|
|
1727
|
++ logger.Errorf("Failed to update load balancer VMs. Error: [%v]", err)
|
|
1728
|
++ return err
|
|
1729
|
++ }
|
|
1730
|
++
|
|
1731
|
++ _, err = cc.apiClient.WaitForTask(StringVal(task.ID))
|
|
1732
|
++ if err != nil {
|
|
1733
|
++ logger.Errorf("Failed to poll task status of updating load balancer VMs. Error: [%v]", err)
|
|
1734
|
++ return err
|
|
1735
|
++ }
|
|
1736
|
++
|
|
1737
|
++ return nil
|
|
1738
|
++}
|
|
1739
|
++
|
|
1740
|
++func toLoadBalancerStatus(lb *LoadBalancer) *v1.LoadBalancerStatus {
|
|
1741
|
++ var endpoint string
|
|
1742
|
++ if lb != nil && lb.Endpoint != nil {
|
|
1743
|
++ endpoint = StringVal(lb.Endpoint)
|
|
1744
|
++ }
|
|
1745
|
++
|
|
1746
|
++ return &v1.LoadBalancerStatus{
|
|
1747
|
++ Ingress: []v1.LoadBalancerIngress{
|
|
1748
|
++ {
|
|
1749
|
++ Hostname: endpoint,
|
|
1750
|
++ },
|
|
1751
|
++ },
|
|
1752
|
++ }
|
|
1753
|
++}
|
|
1754
|
++
|
|
1755
|
++type loadBalancerLogger struct {
|
|
1756
|
++ clusterName string
|
|
1757
|
++ k8sService *v1.Service
|
|
1758
|
++ callingFunc string
|
|
1759
|
++}
|
|
1760
|
++
|
|
1761
|
++func newLoadBalancerLogger(clusterName string, k8sService *v1.Service, callingFunc string) *loadBalancerLogger {
|
|
1762
|
++ return &loadBalancerLogger{
|
|
1763
|
++ clusterName: clusterName,
|
|
1764
|
++ k8sService: k8sService,
|
|
1765
|
++ callingFunc: callingFunc,
|
|
1766
|
++ }
|
|
1767
|
++}
|
|
1768
|
++
|
|
1769
|
++func (l *loadBalancerLogger) getLogMsg(
|
|
1770
|
++ msgTemplate string, args ...interface{}) string {
|
|
1771
|
++
|
|
1772
|
++ errorMsg := fmt.Sprintf("Cascade Cloud Provider::%s::Cluster [%s] Service [%s]: %s",
|
|
1773
|
++ l.callingFunc, l.clusterName, l.k8sService.Name,
|
|
1774
|
++ msgTemplate)
|
|
1775
|
++ return fmt.Sprintf(errorMsg, args)
|
|
1776
|
++}
|
|
1777
|
++
|
|
1778
|
++func (l *loadBalancerLogger) Errorf(msgTemplate string, args ...interface{}) {
|
|
1779
|
++ glog.Errorln(l.getLogMsg(msgTemplate, args))
|
|
1780
|
++}
|
|
1781
|
++
|
|
1782
|
++func (l *loadBalancerLogger) Warningf(msgTemplate string, args ...interface{}) {
|
|
1783
|
++ glog.Warningln(l.getLogMsg(msgTemplate, args))
|
|
1784
|
++}
|
|
1785
|
++
|
|
1786
|
++func (l *loadBalancerLogger) Infof(msgTemplate string, args ...interface{}) {
|
|
1787
|
++ glog.Infoln(l.getLogMsg(msgTemplate, args))
|
|
1788
|
++}
|
|
1789
|
+\ No newline at end of file
|
|
1790
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/client.go cascade-kubernetes/pkg/cloudprovider/providers/cascade/client.go
|
|
1791
|
+--- kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/client.go 1970-01-01 00:00:00.000000000 +0000
|
|
1792
|
+@@ -0,0 +1,382 @@
|
|
1793
|
++package cascade
|
|
1794
|
++
|
|
1795
|
++import (
|
|
1796
|
++ "crypto/tls"
|
|
1797
|
++ "crypto/x509"
|
|
1798
|
++ "encoding/json"
|
|
1799
|
++ "fmt"
|
|
1800
|
++ "net/http"
|
|
1801
|
++ "strings"
|
|
1802
|
++ "time"
|
|
1803
|
++ "bytes"
|
|
1804
|
++ "github.com/golang/glog"
|
|
1805
|
++)
|
|
1806
|
++
|
|
1807
|
++// Represents stateless context needed to call Cascade APIs.
|
|
1808
|
++// Note that we are implementing the Cascade APIs manually instead of using the swagger generated code
|
|
1809
|
++// because swagger uses a different version of openapi library than kubernetes. It is difficult to
|
|
1810
|
++// address the version conflict to make it compile.
|
|
1811
|
++type Client struct {
|
|
1812
|
++ cfg *ClientConfig
|
|
1813
|
++ options ClientOptions
|
|
1814
|
++ restClient *restClient
|
|
1815
|
++}
|
|
1816
|
++
|
|
1817
|
++type ClientConfig struct {
|
|
1818
|
++ tenantName string
|
|
1819
|
++ clusterID string
|
|
1820
|
++ region string
|
|
1821
|
++ endpoint string
|
|
1822
|
++}
|
|
1823
|
++
|
|
1824
|
++// Represents Tokens
|
|
1825
|
++type TokenOptions struct {
|
|
1826
|
++ AccessToken string `json:"access_token"`
|
|
1827
|
++ ExpiresIn int `json:"expires_in"`
|
|
1828
|
++ RefreshToken string `json:"refresh_token,omitempty"`
|
|
1829
|
++ IDToken string `json:"id_token"`
|
|
1830
|
++ TokenType string `json:"token_type"`
|
|
1831
|
++}
|
|
1832
|
++
|
|
1833
|
++type TokenCallback func(string)
|
|
1834
|
++
|
|
1835
|
++// Options for Client
|
|
1836
|
++type ClientOptions struct {
|
|
1837
|
++ // When using the Tasks.Wait APIs, defines the duration of how long
|
|
1838
|
++ // we should continue to poll the server. Default is 30 minutes.
|
|
1839
|
++ // TasksAPI.WaitTimeout() can be used to specify timeout on
|
|
1840
|
++ // individual calls.
|
|
1841
|
++ TaskPollTimeout time.Duration
|
|
1842
|
++
|
|
1843
|
++ // Whether or not to ignore any TLS errors when talking to Cascade,
|
|
1844
|
++ // false by default.
|
|
1845
|
++ IgnoreCertificate bool
|
|
1846
|
++
|
|
1847
|
++ // List of root CA's to use for server validation
|
|
1848
|
++ // nil by default.
|
|
1849
|
++ RootCAs *x509.CertPool
|
|
1850
|
++
|
|
1851
|
++ // For tasks APIs, defines the delay between each polling attempt.
|
|
1852
|
++ // Default is 100 milliseconds.
|
|
1853
|
++ TaskPollDelay time.Duration
|
|
1854
|
++
|
|
1855
|
++ // For tasks APIs, defines the number of retries to make in the event
|
|
1856
|
++ // of an error. Default is 3.
|
|
1857
|
++ TaskRetryCount int
|
|
1858
|
++
|
|
1859
|
++ // Tokens for user authentication. Default is empty.
|
|
1860
|
++ TokenOptions *TokenOptions
|
|
1861
|
++}
|
|
1862
|
++
|
|
1863
|
++// Creates a new Cascade client which can be used to make API calls to Cascade.
|
|
1864
|
++func NewClient(cfg *CascadeConfig, authClient *AuthClient) (c *Client, err error) {
|
|
1865
|
++ tokenOptions, err := authClient.GetTokensByMachineAccount()
|
|
1866
|
++ if err != nil {
|
|
1867
|
++ glog.Errorf("Cascade Cloud Provider: Failed to create new client due to error: %+v", err)
|
|
1868
|
++ return
|
|
1869
|
++ }
|
|
1870
|
++
|
|
1871
|
++ options := &ClientOptions{
|
|
1872
|
++ TaskPollTimeout: 30 * time.Minute,
|
|
1873
|
++ TaskPollDelay: 100 * time.Millisecond,
|
|
1874
|
++ TaskRetryCount: 3,
|
|
1875
|
++ TokenOptions: tokenOptions,
|
|
1876
|
++ IgnoreCertificate: false,
|
|
1877
|
++ RootCAs: nil,
|
|
1878
|
++ }
|
|
1879
|
++
|
|
1880
|
++ tr := &http.Transport{
|
|
1881
|
++ TLSClientConfig: &tls.Config{
|
|
1882
|
++ InsecureSkipVerify: options.IgnoreCertificate,
|
|
1883
|
++ RootCAs: options.RootCAs},
|
|
1884
|
++ }
|
|
1885
|
++
|
|
1886
|
++ tokenCallback := func(newToken string) {
|
|
1887
|
++ c.options.TokenOptions.AccessToken = newToken
|
|
1888
|
++ }
|
|
1889
|
++
|
|
1890
|
++ restClient := &restClient{
|
|
1891
|
++ authClient: authClient,
|
|
1892
|
++ httpClient: &http.Client{Transport: tr},
|
|
1893
|
++ UpdateAccessTokenCallback: tokenCallback,
|
|
1894
|
++ }
|
|
1895
|
++
|
|
1896
|
++ clientConfig := &ClientConfig {
|
|
1897
|
++ tenantName: cfg.Global.TenantName,
|
|
1898
|
++ clusterID: cfg.Global.ClusterID,
|
|
1899
|
++ region: cfg.Global.Region,
|
|
1900
|
++ endpoint: strings.TrimRight(cfg.Global.CloudTarget, "/"),
|
|
1901
|
++ }
|
|
1902
|
++
|
|
1903
|
++ c = &Client{
|
|
1904
|
++ cfg: clientConfig,
|
|
1905
|
++ restClient: restClient,
|
|
1906
|
++ // Ensure a copy of options is made, rather than using a pointer
|
|
1907
|
++ // which may change out from underneath if misused by the caller.
|
|
1908
|
++ options: *options,
|
|
1909
|
++ }
|
|
1910
|
++
|
|
1911
|
++ return
|
|
1912
|
++}
|
|
1913
|
++
|
|
1914
|
++// Gets VM with the specified ID.
|
|
1915
|
++func (api *Client) GetVM(vmID string) (vm *VM, err error) {
|
|
1916
|
++ uri := fmt.Sprintf("%s/v1/tenants/%s/clusters/%s/vms/%s", api.cfg.endpoint, api.cfg.tenantName,
|
|
1917
|
++ api.cfg.clusterID, vmID)
|
|
1918
|
++ res, err := api.restClient.Get(uri, api.options.TokenOptions)
|
|
1919
|
++ if err != nil {
|
|
1920
|
++ return
|
|
1921
|
++ }
|
|
1922
|
++ defer res.Body.Close()
|
|
1923
|
++ res, err = getError(res)
|
|
1924
|
++ if err != nil {
|
|
1925
|
++ return
|
|
1926
|
++ }
|
|
1927
|
++ vm = &VM{}
|
|
1928
|
++ err = json.NewDecoder(res.Body).Decode(vm)
|
|
1929
|
++ return
|
|
1930
|
++}
|
|
1931
|
++
|
|
1932
|
++// Gets disk with the specified ID.
|
|
1933
|
++func (api *Client) GetDisk(diskID string) (disk *PersistentDisk, err error) {
|
|
1934
|
++ uri := fmt.Sprintf("%s/v1/tenants/%s/clusters/%s/disks/%s", api.cfg.endpoint, api.cfg.tenantName,
|
|
1935
|
++ api.cfg.clusterID, diskID)
|
|
1936
|
++ res, err := api.restClient.Get(uri, api.options.TokenOptions)
|
|
1937
|
++ if err != nil {
|
|
1938
|
++ return
|
|
1939
|
++ }
|
|
1940
|
++ defer res.Body.Close()
|
|
1941
|
++ res, err = getError(res)
|
|
1942
|
++ if err != nil {
|
|
1943
|
++ return
|
|
1944
|
++ }
|
|
1945
|
++ disk = &PersistentDisk{}
|
|
1946
|
++ err = json.NewDecoder(res.Body).Decode(disk)
|
|
1947
|
++ return
|
|
1948
|
++}
|
|
1949
|
++
|
|
1950
|
++// Creates a disk under the cluster.
|
|
1951
|
++func (api *Client) CreateDisk(spec *DiskCreateSpec) (task *Task, err error) {
|
|
1952
|
++ body, err := json.Marshal(spec)
|
|
1953
|
++ if err != nil {
|
|
1954
|
++ return
|
|
1955
|
++ }
|
|
1956
|
++ uri := fmt.Sprintf("%s/v1/tenants/%s/clusters/%s/disks", api.cfg.endpoint, api.cfg.tenantName,
|
|
1957
|
++ api.cfg.clusterID)
|
|
1958
|
++ res, err := api.restClient.Post(uri, "application/json", bytes.NewReader(body), api.options.TokenOptions)
|
|
1959
|
++ if err != nil {
|
|
1960
|
++ return
|
|
1961
|
++ }
|
|
1962
|
++ defer res.Body.Close()
|
|
1963
|
++ task, err = getTask(getError(res))
|
|
1964
|
++ return
|
|
1965
|
++}
|
|
1966
|
++
|
|
1967
|
++// Deletes a disk with the specified ID.
|
|
1968
|
++func (api *Client) DeleteDisk(diskID string) (task *Task, err error) {
|
|
1969
|
++ uri := fmt.Sprintf("%s/v1/tenants/%s/clusters/%s/disks/%s", api.cfg.endpoint, api.cfg.tenantName,
|
|
1970
|
++ api.cfg.clusterID, diskID)
|
|
1971
|
++ res, err := api.restClient.Delete(uri, api.options.TokenOptions)
|
|
1972
|
++ if err != nil {
|
|
1973
|
++ return
|
|
1974
|
++ }
|
|
1975
|
++ defer res.Body.Close()
|
|
1976
|
++ task, err = getTask(getError(res))
|
|
1977
|
++ return
|
|
1978
|
++}
|
|
1979
|
++
|
|
1980
|
++// Attaches a disk to the specified VM.
|
|
1981
|
++func (api *Client) AttachDisk(vmID string, op *VMDiskOperation) (task *Task, err error) {
|
|
1982
|
++ body, err := json.Marshal(op)
|
|
1983
|
++ if err != nil {
|
|
1984
|
++ return
|
|
1985
|
++ }
|
|
1986
|
++ uri := fmt.Sprintf("%s/v1/tenants/%s/clusters/%s/vms/%s/attach_disk", api.cfg.endpoint, api.cfg.tenantName,
|
|
1987
|
++ api.cfg.clusterID, vmID)
|
|
1988
|
++ res, err := api.restClient.Post(uri, "application/json", bytes.NewReader(body), api.options.TokenOptions)
|
|
1989
|
++ if err != nil {
|
|
1990
|
++ return
|
|
1991
|
++ }
|
|
1992
|
++ defer res.Body.Close()
|
|
1993
|
++ task, err = getTask(getError(res))
|
|
1994
|
++ return
|
|
1995
|
++}
|
|
1996
|
++
|
|
1997
|
++// Detaches a disk from the specified VM.
|
|
1998
|
++func (api *Client) DetachDisk(vmID string, op *VMDiskOperation) (task *Task, err error) {
|
|
1999
|
++ body, err := json.Marshal(op)
|
|
2000
|
++ if err != nil {
|
|
2001
|
++ return
|
|
2002
|
++ }
|
|
2003
|
++ uri := fmt.Sprintf("%s/v1/tenants/%s/clusters/%s/vms/%s/detach_disk", api.cfg.endpoint, api.cfg.tenantName,
|
|
2004
|
++ api.cfg.clusterID, vmID)
|
|
2005
|
++ res, err := api.restClient.Post(uri, "application/json", bytes.NewReader(body), api.options.TokenOptions)
|
|
2006
|
++ if err != nil {
|
|
2007
|
++ return
|
|
2008
|
++ }
|
|
2009
|
++ defer res.Body.Close()
|
|
2010
|
++ task, err = getTask(getError(res))
|
|
2011
|
++ return
|
|
2012
|
++}
|
|
2013
|
++
|
|
2014
|
++// Gets a task by ID.
|
|
2015
|
++func (api *Client) GetTask(taskID string) (task *Task, err error) {
|
|
2016
|
++ uri := fmt.Sprintf("%s/v1/tenants/%s/tasks/%s?region=%s", api.cfg.endpoint, api.cfg.tenantName,
|
|
2017
|
++ taskID, api.cfg.region)
|
|
2018
|
++ res, err := api.restClient.Get(uri, api.options.TokenOptions)
|
|
2019
|
++ if err != nil {
|
|
2020
|
++ return
|
|
2021
|
++ }
|
|
2022
|
++ defer res.Body.Close()
|
|
2023
|
++ result, err := getTask(getError(res))
|
|
2024
|
++ return result, err
|
|
2025
|
++}
|
|
2026
|
++
|
|
2027
|
++// Waits for a task to complete by polling the tasks API until a task returns with the state COMPLETED or ERROR.
|
|
2028
|
++func (api *Client) WaitForTask(taskID string) (task *Task, err error) {
|
|
2029
|
++ start := time.Now()
|
|
2030
|
++ numErrors := 0
|
|
2031
|
++ maxErrors := api.options.TaskRetryCount
|
|
2032
|
++
|
|
2033
|
++ for time.Since(start) < api.options.TaskPollTimeout {
|
|
2034
|
++ task, err = api.GetTask(taskID)
|
|
2035
|
++ if err != nil {
|
|
2036
|
++ switch err.(type) {
|
|
2037
|
++ // If an ApiError comes back, something is wrong, return the error to the caller
|
|
2038
|
++ case APIError:
|
|
2039
|
++ return
|
|
2040
|
++ // For other errors, retry before giving up
|
|
2041
|
++ default:
|
|
2042
|
++ numErrors++
|
|
2043
|
++ if numErrors > maxErrors {
|
|
2044
|
++ return
|
|
2045
|
++ }
|
|
2046
|
++ }
|
|
2047
|
++ } else {
|
|
2048
|
++ // Reset the error count any time a successful call is made
|
|
2049
|
++ numErrors = 0
|
|
2050
|
++ if StringVal(task.State) == "COMPLETED" {
|
|
2051
|
++ return
|
|
2052
|
++ }
|
|
2053
|
++ if StringVal(task.State) == "ERROR" {
|
|
2054
|
++ err = TaskError{StringVal(task.ID), getFailedStep(task)}
|
|
2055
|
++ return
|
|
2056
|
++ }
|
|
2057
|
++ }
|
|
2058
|
++ time.Sleep(api.options.TaskPollDelay)
|
|
2059
|
++ }
|
|
2060
|
++ err = TaskTimeoutError{taskID}
|
|
2061
|
++ return
|
|
2062
|
++}
|
|
2063
|
++
|
|
2064
|
++// CreateOrUpdateLoadBalancer creates a load balancer if not existed, or update one otherwise
|
|
2065
|
++func (api *Client) CreateOrUpdateLoadBalancer(spec *LoadBalancerCreateSpec) (*Task, error) {
|
|
2066
|
++ body, err := json.Marshal(spec)
|
|
2067
|
++ if err != nil {
|
|
2068
|
++ return nil, err
|
|
2069
|
++ }
|
|
2070
|
++ uri := fmt.Sprintf("%s/v1/tenants/%s/clusters/%s/loadbalancers", api.cfg.endpoint, api.cfg.tenantName,
|
|
2071
|
++ api.cfg.clusterID)
|
|
2072
|
++ res, err := api.restClient.Post(uri, "application/json", bytes.NewReader(body), api.options.TokenOptions)
|
|
2073
|
++ if err != nil {
|
|
2074
|
++ return nil, err
|
|
2075
|
++ }
|
|
2076
|
++ defer res.Body.Close()
|
|
2077
|
++ return getTask(getError(res))
|
|
2078
|
++}
|
|
2079
|
++
|
|
2080
|
++// GetLoadBalancer returns a load balancer by name
|
|
2081
|
++func (api *Client) GetLoadBalancer(loadBalancerName *string) (*LoadBalancer, error) {
|
|
2082
|
++ uri := fmt.Sprintf("%s/v1/tenants/%s/clusters/%s/loadbalancers/%s", api.cfg.endpoint, api.cfg.tenantName,
|
|
2083
|
++ api.cfg.clusterID, StringVal(loadBalancerName))
|
|
2084
|
++ res, err := api.restClient.Get(uri, api.options.TokenOptions)
|
|
2085
|
++ if err != nil {
|
|
2086
|
++ return nil, err
|
|
2087
|
++ }
|
|
2088
|
++ defer res.Body.Close()
|
|
2089
|
++ res, err = getError(res)
|
|
2090
|
++ if err != nil {
|
|
2091
|
++ return nil, err
|
|
2092
|
++ }
|
|
2093
|
++ loadBalancer := &LoadBalancer{}
|
|
2094
|
++ err = json.NewDecoder(res.Body).Decode(loadBalancer)
|
|
2095
|
++ return loadBalancer, err
|
|
2096
|
++}
|
|
2097
|
++
|
|
2098
|
++// DeleteLoadBalancer deletes a load balancer by name
|
|
2099
|
++func (api *Client) DeleteLoadBalancer(loadBalancerName *string) (*Task, error) {
|
|
2100
|
++ uri := fmt.Sprintf("%s/v1/tenants/%s/clusters/%s/loadbalancers/%s", api.cfg.endpoint, api.cfg.tenantName,
|
|
2101
|
++ api.cfg.clusterID, StringVal(loadBalancerName))
|
|
2102
|
++ res, err := api.restClient.Delete(uri, api.options.TokenOptions)
|
|
2103
|
++ if err != nil {
|
|
2104
|
++ return nil, err
|
|
2105
|
++ }
|
|
2106
|
++ return getTask(getError(res))
|
|
2107
|
++}
|
|
2108
|
++
|
|
2109
|
++// ApplyVMsToLoadBalancer updates the instances that are registered with the load balancer
|
|
2110
|
++func (api *Client) ApplyVMsToLoadBalancer(loadBalancerName *string, update *LoadBalancerVMUpdate) (*Task, error) {
|
|
2111
|
++ body, err := json.Marshal(update)
|
|
2112
|
++ if err != nil {
|
|
2113
|
++ return nil, err
|
|
2114
|
++ }
|
|
2115
|
++ uri := fmt.Sprintf("%s/v1/tenants/%s/clusters/%s/loadbalancers/%s/update_vms", api.cfg.endpoint, api.cfg.tenantName,
|
|
2116
|
++ api.cfg.clusterID, StringVal(loadBalancerName))
|
|
2117
|
++ res, err := api.restClient.Post(uri, "application/json", bytes.NewReader(body), api.options.TokenOptions)
|
|
2118
|
++ if err != nil {
|
|
2119
|
++ return nil, err
|
|
2120
|
++ }
|
|
2121
|
++ defer res.Body.Close()
|
|
2122
|
++ return getTask(getError(res))
|
|
2123
|
++}
|
|
2124
|
++
|
|
2125
|
++// Gets all the zones in which the cluster has the VMs in.
|
|
2126
|
++func (api *Client) GetZones() (zones []string, err error) {
|
|
2127
|
++ uri := fmt.Sprintf("%s/v1/tenants/%s/clusters/%s/zones", api.cfg.endpoint, api.cfg.tenantName,
|
|
2128
|
++ api.cfg.clusterID)
|
|
2129
|
++ res, err := api.restClient.Get(uri, api.options.TokenOptions)
|
|
2130
|
++ if err != nil {
|
|
2131
|
++ return
|
|
2132
|
++ }
|
|
2133
|
++ defer res.Body.Close()
|
|
2134
|
++ res, err = getError(res)
|
|
2135
|
++ if err != nil {
|
|
2136
|
++ return
|
|
2137
|
++ }
|
|
2138
|
++ err = json.NewDecoder(res.Body).Decode(&zones)
|
|
2139
|
++ return
|
|
2140
|
++}
|
|
2141
|
++
|
|
2142
|
++// Reads a task object out of the HTTP response. Takes an error argument
|
|
2143
|
++// so that GetTask can easily wrap GetError. This function will do nothing
|
|
2144
|
++// if e is not nil.
|
|
2145
|
++// e.g. res, err := getTask(getError(someApi.Get()))
|
|
2146
|
++func getTask(res *http.Response, e error) (*Task, error) {
|
|
2147
|
++ if e != nil {
|
|
2148
|
++ return nil, e
|
|
2149
|
++ }
|
|
2150
|
++ var task Task
|
|
2151
|
++ err := json.NewDecoder(res.Body).Decode(&task)
|
|
2152
|
++ if err != nil {
|
|
2153
|
++ return nil, err
|
|
2154
|
++ }
|
|
2155
|
++ if StringVal(task.State) == "ERROR" {
|
|
2156
|
++ // Critical: return task as well, so that it can be examined
|
|
2157
|
++ // for error details.
|
|
2158
|
++ return &task, TaskError{StringVal(task.ID), getFailedStep(&task)}
|
|
2159
|
++ }
|
|
2160
|
++ return &task, nil
|
|
2161
|
++}
|
|
2162
|
++
|
|
2163
|
++// Gets the failed step in the task to get error details for failed task.
|
|
2164
|
++func getFailedStep(task *Task) (step Step) {
|
|
2165
|
++ var errorStep Step
|
|
2166
|
++ for _, s := range task.Steps {
|
|
2167
|
++ if StringVal(s.State) == "ERROR" {
|
|
2168
|
++ errorStep = *s
|
|
2169
|
++ break
|
|
2170
|
++ }
|
|
2171
|
++ }
|
|
2172
|
++
|
|
2173
|
++ return errorStep
|
|
2174
|
++}
|
|
2175
|
+\ No newline at end of file
|
|
2176
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/oidcclient.go cascade-kubernetes/pkg/cloudprovider/providers/cascade/oidcclient.go
|
|
2177
|
+--- kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/oidcclient.go 1970-01-01 00:00:00.000000000 +0000
|
|
2178
|
+@@ -0,0 +1,297 @@
|
|
2179
|
++package cascade
|
|
2180
|
++
|
|
2181
|
++import (
|
|
2182
|
++ "crypto/tls"
|
|
2183
|
++ "crypto/x509"
|
|
2184
|
++ "encoding/json"
|
|
2185
|
++ "encoding/pem"
|
|
2186
|
++ "fmt"
|
|
2187
|
++ "io/ioutil"
|
|
2188
|
++ "log"
|
|
2189
|
++ "net/http"
|
|
2190
|
++ "net/url"
|
|
2191
|
++ "strings"
|
|
2192
|
++)
|
|
2193
|
++
|
|
2194
|
++const tokenScope string = "openid offline_access"
|
|
2195
|
++
|
|
2196
|
++// OIDCClient is client for OIDC
|
|
2197
|
++type OIDCClient struct {
|
|
2198
|
++ httpClient *http.Client
|
|
2199
|
++ logger *log.Logger
|
|
2200
|
++
|
|
2201
|
++ Endpoint string
|
|
2202
|
++ Options *OIDCClientOptions
|
|
2203
|
++}
|
|
2204
|
++
|
|
2205
|
++// OIDCClientOptions is OIDC client options
|
|
2206
|
++type OIDCClientOptions struct {
|
|
2207
|
++ // Whether or not to ignore any TLS errors when talking to Cascade,
|
|
2208
|
++ // false by default.
|
|
2209
|
++ IgnoreCertificate bool
|
|
2210
|
++
|
|
2211
|
++ // List of root CA's to use for server validation
|
|
2212
|
++ // nil by default.
|
|
2213
|
++ RootCAs *x509.CertPool
|
|
2214
|
++
|
|
2215
|
++ // The scope values to use when requesting tokens
|
|
2216
|
++ TokenScope string
|
|
2217
|
++}
|
|
2218
|
++
|
|
2219
|
++// NewOIDCClient creates an instance of OIDCClient
|
|
2220
|
++func NewOIDCClient(endpoint string, options *OIDCClientOptions, logger *log.Logger) (c *OIDCClient) {
|
|
2221
|
++ if logger == nil {
|
|
2222
|
++ logger = log.New(ioutil.Discard, "", log.LstdFlags)
|
|
2223
|
++ }
|
|
2224
|
++
|
|
2225
|
++ options = buildOptions(options)
|
|
2226
|
++ tr := &http.Transport{
|
|
2227
|
++ TLSClientConfig: &tls.Config{
|
|
2228
|
++ InsecureSkipVerify: options.IgnoreCertificate,
|
|
2229
|
++ RootCAs: options.RootCAs},
|
|
2230
|
++ }
|
|
2231
|
++
|
|
2232
|
++ c = &OIDCClient{
|
|
2233
|
++ httpClient: &http.Client{Transport: tr},
|
|
2234
|
++ logger: logger,
|
|
2235
|
++ Endpoint: strings.TrimRight(endpoint, "/"),
|
|
2236
|
++ Options: options,
|
|
2237
|
++ }
|
|
2238
|
++ return
|
|
2239
|
++}
|
|
2240
|
++
|
|
2241
|
++func buildOptions(options *OIDCClientOptions) (result *OIDCClientOptions) {
|
|
2242
|
++ result = &OIDCClientOptions{
|
|
2243
|
++ TokenScope: tokenScope,
|
|
2244
|
++ }
|
|
2245
|
++
|
|
2246
|
++ if options == nil {
|
|
2247
|
++ return
|
|
2248
|
++ }
|
|
2249
|
++
|
|
2250
|
++ result.IgnoreCertificate = options.IgnoreCertificate
|
|
2251
|
++
|
|
2252
|
++ if options.RootCAs != nil {
|
|
2253
|
++ result.RootCAs = options.RootCAs
|
|
2254
|
++ }
|
|
2255
|
++
|
|
2256
|
++ if options.TokenScope != "" {
|
|
2257
|
++ result.TokenScope = options.TokenScope
|
|
2258
|
++ }
|
|
2259
|
++
|
|
2260
|
++ return
|
|
2261
|
++}
|
|
2262
|
++
|
|
2263
|
++func (client *OIDCClient) buildURL(path string) (url string) {
|
|
2264
|
++ return fmt.Sprintf("%s%s", client.Endpoint, path)
|
|
2265
|
++}
|
|
2266
|
++
|
|
2267
|
++// Cert download helper
|
|
2268
|
++
|
|
2269
|
++const certDownloadPath string = "/afd/vecs/ssl"
|
|
2270
|
++
|
|
2271
|
++type lightWaveCert struct {
|
|
2272
|
++ Value string `json:"encoded"`
|
|
2273
|
++}
|
|
2274
|
++
|
|
2275
|
++// GetRootCerts gets root certs
|
|
2276
|
++func (client *OIDCClient) GetRootCerts() (certList []*x509.Certificate, err error) {
|
|
2277
|
++ // turn TLS verification off for
|
|
2278
|
++ originalTr := client.httpClient.Transport
|
|
2279
|
++ defer client.setTransport(originalTr)
|
|
2280
|
++
|
|
2281
|
++ tr := &http.Transport{
|
|
2282
|
++ TLSClientConfig: &tls.Config{
|
|
2283
|
++ InsecureSkipVerify: true,
|
|
2284
|
++ },
|
|
2285
|
++ }
|
|
2286
|
++ client.setTransport(tr)
|
|
2287
|
++
|
|
2288
|
++ // get the certs
|
|
2289
|
++ resp, err := client.httpClient.Get(client.buildURL(certDownloadPath))
|
|
2290
|
++ if err != nil {
|
|
2291
|
++ return
|
|
2292
|
++ }
|
|
2293
|
++ defer resp.Body.Close()
|
|
2294
|
++ if resp.StatusCode != 200 {
|
|
2295
|
++ err = fmt.Errorf("Unexpected error retrieving auth server certs: %v %s", resp.StatusCode, resp.Status)
|
|
2296
|
++ return
|
|
2297
|
++ }
|
|
2298
|
++
|
|
2299
|
++ // parse the certs
|
|
2300
|
++ certsData := &[]lightWaveCert{}
|
|
2301
|
++ err = json.NewDecoder(resp.Body).Decode(certsData)
|
|
2302
|
++ if err != nil {
|
|
2303
|
++ return
|
|
2304
|
++ }
|
|
2305
|
++
|
|
2306
|
++ certList = make([]*x509.Certificate, len(*certsData))
|
|
2307
|
++ for idx, cert := range *certsData {
|
|
2308
|
++ block, _ := pem.Decode([]byte(cert.Value))
|
|
2309
|
++ if block == nil {
|
|
2310
|
++ err = fmt.Errorf("Unexpected response format: %v", certsData)
|
|
2311
|
++ return nil, err
|
|
2312
|
++ }
|
|
2313
|
++
|
|
2314
|
++ decodedCert, err := x509.ParseCertificate(block.Bytes)
|
|
2315
|
++ if err != nil {
|
|
2316
|
++ return nil, err
|
|
2317
|
++ }
|
|
2318
|
++
|
|
2319
|
++ certList[idx] = decodedCert
|
|
2320
|
++ }
|
|
2321
|
++
|
|
2322
|
++ return
|
|
2323
|
++}
|
|
2324
|
++
|
|
2325
|
++func (client *OIDCClient) setTransport(tr http.RoundTripper) {
|
|
2326
|
++ client.httpClient.Transport = tr
|
|
2327
|
++}
|
|
2328
|
++
|
|
2329
|
++// Metadata request helpers
|
|
2330
|
++const metadataPathFormat string = "/openidconnect/%s/.well-known/openid-configuration"
|
|
2331
|
++
|
|
2332
|
++// OIDCMetadataResponse is the response for Metadata request
|
|
2333
|
++type OIDCMetadataResponse struct {
|
|
2334
|
++ TokenEndpoint string `json:"token_endpoint"`
|
|
2335
|
++ AuthorizationEndpoint string `json:"authorization_endpoint"`
|
|
2336
|
++ EndSessionEndpoint string `json:"end_session_endpoint"`
|
|
2337
|
++}
|
|
2338
|
++
|
|
2339
|
++func (client *OIDCClient) getMetadata(domain string) (metadata *OIDCMetadataResponse, err error) {
|
|
2340
|
++ metadataPath := fmt.Sprintf(metadataPathFormat, domain)
|
|
2341
|
++ request, err := http.NewRequest("GET", client.buildURL(metadataPath), nil)
|
|
2342
|
++ if err != nil {
|
|
2343
|
++ return nil, err
|
|
2344
|
++ }
|
|
2345
|
++
|
|
2346
|
++ resp, err := client.httpClient.Do(request)
|
|
2347
|
++ if err != nil {
|
|
2348
|
++ return nil, err
|
|
2349
|
++ }
|
|
2350
|
++ defer resp.Body.Close()
|
|
2351
|
++
|
|
2352
|
++ err = client.checkResponse(resp)
|
|
2353
|
++ if err != nil {
|
|
2354
|
++ return nil, err
|
|
2355
|
++ }
|
|
2356
|
++
|
|
2357
|
++ metadata = &OIDCMetadataResponse{}
|
|
2358
|
++ err = json.NewDecoder(resp.Body).Decode(metadata)
|
|
2359
|
++ if err != nil {
|
|
2360
|
++ return nil, err
|
|
2361
|
++ }
|
|
2362
|
++
|
|
2363
|
++ return
|
|
2364
|
++}
|
|
2365
|
++
|
|
2366
|
++// Token request helpers
|
|
2367
|
++
|
|
2368
|
++const passwordGrantFormatString = "grant_type=password&username=%s&password=%s&scope=%s"
|
|
2369
|
++const refreshTokenGrantFormatString = "grant_type=refresh_token&refresh_token=%s"
|
|
2370
|
++const clientGrantFormatString = "grant_type=password&username=%s&password=%s&scope=%s&client_id=%s"
|
|
2371
|
++
|
|
2372
|
++// OIDCTokenResponse is the response for OIDC request
|
|
2373
|
++type OIDCTokenResponse struct {
|
|
2374
|
++ AccessToken string `json:"access_token"`
|
|
2375
|
++ ExpiresIn int `json:"expires_in"`
|
|
2376
|
++ RefreshToken string `json:"refresh_token,omitempty"`
|
|
2377
|
++ IDToken string `json:"id_token"`
|
|
2378
|
++ TokenType string `json:"token_type"`
|
|
2379
|
++}
|
|
2380
|
++
|
|
2381
|
++// GetTokenByPasswordGrant gets OIDC tokens by password
|
|
2382
|
++func (client *OIDCClient) GetTokenByPasswordGrant(domain, username, password string) (tokens *OIDCTokenResponse, err error) {
|
|
2383
|
++ metadata, err := client.getMetadata(domain)
|
|
2384
|
++ if err != nil {
|
|
2385
|
++ return nil, err
|
|
2386
|
++ }
|
|
2387
|
++
|
|
2388
|
++ username = url.QueryEscape(username)
|
|
2389
|
++ password = url.QueryEscape(password)
|
|
2390
|
++ body := fmt.Sprintf(passwordGrantFormatString, username, password, client.Options.TokenScope)
|
|
2391
|
++ return client.getToken(metadata.TokenEndpoint, body)
|
|
2392
|
++}
|
|
2393
|
++
|
|
2394
|
++// GetClientTokenByPasswordGrant gets OIDC tokens by password
|
|
2395
|
++func (client *OIDCClient) GetClientTokenByPasswordGrant(domain, username, password, clientID string) (tokens *OIDCTokenResponse, err error) {
|
|
2396
|
++ metadata, err := client.getMetadata(domain)
|
|
2397
|
++ if err != nil {
|
|
2398
|
++ return nil, err
|
|
2399
|
++ }
|
|
2400
|
++
|
|
2401
|
++ username = url.QueryEscape(username)
|
|
2402
|
++ password = url.QueryEscape(password)
|
|
2403
|
++ clientID = url.QueryEscape(clientID)
|
|
2404
|
++ body := fmt.Sprintf(clientGrantFormatString, username, password, client.Options.TokenScope, clientID)
|
|
2405
|
++ return client.getToken(metadata.TokenEndpoint, body)
|
|
2406
|
++}
|
|
2407
|
++
|
|
2408
|
++// GetTokenByRefreshTokenGrant gets OIDC tokens by refresh token
|
|
2409
|
++func (client *OIDCClient) GetTokenByRefreshTokenGrant(domain, refreshToken string) (tokens *OIDCTokenResponse, err error) {
|
|
2410
|
++ metadata, err := client.getMetadata(domain)
|
|
2411
|
++ if err != nil {
|
|
2412
|
++ return nil, err
|
|
2413
|
++ }
|
|
2414
|
++
|
|
2415
|
++ body := fmt.Sprintf(refreshTokenGrantFormatString, refreshToken)
|
|
2416
|
++ return client.getToken(metadata.TokenEndpoint, body)
|
|
2417
|
++}
|
|
2418
|
++
|
|
2419
|
++func (client *OIDCClient) getToken(tokenEndpoint, body string) (tokens *OIDCTokenResponse, err error) {
|
|
2420
|
++ request, err := http.NewRequest("POST", tokenEndpoint, strings.NewReader(body))
|
|
2421
|
++ if err != nil {
|
|
2422
|
++ return nil, err
|
|
2423
|
++ }
|
|
2424
|
++ request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
|
2425
|
++
|
|
2426
|
++ resp, err := client.httpClient.Do(request)
|
|
2427
|
++ if err != nil {
|
|
2428
|
++ return nil, err
|
|
2429
|
++ }
|
|
2430
|
++ defer resp.Body.Close()
|
|
2431
|
++
|
|
2432
|
++ err = client.checkResponse(resp)
|
|
2433
|
++ if err != nil {
|
|
2434
|
++ return nil, err
|
|
2435
|
++ }
|
|
2436
|
++
|
|
2437
|
++ tokens = &OIDCTokenResponse{}
|
|
2438
|
++ err = json.NewDecoder(resp.Body).Decode(tokens)
|
|
2439
|
++ if err != nil {
|
|
2440
|
++ return nil, err
|
|
2441
|
++ }
|
|
2442
|
++
|
|
2443
|
++ return
|
|
2444
|
++}
|
|
2445
|
++
|
|
2446
|
++// OIDCError is OIDC error
|
|
2447
|
++type OIDCError struct {
|
|
2448
|
++ Code string `json:"error"`
|
|
2449
|
++ Message string `json:"error_description"`
|
|
2450
|
++}
|
|
2451
|
++
|
|
2452
|
++func (e OIDCError) Error() string {
|
|
2453
|
++ return fmt.Sprintf("%v: %v", e.Code, e.Message)
|
|
2454
|
++}
|
|
2455
|
++
|
|
2456
|
++func (client *OIDCClient) checkResponse(response *http.Response) (err error) {
|
|
2457
|
++ if response.StatusCode/100 == 2 {
|
|
2458
|
++ return
|
|
2459
|
++ }
|
|
2460
|
++
|
|
2461
|
++ respBody, readErr := ioutil.ReadAll(response.Body)
|
|
2462
|
++ if readErr != nil {
|
|
2463
|
++ return fmt.Errorf(
|
|
2464
|
++ "Status: %v, Body: %v [%v]", response.Status, string(respBody[:]), readErr)
|
|
2465
|
++ }
|
|
2466
|
++
|
|
2467
|
++ var oidcErr OIDCError
|
|
2468
|
++ err = json.Unmarshal(respBody, &oidcErr)
|
|
2469
|
++ if err != nil || oidcErr.Code == "" {
|
|
2470
|
++ return fmt.Errorf(
|
|
2471
|
++ "Status: %v, Body: %v [%v]", response.Status, string(respBody[:]), readErr)
|
|
2472
|
++ }
|
|
2473
|
++
|
|
2474
|
++ return oidcErr
|
|
2475
|
++}
|
|
2476
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/OWNERS cascade-kubernetes/pkg/cloudprovider/providers/cascade/OWNERS
|
|
2477
|
+--- kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/OWNERS 1970-01-01 00:00:00.000000000 +0000
|
|
2478
|
+@@ -0,0 +1,3 @@
|
|
2479
|
++maintainers:
|
|
2480
|
++- ashokc
|
|
2481
|
++- ysheng
|
|
2482
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/restclient.go cascade-kubernetes/pkg/cloudprovider/providers/cascade/restclient.go
|
|
2483
|
+--- kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/restclient.go 1970-01-01 00:00:00.000000000 +0000
|
|
2484
|
+@@ -0,0 +1,262 @@
|
|
2485
|
++package cascade
|
|
2486
|
++
|
|
2487
|
++import (
|
|
2488
|
++ "bytes"
|
|
2489
|
++ "encoding/json"
|
|
2490
|
++ "io"
|
|
2491
|
++ "io/ioutil"
|
|
2492
|
++ "net/http"
|
|
2493
|
++)
|
|
2494
|
++
|
|
2495
|
++type restClient struct {
|
|
2496
|
++ httpClient *http.Client
|
|
2497
|
++ authClient *AuthClient
|
|
2498
|
++ UpdateAccessTokenCallback TokenCallback
|
|
2499
|
++}
|
|
2500
|
++
|
|
2501
|
++type request struct {
|
|
2502
|
++ Method string
|
|
2503
|
++ URL string
|
|
2504
|
++ ContentType string
|
|
2505
|
++ Body io.Reader
|
|
2506
|
++ Tokens *TokenOptions
|
|
2507
|
++}
|
|
2508
|
++
|
|
2509
|
++type page struct {
|
|
2510
|
++ Items []interface{} `json:"items"`
|
|
2511
|
++ NextPageLink string `json:"nextPageLink"`
|
|
2512
|
++ PreviousPageLink string `json:"previousPageLink"`
|
|
2513
|
++}
|
|
2514
|
++
|
|
2515
|
++type documentList struct {
|
|
2516
|
++ Items []interface{}
|
|
2517
|
++}
|
|
2518
|
++
|
|
2519
|
++type bodyRewinder func() io.Reader
|
|
2520
|
++
|
|
2521
|
++const appJson string = "application/json"
|
|
2522
|
++const expiredAuthToken int32 = 1904
|
|
2523
|
++
|
|
2524
|
++func (client *restClient) AppendSlice(origSlice []interface{}, dataToAppend []interface{}) []interface{} {
|
|
2525
|
++ origLen := len(origSlice)
|
|
2526
|
++ newLen := origLen + len(dataToAppend)
|
|
2527
|
++
|
|
2528
|
++ if newLen > cap(origSlice) {
|
|
2529
|
++ newSlice := make([]interface{}, (newLen+1)*2)
|
|
2530
|
++ copy(newSlice, origSlice)
|
|
2531
|
++ origSlice = newSlice
|
|
2532
|
++ }
|
|
2533
|
++
|
|
2534
|
++ origSlice = origSlice[0:newLen]
|
|
2535
|
++ copy(origSlice[origLen:newLen], dataToAppend)
|
|
2536
|
++
|
|
2537
|
++ return origSlice
|
|
2538
|
++}
|
|
2539
|
++
|
|
2540
|
++func (client *restClient) Get(url string, tokens *TokenOptions) (res *http.Response, err error) {
|
|
2541
|
++ req := request{"GET", url, "", nil, tokens}
|
|
2542
|
++ res, err = client.SendRequest(&req, nil)
|
|
2543
|
++ return
|
|
2544
|
++}
|
|
2545
|
++
|
|
2546
|
++func (client *restClient) GetList(endpoint string, url string, tokens *TokenOptions) (result []byte, err error) {
|
|
2547
|
++ req := request{"GET", url, "", nil, tokens}
|
|
2548
|
++ res, err := client.SendRequest(&req, nil)
|
|
2549
|
++ if err != nil {
|
|
2550
|
++ return
|
|
2551
|
++ }
|
|
2552
|
++ res, err = getError(res)
|
|
2553
|
++ if err != nil {
|
|
2554
|
++ return
|
|
2555
|
++ }
|
|
2556
|
++
|
|
2557
|
++ decoder := json.NewDecoder(res.Body)
|
|
2558
|
++ decoder.UseNumber()
|
|
2559
|
++
|
|
2560
|
++ page := &page{}
|
|
2561
|
++ err = decoder.Decode(page)
|
|
2562
|
++ if err != nil {
|
|
2563
|
++ return
|
|
2564
|
++ }
|
|
2565
|
++
|
|
2566
|
++ documentList := &documentList{}
|
|
2567
|
++ documentList.Items = client.AppendSlice(documentList.Items, page.Items)
|
|
2568
|
++
|
|
2569
|
++ for page.NextPageLink != "" {
|
|
2570
|
++ req = request{"GET", endpoint + page.NextPageLink, "", nil, tokens}
|
|
2571
|
++ res, err = client.SendRequest(&req, nil)
|
|
2572
|
++ if err != nil {
|
|
2573
|
++ return
|
|
2574
|
++ }
|
|
2575
|
++ res, err = getError(res)
|
|
2576
|
++ if err != nil {
|
|
2577
|
++ return
|
|
2578
|
++ }
|
|
2579
|
++
|
|
2580
|
++ decoder = json.NewDecoder(res.Body)
|
|
2581
|
++ decoder.UseNumber()
|
|
2582
|
++
|
|
2583
|
++ page.NextPageLink = ""
|
|
2584
|
++ page.PreviousPageLink = ""
|
|
2585
|
++
|
|
2586
|
++ err = decoder.Decode(page)
|
|
2587
|
++ if err != nil {
|
|
2588
|
++ return
|
|
2589
|
++ }
|
|
2590
|
++
|
|
2591
|
++ documentList.Items = client.AppendSlice(documentList.Items, page.Items)
|
|
2592
|
++ }
|
|
2593
|
++
|
|
2594
|
++ result, err = json.Marshal(documentList)
|
|
2595
|
++
|
|
2596
|
++ return
|
|
2597
|
++}
|
|
2598
|
++
|
|
2599
|
++func (client *restClient) Post(url string, contentType string, body io.ReadSeeker, tokens *TokenOptions) (res *http.Response, err error) {
|
|
2600
|
++ if contentType == "" {
|
|
2601
|
++ contentType = appJson
|
|
2602
|
++ }
|
|
2603
|
++
|
|
2604
|
++ req := request{"POST", url, contentType, body, tokens}
|
|
2605
|
++ rewinder := func() io.Reader {
|
|
2606
|
++ body.Seek(0, 0)
|
|
2607
|
++ return body
|
|
2608
|
++ }
|
|
2609
|
++ res, err = client.SendRequest(&req, rewinder)
|
|
2610
|
++ return
|
|
2611
|
++}
|
|
2612
|
++
|
|
2613
|
++func (client *restClient) Patch(url string, contentType string, body io.ReadSeeker, tokens *TokenOptions) (res *http.Response, err error) {
|
|
2614
|
++ if contentType == "" {
|
|
2615
|
++ contentType = appJson
|
|
2616
|
++ }
|
|
2617
|
++
|
|
2618
|
++ req := request{"PATCH", url, contentType, body, tokens}
|
|
2619
|
++ rewinder := func() io.Reader {
|
|
2620
|
++ body.Seek(0, 0)
|
|
2621
|
++ return body
|
|
2622
|
++ }
|
|
2623
|
++ res, err = client.SendRequest(&req, rewinder)
|
|
2624
|
++ return
|
|
2625
|
++}
|
|
2626
|
++
|
|
2627
|
++func (client *restClient) Put(url string, contentType string, body io.ReadSeeker, tokens *TokenOptions) (res *http.Response, err error) {
|
|
2628
|
++ if contentType == "" {
|
|
2629
|
++ contentType = appJson
|
|
2630
|
++ }
|
|
2631
|
++
|
|
2632
|
++ req := request{"PUT", url, contentType, body, tokens}
|
|
2633
|
++ rewinder := func() io.Reader {
|
|
2634
|
++ body.Seek(0, 0)
|
|
2635
|
++ return body
|
|
2636
|
++ }
|
|
2637
|
++ res, err = client.SendRequest(&req, rewinder)
|
|
2638
|
++ return
|
|
2639
|
++}
|
|
2640
|
++
|
|
2641
|
++func (client *restClient) Delete(url string, tokens *TokenOptions) (res *http.Response, err error) {
|
|
2642
|
++ req := request{"DELETE", url, "", nil, tokens}
|
|
2643
|
++ res, err = client.SendRequest(&req, nil)
|
|
2644
|
++ return
|
|
2645
|
++}
|
|
2646
|
++
|
|
2647
|
++func (client *restClient) SendRequest(req *request, bodyRewinder bodyRewinder) (res *http.Response, err error) {
|
|
2648
|
++ res, err = client.sendRequestHelper(req)
|
|
2649
|
++ // In most cases, we'll return immediately
|
|
2650
|
++ // If the operation succeeded, but we got a 401 response and if we're using
|
|
2651
|
++ // authentication, then we'll look into the body to see if the token expired
|
|
2652
|
++ if err != nil {
|
|
2653
|
++ return res, err
|
|
2654
|
++ }
|
|
2655
|
++ if res.StatusCode != 401 {
|
|
2656
|
++ // It's not a 401, so the token didn't expire
|
|
2657
|
++ return res, err
|
|
2658
|
++ }
|
|
2659
|
++ if req.Tokens == nil || req.Tokens.AccessToken == "" {
|
|
2660
|
++ // We don't have a token, so we can't renew the token, no need to proceed
|
|
2661
|
++ return res, err
|
|
2662
|
++ }
|
|
2663
|
++
|
|
2664
|
++ // We're going to look in the body to see if it failed because the token expired
|
|
2665
|
++ // This means we need to read the body, but the functions that call us also
|
|
2666
|
++ // expect to read the body. So we read the body, then create a new reader
|
|
2667
|
++ // so they can read the body as normal.
|
|
2668
|
++ body, err := ioutil.ReadAll(res.Body)
|
|
2669
|
++ if err != nil {
|
|
2670
|
++ return res, err
|
|
2671
|
++ }
|
|
2672
|
++ res.Body = ioutil.NopCloser(bytes.NewReader(body))
|
|
2673
|
++
|
|
2674
|
++ // Now see if we had an expired token or not
|
|
2675
|
++ var apiError APIError
|
|
2676
|
++ err = json.Unmarshal(body, &apiError)
|
|
2677
|
++ if err != nil {
|
|
2678
|
++ return res, err
|
|
2679
|
++ }
|
|
2680
|
++ if apiError.ErrorCode != expiredAuthToken {
|
|
2681
|
++ return res, nil
|
|
2682
|
++ }
|
|
2683
|
++
|
|
2684
|
++ // We were told that the access token expired, so we acquire a new token using the refresh token.
|
|
2685
|
++ newTokens, err := client.authClient.GetTokensByRefreshToken(req.Tokens.RefreshToken)
|
|
2686
|
++ // If there is an error during token refresh, we assume that the refresh token also expired. So we login again using
|
|
2687
|
++ // the machine account.
|
|
2688
|
++ if err != nil {
|
|
2689
|
++ newTokens, err = client.authClient.GetTokensByMachineAccount()
|
|
2690
|
++ if err != nil {
|
|
2691
|
++ return res, err
|
|
2692
|
++ }
|
|
2693
|
++ }
|
|
2694
|
++ req.Tokens.AccessToken = newTokens.AccessToken
|
|
2695
|
++ if client.UpdateAccessTokenCallback != nil {
|
|
2696
|
++ client.UpdateAccessTokenCallback(newTokens.AccessToken)
|
|
2697
|
++ }
|
|
2698
|
++ if req.Body != nil && bodyRewinder != nil {
|
|
2699
|
++ req.Body = bodyRewinder()
|
|
2700
|
++ }
|
|
2701
|
++ res, err = client.sendRequestHelper(req)
|
|
2702
|
++ return res, nil
|
|
2703
|
++}
|
|
2704
|
++
|
|
2705
|
++func (client *restClient) sendRequestHelper(req *request) (res *http.Response, err error) {
|
|
2706
|
++ r, err := http.NewRequest(req.Method, req.URL, req.Body)
|
|
2707
|
++ if err != nil {
|
|
2708
|
++ return
|
|
2709
|
++ }
|
|
2710
|
++ if req.ContentType != "" {
|
|
2711
|
++ r.Header.Add("Content-Type", req.ContentType)
|
|
2712
|
++ }
|
|
2713
|
++ if req.Tokens != nil && req.Tokens.AccessToken != "" {
|
|
2714
|
++ r.Header.Add("Authorization", "Bearer "+req.Tokens.AccessToken)
|
|
2715
|
++ }
|
|
2716
|
++ res, err = client.httpClient.Do(r)
|
|
2717
|
++ if err != nil {
|
|
2718
|
++ return
|
|
2719
|
++ }
|
|
2720
|
++
|
|
2721
|
++ return
|
|
2722
|
++}
|
|
2723
|
++
|
|
2724
|
++// Reads an error out of the HTTP response, or does nothing if
|
|
2725
|
++// no error occured.
|
|
2726
|
++func getError(res *http.Response) (*http.Response, error) {
|
|
2727
|
++ // Do nothing if the response is a successful 2xx
|
|
2728
|
++ if res.StatusCode/100 == 2 {
|
|
2729
|
++ return res, nil
|
|
2730
|
++ }
|
|
2731
|
++ var apiError APIError
|
|
2732
|
++ // ReadAll is usually a bad practice, but here we need to read the response all
|
|
2733
|
++ // at once because we may attempt to use the data twice. It's preferable to use
|
|
2734
|
++ // methods that take io.Reader, e.g. json.NewDecoder
|
|
2735
|
++ body, err := ioutil.ReadAll(res.Body)
|
|
2736
|
++ if err != nil {
|
|
2737
|
++ return nil, err
|
|
2738
|
++ }
|
|
2739
|
++ err = json.Unmarshal(body, &apiError)
|
|
2740
|
++ if err != nil {
|
|
2741
|
++ // If deserializing into ApiError fails, return a generic HttpError instead
|
|
2742
|
++ return nil, HttpError{res.StatusCode, string(body[:])}
|
|
2743
|
++ }
|
|
2744
|
++ apiError.HttpStatusCode = res.StatusCode
|
|
2745
|
++ return nil, apiError
|
|
2746
|
++}
|
|
2747
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/utils.go cascade-kubernetes/pkg/cloudprovider/providers/cascade/utils.go
|
|
2748
|
+--- kubernetes-1.8.1/pkg/cloudprovider/providers/cascade/utils.go 1970-01-01 00:00:00.000000000 +0000
|
|
2749
|
+@@ -0,0 +1,25 @@
|
|
2750
|
++package cascade
|
|
2751
|
++
|
|
2752
|
++func StringPtr(s string) *string {
|
|
2753
|
++ return &s
|
|
2754
|
++}
|
|
2755
|
++
|
|
2756
|
++// StringVal returns string from string pointer, nil returns ""
|
|
2757
|
++func StringVal(p *string) (s string) {
|
|
2758
|
++ if p != nil {
|
|
2759
|
++ s = *p
|
|
2760
|
++ }
|
|
2761
|
++ return
|
|
2762
|
++}
|
|
2763
|
++
|
|
2764
|
++func Int64Ptr(s int64) *int64 {
|
|
2765
|
++ return &s
|
|
2766
|
++}
|
|
2767
|
++
|
|
2768
|
++func Int64Val(s *int64) int64 {
|
|
2769
|
++ return *s
|
|
2770
|
++}
|
|
2771
|
++
|
|
2772
|
++func Int32Ptr(s int32) *int32 {
|
|
2773
|
++ return &s
|
|
2774
|
++}
|
|
2775
|
+\ No newline at end of file
|
|
2776
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/cloudprovider/providers/providers.go cascade-kubernetes/pkg/cloudprovider/providers/providers.go
|
|
2777
|
+--- kubernetes-1.8.1/pkg/cloudprovider/providers/providers.go 2018-01-23 22:47:25.422819349 +0000
|
|
2778
|
+@@ -20,6 +20,7 @@
|
|
2779
|
+ // Cloud providers
|
|
2780
|
+ _ "k8s.io/kubernetes/pkg/cloudprovider/providers/aws"
|
|
2781
|
+ _ "k8s.io/kubernetes/pkg/cloudprovider/providers/azure"
|
|
2782
|
++ _ "k8s.io/kubernetes/pkg/cloudprovider/providers/cascade"
|
|
2783
|
+ _ "k8s.io/kubernetes/pkg/cloudprovider/providers/cloudstack"
|
|
2784
|
+ _ "k8s.io/kubernetes/pkg/cloudprovider/providers/gce"
|
|
2785
|
+ _ "k8s.io/kubernetes/pkg/cloudprovider/providers/openstack"
|
|
2786
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/printers/internalversion/describe.go cascade-kubernetes/pkg/printers/internalversion/describe.go
|
|
2787
|
+--- kubernetes-1.8.1/pkg/printers/internalversion/describe.go 2018-01-23 22:47:25.518819352 +0000
|
|
2788
|
+@@ -764,6 +764,8 @@
|
|
2789
|
+ printFlexVolumeSource(volume.VolumeSource.FlexVolume, w)
|
|
2790
|
+ case volume.VolumeSource.Flocker != nil:
|
|
2791
|
+ printFlockerVolumeSource(volume.VolumeSource.Flocker, w)
|
|
2792
|
++ case volume.VolumeSource.CascadeDisk != nil:
|
|
2793
|
++ printCascadeDiskVolumeSource(volume.VolumeSource.CascadeDisk, w)
|
|
2794
|
+ default:
|
|
2795
|
+ w.Write(LEVEL_1, "<unknown>\n")
|
|
2796
|
+ }
|
|
2797
|
+@@ -1047,6 +1049,13 @@
|
|
2798
|
+ flocker.DatasetName, flocker.DatasetUUID)
|
|
2799
|
+ }
|
|
2800
|
+
|
|
2801
|
++func printCascadeDiskVolumeSource(cascade *api.CascadeDiskVolumeSource, w PrefixWriter) {
|
|
2802
|
++ w.Write(LEVEL_2, "Type:\tCascadeDisk (a Persistent Disk resource in Cascade)\n"+
|
|
2803
|
++ " DiskID:\t%v\n"+
|
|
2804
|
++ " FSType:\t%v\n",
|
|
2805
|
++ cascade.DiskID, cascade.FSType)
|
|
2806
|
++}
|
|
2807
|
++
|
|
2808
|
+ type PersistentVolumeDescriber struct {
|
|
2809
|
+ clientset.Interface
|
|
2810
|
+ }
|
|
2811
|
+@@ -1130,6 +1139,8 @@
|
|
2812
|
+ printFlexVolumeSource(pv.Spec.FlexVolume, w)
|
|
2813
|
+ case pv.Spec.Flocker != nil:
|
|
2814
|
+ printFlockerVolumeSource(pv.Spec.Flocker, w)
|
|
2815
|
++ case pv.Spec.CascadeDisk != nil:
|
|
2816
|
++ printCascadeDiskVolumeSource(pv.Spec.CascadeDisk, w)
|
|
2817
|
+ default:
|
|
2818
|
+ w.Write(LEVEL_1, "<unknown>\n")
|
|
2819
|
+ }
|
|
2820
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/security/podsecuritypolicy/util/util.go cascade-kubernetes/pkg/security/podsecuritypolicy/util/util.go
|
|
2821
|
+--- kubernetes-1.8.1/pkg/security/podsecuritypolicy/util/util.go 2018-01-23 22:47:25.558819354 +0000
|
|
2822
|
+@@ -67,6 +67,7 @@
|
|
2823
|
+ string(extensions.Projected),
|
|
2824
|
+ string(extensions.PortworxVolume),
|
|
2825
|
+ string(extensions.ScaleIO),
|
|
2826
|
++ string(extensions.CascadeDisk),
|
|
2827
|
+ )
|
|
2828
|
+ return fstypes
|
|
2829
|
+ }
|
|
2830
|
+@@ -128,6 +129,8 @@
|
|
2831
|
+ return extensions.PortworxVolume, nil
|
|
2832
|
+ case v.ScaleIO != nil:
|
|
2833
|
+ return extensions.ScaleIO, nil
|
|
2834
|
++ case v.CascadeDisk != nil:
|
|
2835
|
++ return extensions.CascadeDisk, nil
|
|
2836
|
+ }
|
|
2837
|
+
|
|
2838
|
+ return "", fmt.Errorf("unknown volume type for volume: %#v", v)
|
|
2839
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/volume/cascade_disk/attacher.go cascade-kubernetes/pkg/volume/cascade_disk/attacher.go
|
|
2840
|
+--- kubernetes-1.8.1/pkg/volume/cascade_disk/attacher.go 1970-01-01 00:00:00.000000000 +0000
|
|
2841
|
+@@ -0,0 +1,278 @@
|
|
2842
|
++package cascade_disk
|
|
2843
|
++
|
|
2844
|
++import (
|
|
2845
|
++ "fmt"
|
|
2846
|
++ "os"
|
|
2847
|
++ "path"
|
|
2848
|
++ "time"
|
|
2849
|
++
|
|
2850
|
++ "github.com/golang/glog"
|
|
2851
|
++ "k8s.io/api/core/v1"
|
|
2852
|
++ "k8s.io/apimachinery/pkg/types"
|
|
2853
|
++ "k8s.io/kubernetes/pkg/cloudprovider/providers/cascade"
|
|
2854
|
++ "k8s.io/kubernetes/pkg/util/mount"
|
|
2855
|
++ "k8s.io/kubernetes/pkg/volume"
|
|
2856
|
++ volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
|
2857
|
++ "k8s.io/kubernetes/pkg/volume/util/volumehelper"
|
|
2858
|
++ "strings"
|
|
2859
|
++)
|
|
2860
|
++
|
|
2861
|
++type cascadeDiskAttacher struct {
|
|
2862
|
++ host volume.VolumeHost
|
|
2863
|
++ cascadeDisks cascade.Disks
|
|
2864
|
++}
|
|
2865
|
++
|
|
2866
|
++var _ volume.Attacher = &cascadeDiskAttacher{}
|
|
2867
|
++var _ volume.AttachableVolumePlugin = &cascadeDiskPlugin{}
|
|
2868
|
++
|
|
2869
|
++func (plugin *cascadeDiskPlugin) NewAttacher() (volume.Attacher, error) {
|
|
2870
|
++ cascadeCloud, err := getCloudProvider(plugin.host.GetCloudProvider())
|
|
2871
|
++ if err != nil {
|
|
2872
|
++ glog.Errorf("Cascade attacher: NewAttacher failed to get cloud provider")
|
|
2873
|
++ return nil, err
|
|
2874
|
++ }
|
|
2875
|
++
|
|
2876
|
++ return &cascadeDiskAttacher{
|
|
2877
|
++ host: plugin.host,
|
|
2878
|
++ cascadeDisks: cascadeCloud,
|
|
2879
|
++ }, nil
|
|
2880
|
++}
|
|
2881
|
++
|
|
2882
|
++// Attach attaches the volume specified by the given spec to the given host. On success, returns the device path where
|
|
2883
|
++// the device was attached on the node.
|
|
2884
|
++func (attacher *cascadeDiskAttacher) Attach(spec *volume.Spec, nodeName types.NodeName) (string, error) {
|
|
2885
|
++ hostName := string(nodeName)
|
|
2886
|
++ volumeSource, _, err := getVolumeSource(spec)
|
|
2887
|
++ if err != nil {
|
|
2888
|
++ glog.Errorf("Cascade attacher: Attach failed to get volume source")
|
|
2889
|
++ return "", err
|
|
2890
|
++ }
|
|
2891
|
++
|
|
2892
|
++ attached, err := attacher.cascadeDisks.DiskIsAttached(volumeSource.DiskID, nodeName)
|
|
2893
|
++ if err != nil {
|
|
2894
|
++ glog.Warningf("Cascade: couldn't check if disk is Attached for host %s, will try attach disk: %+v",
|
|
2895
|
++ hostName, err)
|
|
2896
|
++ attached = false
|
|
2897
|
++ }
|
|
2898
|
++
|
|
2899
|
++ var devicePath string
|
|
2900
|
++ if !attached {
|
|
2901
|
++ glog.V(4).Infof("Cascade: Attach disk called for host %s", hostName)
|
|
2902
|
++
|
|
2903
|
++ devicePath, err = attacher.cascadeDisks.AttachDisk(volumeSource.DiskID, nodeName)
|
|
2904
|
++ if err != nil {
|
|
2905
|
++ glog.Errorf("Error attaching volume %q to node %q: %+v", volumeSource.DiskID, nodeName, err)
|
|
2906
|
++ return "", err
|
|
2907
|
++ }
|
|
2908
|
++ }
|
|
2909
|
++
|
|
2910
|
++ // Cacsade uses device names of the format /dev/sdX, but newer Linux Kernels mount them under /dev/xvdX
|
|
2911
|
++ // (source: AWS console). So we have to rename the first occurrence of sd to xvd.
|
|
2912
|
++ devicePath = strings.Replace(devicePath, "sd", "xvd", 1)
|
|
2913
|
++ return devicePath, nil
|
|
2914
|
++}
|
|
2915
|
++
|
|
2916
|
++// VolumesAreAttached verifies whether the volumes specified in the spec are attached to the specified node.
|
|
2917
|
++func (attacher *cascadeDiskAttacher) VolumesAreAttached(specs []*volume.Spec,
|
|
2918
|
++ nodeName types.NodeName) (map[*volume.Spec]bool, error) {
|
|
2919
|
++ volumesAttachedCheck := make(map[*volume.Spec]bool)
|
|
2920
|
++ volumeSpecMap := make(map[string]*volume.Spec)
|
|
2921
|
++ diskIDList := []string{}
|
|
2922
|
++ for _, spec := range specs {
|
|
2923
|
++ volumeSource, _, err := getVolumeSource(spec)
|
|
2924
|
++ if err != nil {
|
|
2925
|
++ glog.Errorf("Error getting volume (%q) source : %v", spec.Name(), err)
|
|
2926
|
++ continue
|
|
2927
|
++ }
|
|
2928
|
++
|
|
2929
|
++ diskIDList = append(diskIDList, volumeSource.DiskID)
|
|
2930
|
++ volumesAttachedCheck[spec] = true
|
|
2931
|
++ volumeSpecMap[volumeSource.DiskID] = spec
|
|
2932
|
++ }
|
|
2933
|
++ attachedResult, err := attacher.cascadeDisks.DisksAreAttached(diskIDList, nodeName)
|
|
2934
|
++ if err != nil {
|
|
2935
|
++ glog.Errorf(
|
|
2936
|
++ "Error checking if volumes (%v) are attached to current node (%q). err=%v",
|
|
2937
|
++ diskIDList, nodeName, err)
|
|
2938
|
++ return volumesAttachedCheck, err
|
|
2939
|
++ }
|
|
2940
|
++
|
|
2941
|
++ for diskID, attached := range attachedResult {
|
|
2942
|
++ if !attached {
|
|
2943
|
++ spec := volumeSpecMap[diskID]
|
|
2944
|
++ volumesAttachedCheck[spec] = false
|
|
2945
|
++ glog.V(2).Infof("VolumesAreAttached: check volume %q (specName: %q) is no longer attached",
|
|
2946
|
++ diskID, spec.Name())
|
|
2947
|
++ }
|
|
2948
|
++ }
|
|
2949
|
++ return volumesAttachedCheck, nil
|
|
2950
|
++}
|
|
2951
|
++
|
|
2952
|
++// WaitForAttach waits until the devicePath returned by the Attach call is available.
|
|
2953
|
++func (attacher *cascadeDiskAttacher) WaitForAttach(spec *volume.Spec, devicePath string, _ *v1.Pod,
|
|
2954
|
++ timeout time.Duration) (string, error) {
|
|
2955
|
++ volumeSource, _, err := getVolumeSource(spec)
|
|
2956
|
++ if err != nil {
|
|
2957
|
++ glog.Errorf("Cascade attacher: WaitForAttach failed to get volume source")
|
|
2958
|
++ return "", err
|
|
2959
|
++ }
|
|
2960
|
++
|
|
2961
|
++ if devicePath == "" {
|
|
2962
|
++ return "", fmt.Errorf("WaitForAttach failed for disk %s: devicePath is empty.", volumeSource.DiskID)
|
|
2963
|
++ }
|
|
2964
|
++
|
|
2965
|
++ ticker := time.NewTicker(checkSleepDuration)
|
|
2966
|
++ defer ticker.Stop()
|
|
2967
|
++
|
|
2968
|
++ timer := time.NewTimer(timeout)
|
|
2969
|
++ defer timer.Stop()
|
|
2970
|
++
|
|
2971
|
++ for {
|
|
2972
|
++ select {
|
|
2973
|
++ case <-ticker.C:
|
|
2974
|
++ glog.V(4).Infof("Checking disk %s is attached", volumeSource.DiskID)
|
|
2975
|
++ checkPath, err := verifyDevicePath(devicePath)
|
|
2976
|
++ if err != nil {
|
|
2977
|
++ // Log error, if any, and continue checking periodically. See issue #11321
|
|
2978
|
++ glog.Warningf("Cascade attacher: WaitForAttach with devicePath %s Checking PD %s Error verify " +
|
|
2979
|
++ "path", devicePath, volumeSource.DiskID)
|
|
2980
|
++ } else if checkPath != "" {
|
|
2981
|
++ // A device path has successfully been created for the disk
|
|
2982
|
++ glog.V(4).Infof("Successfully found attached disk %s.", volumeSource.DiskID)
|
|
2983
|
++ return devicePath, nil
|
|
2984
|
++ }
|
|
2985
|
++ case <-timer.C:
|
|
2986
|
++ return "", fmt.Errorf("Could not find attached disk %s. Timeout waiting for mount paths to be " +
|
|
2987
|
++ "created.", volumeSource.DiskID)
|
|
2988
|
++ }
|
|
2989
|
++ }
|
|
2990
|
++}
|
|
2991
|
++
|
|
2992
|
++// GetDeviceMountPath returns a path where the device should point which should be bind mounted for individual volumes.
|
|
2993
|
++func (attacher *cascadeDiskAttacher) GetDeviceMountPath(spec *volume.Spec) (string, error) {
|
|
2994
|
++ volumeSource, _, err := getVolumeSource(spec)
|
|
2995
|
++ if err != nil {
|
|
2996
|
++ glog.Errorf("Cascade attacher: GetDeviceMountPath failed to get volume source")
|
|
2997
|
++ return "", err
|
|
2998
|
++ }
|
|
2999
|
++
|
|
3000
|
++ return makeGlobalPDPath(attacher.host, volumeSource.DiskID), nil
|
|
3001
|
++}
|
|
3002
|
++
|
|
3003
|
++// GetMountDeviceRefs finds all other references to the device referenced by deviceMountPath; returns a list of paths.
|
|
3004
|
++func (plugin *cascadeDiskPlugin) GetDeviceMountRefs(deviceMountPath string) ([]string, error) {
|
|
3005
|
++ mounter := plugin.host.GetMounter(plugin.GetPluginName())
|
|
3006
|
++ return mount.GetMountRefs(mounter, deviceMountPath)
|
|
3007
|
++}
|
|
3008
|
++
|
|
3009
|
++// MountDevice mounts device to global mount point.
|
|
3010
|
++func (attacher *cascadeDiskAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string) error {
|
|
3011
|
++ mounter := attacher.host.GetMounter(cascadeDiskPluginName)
|
|
3012
|
++ notMnt, err := mounter.IsLikelyNotMountPoint(deviceMountPath)
|
|
3013
|
++ if err != nil {
|
|
3014
|
++ if os.IsNotExist(err) {
|
|
3015
|
++ if err := os.MkdirAll(deviceMountPath, 0750); err != nil {
|
|
3016
|
++ glog.Errorf("Failed to create directory at %#v. err: %s", deviceMountPath, err)
|
|
3017
|
++ return err
|
|
3018
|
++ }
|
|
3019
|
++ notMnt = true
|
|
3020
|
++ } else {
|
|
3021
|
++ return err
|
|
3022
|
++ }
|
|
3023
|
++ }
|
|
3024
|
++
|
|
3025
|
++ volumeSource, _, err := getVolumeSource(spec)
|
|
3026
|
++ if err != nil {
|
|
3027
|
++ glog.Errorf("Cascade attacher: MountDevice failed to get volume source. err: %s", err)
|
|
3028
|
++ return err
|
|
3029
|
++ }
|
|
3030
|
++
|
|
3031
|
++ options := []string{}
|
|
3032
|
++
|
|
3033
|
++ if notMnt {
|
|
3034
|
++ diskMounter := volumehelper.NewSafeFormatAndMountFromHost(cascadeDiskPluginName, attacher.host)
|
|
3035
|
++ mountOptions := volume.MountOptionFromSpec(spec)
|
|
3036
|
++ err = diskMounter.FormatAndMount(devicePath, deviceMountPath, volumeSource.FSType, mountOptions)
|
|
3037
|
++ if err != nil {
|
|
3038
|
++ os.Remove(deviceMountPath)
|
|
3039
|
++ return err
|
|
3040
|
++ }
|
|
3041
|
++ glog.V(4).Infof("formatting spec %v devicePath %v deviceMountPath %v fs %v with options %+v",
|
|
3042
|
++ spec.Name(), devicePath, deviceMountPath, volumeSource.FSType, options)
|
|
3043
|
++ }
|
|
3044
|
++ return nil
|
|
3045
|
++}
|
|
3046
|
++
|
|
3047
|
++type cascadeDiskDetacher struct {
|
|
3048
|
++ mounter mount.Interface
|
|
3049
|
++ cascadeDisks cascade.Disks
|
|
3050
|
++}
|
|
3051
|
++
|
|
3052
|
++var _ volume.Detacher = &cascadeDiskDetacher{}
|
|
3053
|
++
|
|
3054
|
++// NewDetacher returns the detacher associated with the Cascade volume plugin.
|
|
3055
|
++func (plugin *cascadeDiskPlugin) NewDetacher() (volume.Detacher, error) {
|
|
3056
|
++ cascadeCloud, err := getCloudProvider(plugin.host.GetCloudProvider())
|
|
3057
|
++ if err != nil {
|
|
3058
|
++ glog.Errorf("Cascade attacher: NewDetacher failed to get cloud provider. err: %s", err)
|
|
3059
|
++ return nil, err
|
|
3060
|
++ }
|
|
3061
|
++
|
|
3062
|
++ return &cascadeDiskDetacher{
|
|
3063
|
++ mounter: plugin.host.GetMounter(plugin.GetPluginName()),
|
|
3064
|
++ cascadeDisks: cascadeCloud,
|
|
3065
|
++ }, nil
|
|
3066
|
++}
|
|
3067
|
++
|
|
3068
|
++// Detach detaches the given device from the given host.
|
|
3069
|
++func (detacher *cascadeDiskDetacher) Detach(deviceMountPath string, nodeName types.NodeName) error {
|
|
3070
|
++ hostName := string(nodeName)
|
|
3071
|
++ diskID := path.Base(deviceMountPath)
|
|
3072
|
++ attached, err := detacher.cascadeDisks.DiskIsAttached(diskID, nodeName)
|
|
3073
|
++ if err != nil {
|
|
3074
|
++ // Log error and continue with detach
|
|
3075
|
++ glog.Errorf(
|
|
3076
|
++ "Error checking if persistent disk (%q) is already attached to current node (%q). " +
|
|
3077
|
++ "Will continue and try detach anyway. err=%v", diskID, hostName, err)
|
|
3078
|
++ }
|
|
3079
|
++
|
|
3080
|
++ if err == nil && !attached {
|
|
3081
|
++ // Volume is already detached from node.
|
|
3082
|
++ glog.V(4).Infof("detach operation was successful. persistent disk %q is already detached " +
|
|
3083
|
++ "from node %q.", diskID, hostName)
|
|
3084
|
++ return nil
|
|
3085
|
++ }
|
|
3086
|
++
|
|
3087
|
++ if err := detacher.cascadeDisks.DetachDisk(diskID, nodeName); err != nil {
|
|
3088
|
++ glog.Errorf("Error detaching volume %q: %v", diskID, err)
|
|
3089
|
++ return err
|
|
3090
|
++ }
|
|
3091
|
++ return nil
|
|
3092
|
++}
|
|
3093
|
++
|
|
3094
|
++// WaitForDetach waits for the devicePath to become unavailable.
|
|
3095
|
++func (detacher *cascadeDiskDetacher) WaitForDetach(devicePath string, timeout time.Duration) error {
|
|
3096
|
++ ticker := time.NewTicker(checkSleepDuration)
|
|
3097
|
++ defer ticker.Stop()
|
|
3098
|
++ timer := time.NewTimer(timeout)
|
|
3099
|
++ defer timer.Stop()
|
|
3100
|
++
|
|
3101
|
++ for {
|
|
3102
|
++ select {
|
|
3103
|
++ case <-ticker.C:
|
|
3104
|
++ glog.V(4).Infof("Checking device %q is detached.", devicePath)
|
|
3105
|
++ if pathExists, err := volumeutil.PathExists(devicePath); err != nil {
|
|
3106
|
++ return fmt.Errorf("Error checking if device path exists: %v", err)
|
|
3107
|
++ } else if !pathExists {
|
|
3108
|
++ return nil
|
|
3109
|
++ }
|
|
3110
|
++ case <-timer.C:
|
|
3111
|
++ return fmt.Errorf("Timeout reached; Device %v is still attached", devicePath)
|
|
3112
|
++ }
|
|
3113
|
++ }
|
|
3114
|
++}
|
|
3115
|
++
|
|
3116
|
++// UnmountDevice unmounts the disk specified by the device mount path.
|
|
3117
|
++func (detacher *cascadeDiskDetacher) UnmountDevice(deviceMountPath string) error {
|
|
3118
|
++ return volumeutil.UnmountPath(deviceMountPath, detacher.mounter)
|
|
3119
|
++}
|
|
3120
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/volume/cascade_disk/BUILD cascade-kubernetes/pkg/volume/cascade_disk/BUILD
|
|
3121
|
+--- kubernetes-1.8.1/pkg/volume/cascade_disk/BUILD 1970-01-01 00:00:00.000000000 +0000
|
|
3122
|
+@@ -0,0 +1,43 @@
|
|
3123
|
++package(default_visibility = ["//visibility:public"])
|
|
3124
|
++
|
|
3125
|
++load(
|
|
3126
|
++ "@io_bazel_rules_go//go:def.bzl",
|
|
3127
|
++ "go_library",
|
|
3128
|
++ "go_test",
|
|
3129
|
++)
|
|
3130
|
++
|
|
3131
|
++go_library(
|
|
3132
|
++ name = "go_default_library",
|
|
3133
|
++ srcs = [
|
|
3134
|
++ "attacher.go",
|
|
3135
|
++ "cascade_disk.go",
|
|
3136
|
++ "cascade_util.go",
|
|
3137
|
++ ],
|
|
3138
|
++ deps = [
|
|
3139
|
++ "//pkg/cloudprovider:go_default_library",
|
|
3140
|
++ "//pkg/cloudprovider/providers/cascade:go_default_library",
|
|
3141
|
++ "//pkg/util/mount:go_default_library",
|
|
3142
|
++ "//pkg/util/strings:go_default_library",
|
|
3143
|
++ "//pkg/volume:go_default_library",
|
|
3144
|
++ "//pkg/volume/util:go_default_library",
|
|
3145
|
++ "//pkg/volume/util/volumehelper:go_default_library",
|
|
3146
|
++ "//vendor/github.com/golang/glog:go_default_library",
|
|
3147
|
++ "//vendor/k8s.io/api/core/v1:go_default_library",
|
|
3148
|
++ "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
|
3149
|
++ "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
|
3150
|
++ "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
|
3151
|
++ ],
|
|
3152
|
++)
|
|
3153
|
++
|
|
3154
|
++filegroup(
|
|
3155
|
++ name = "package-srcs",
|
|
3156
|
++ srcs = glob(["**"]),
|
|
3157
|
++ tags = ["automanaged"],
|
|
3158
|
++ visibility = ["//visibility:private"],
|
|
3159
|
++)
|
|
3160
|
++
|
|
3161
|
++filegroup(
|
|
3162
|
++ name = "all-srcs",
|
|
3163
|
++ srcs = [":package-srcs"],
|
|
3164
|
++ tags = ["automanaged"],
|
|
3165
|
++)
|
|
3166
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/volume/cascade_disk/cascade_disk.go cascade-kubernetes/pkg/volume/cascade_disk/cascade_disk.go
|
|
3167
|
+--- kubernetes-1.8.1/pkg/volume/cascade_disk/cascade_disk.go 1970-01-01 00:00:00.000000000 +0000
|
|
3168
|
+@@ -0,0 +1,391 @@
|
|
3169
|
++package cascade_disk
|
|
3170
|
++
|
|
3171
|
++import (
|
|
3172
|
++ "fmt"
|
|
3173
|
++ "os"
|
|
3174
|
++ "path"
|
|
3175
|
++
|
|
3176
|
++ "github.com/golang/glog"
|
|
3177
|
++ "k8s.io/api/core/v1"
|
|
3178
|
++ "k8s.io/apimachinery/pkg/api/resource"
|
|
3179
|
++ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
3180
|
++ "k8s.io/apimachinery/pkg/types"
|
|
3181
|
++ "k8s.io/kubernetes/pkg/util/mount"
|
|
3182
|
++ utilstrings "k8s.io/kubernetes/pkg/util/strings"
|
|
3183
|
++ "k8s.io/kubernetes/pkg/volume"
|
|
3184
|
++ "k8s.io/kubernetes/pkg/volume/util"
|
|
3185
|
++ "k8s.io/kubernetes/pkg/volume/util/volumehelper"
|
|
3186
|
++)
|
|
3187
|
++
|
|
3188
|
++// This is the primary entrypoint for volume plugins.
|
|
3189
|
++func ProbeVolumePlugins() []volume.VolumePlugin {
|
|
3190
|
++ return []volume.VolumePlugin{&cascadeDiskPlugin{}}
|
|
3191
|
++}
|
|
3192
|
++
|
|
3193
|
++type cascadeDiskPlugin struct {
|
|
3194
|
++ host volume.VolumeHost
|
|
3195
|
++}
|
|
3196
|
++
|
|
3197
|
++var _ volume.VolumePlugin = &cascadeDiskPlugin{}
|
|
3198
|
++var _ volume.PersistentVolumePlugin = &cascadeDiskPlugin{}
|
|
3199
|
++var _ volume.DeletableVolumePlugin = &cascadeDiskPlugin{}
|
|
3200
|
++var _ volume.ProvisionableVolumePlugin = &cascadeDiskPlugin{}
|
|
3201
|
++
|
|
3202
|
++const (
|
|
3203
|
++ cascadeDiskPluginName = "kubernetes.io/cascade-disk"
|
|
3204
|
++)
|
|
3205
|
++
|
|
3206
|
++// Init initializes the Cascade volume plugin.
|
|
3207
|
++func (plugin *cascadeDiskPlugin) Init(host volume.VolumeHost) error {
|
|
3208
|
++ plugin.host = host
|
|
3209
|
++ return nil
|
|
3210
|
++}
|
|
3211
|
++
|
|
3212
|
++// GetPluginName returns the name of the Cascade volume plugin.
|
|
3213
|
++func (plugin *cascadeDiskPlugin) GetPluginName() string {
|
|
3214
|
++ return cascadeDiskPluginName
|
|
3215
|
++}
|
|
3216
|
++
|
|
3217
|
++// GetVolumeName returns the name of the volume which is the diskID in our case.
|
|
3218
|
++func (plugin *cascadeDiskPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
|
|
3219
|
++ volumeSource, _, err := getVolumeSource(spec)
|
|
3220
|
++ if err != nil {
|
|
3221
|
++ glog.Errorf("Cascade volume plugin: GetVolumeName failed to get volume source")
|
|
3222
|
++ return "", err
|
|
3223
|
++ }
|
|
3224
|
++
|
|
3225
|
++ return volumeSource.DiskID, nil
|
|
3226
|
++}
|
|
3227
|
++
|
|
3228
|
++// CanSupport specifies whether the Cascade volume plguin can support the specific resource type.
|
|
3229
|
++// Cascade plugin only supports the persistent volume and volume resource which has the Cascade disk annotation.
|
|
3230
|
++func (plugin *cascadeDiskPlugin) CanSupport(spec *volume.Spec) bool {
|
|
3231
|
++ return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.CascadeDisk != nil) ||
|
|
3232
|
++ (spec.Volume != nil && spec.Volume.CascadeDisk != nil)
|
|
3233
|
++}
|
|
3234
|
++
|
|
3235
|
++// RequiresRemount specifies whether remount is required for the disk.
|
|
3236
|
++func (plugin *cascadeDiskPlugin) RequiresRemount() bool {
|
|
3237
|
++ return false
|
|
3238
|
++}
|
|
3239
|
++
|
|
3240
|
++// SupportsMountOption specifies whether the Cascade volume plugin supports the mount operation.
|
|
3241
|
++func (plugin *cascadeDiskPlugin) SupportsMountOption() bool {
|
|
3242
|
++ return true
|
|
3243
|
++}
|
|
3244
|
++
|
|
3245
|
++// SupportsBulkVolumeVerification specifies whether bulk volume verification is supported.
|
|
3246
|
++func (plugin *cascadeDiskPlugin) SupportsBulkVolumeVerification() bool {
|
|
3247
|
++ return false
|
|
3248
|
++}
|
|
3249
|
++
|
|
3250
|
++// NewMounter returns the mounter associated with the Cascade volume plugin.
|
|
3251
|
++func (plugin *cascadeDiskPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod,
|
|
3252
|
++ _ volume.VolumeOptions) (volume.Mounter, error) {
|
|
3253
|
++ return plugin.newMounterInternal(spec, pod.UID, &CascadeDiskUtil{}, plugin.host.GetMounter(plugin.GetPluginName()))
|
|
3254
|
++}
|
|
3255
|
++
|
|
3256
|
++// NewUnmounter returns the unmounter associated with the Cascade volume plugin.
|
|
3257
|
++func (plugin *cascadeDiskPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
|
|
3258
|
++ return plugin.newUnmounterInternal(volName, podUID, &CascadeDiskUtil{},
|
|
3259
|
++ plugin.host.GetMounter(plugin.GetPluginName()))
|
|
3260
|
++}
|
|
3261
|
++
|
|
3262
|
++func (plugin *cascadeDiskPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager diskManager,
|
|
3263
|
++ mounter mount.Interface) (volume.Mounter, error) {
|
|
3264
|
++ volumeSource, _, err := getVolumeSource(spec)
|
|
3265
|
++ if err != nil {
|
|
3266
|
++ glog.Errorf("Cascade volume plugin: newMounterInternal failed to get volume source")
|
|
3267
|
++ return nil, err
|
|
3268
|
++ }
|
|
3269
|
++
|
|
3270
|
++ diskID := volumeSource.DiskID
|
|
3271
|
++ fsType := volumeSource.FSType
|
|
3272
|
++
|
|
3273
|
++ return &cascadeDiskMounter{
|
|
3274
|
++ cascadeDisk: &cascadeDisk{
|
|
3275
|
++ podUID: podUID,
|
|
3276
|
++ volName: spec.Name(),
|
|
3277
|
++ diskID: diskID,
|
|
3278
|
++ manager: manager,
|
|
3279
|
++ mounter: mounter,
|
|
3280
|
++ plugin: plugin,
|
|
3281
|
++ },
|
|
3282
|
++ fsType: fsType,
|
|
3283
|
++ diskMounter: volumehelper.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host)}, nil
|
|
3284
|
++}
|
|
3285
|
++
|
|
3286
|
++func (plugin *cascadeDiskPlugin) newUnmounterInternal(volName string, podUID types.UID, manager diskManager,
|
|
3287
|
++ mounter mount.Interface) (volume.Unmounter, error) {
|
|
3288
|
++ return &cascadeDiskUnmounter{
|
|
3289
|
++ &cascadeDisk{
|
|
3290
|
++ podUID: podUID,
|
|
3291
|
++ volName: volName,
|
|
3292
|
++ manager: manager,
|
|
3293
|
++ mounter: mounter,
|
|
3294
|
++ plugin: plugin,
|
|
3295
|
++ }}, nil
|
|
3296
|
++}
|
|
3297
|
++
|
|
3298
|
++// ConstructVolumeSpec constructs a Cascade volume spec based on the name and mount path.
|
|
3299
|
++func (plugin *cascadeDiskPlugin) ConstructVolumeSpec(volumeSpecName, mountPath string) (*volume.Spec, error) {
|
|
3300
|
++ mounter := plugin.host.GetMounter(plugin.GetPluginName())
|
|
3301
|
++ pluginDir := plugin.host.GetPluginDir(plugin.GetPluginName())
|
|
3302
|
++ diskID, err := mounter.GetDeviceNameFromMount(mountPath, pluginDir)
|
|
3303
|
++ if err != nil {
|
|
3304
|
++ return nil, err
|
|
3305
|
++ }
|
|
3306
|
++
|
|
3307
|
++ cascadeDisk := &v1.Volume{
|
|
3308
|
++ Name: volumeSpecName,
|
|
3309
|
++ VolumeSource: v1.VolumeSource{
|
|
3310
|
++ CascadeDisk: &v1.CascadeDiskVolumeSource{
|
|
3311
|
++ DiskID: diskID,
|
|
3312
|
++ },
|
|
3313
|
++ },
|
|
3314
|
++ }
|
|
3315
|
++ return volume.NewSpecFromVolume(cascadeDisk), nil
|
|
3316
|
++}
|
|
3317
|
++
|
|
3318
|
++// Abstract interface to disk operations.
|
|
3319
|
++type diskManager interface {
|
|
3320
|
++ // Creates a volume
|
|
3321
|
++ CreateVolume(provisioner *cascadeDiskProvisioner) (diskID string, volumeSizeGB int, fstype string, err error)
|
|
3322
|
++ // Deletes a volume
|
|
3323
|
++ DeleteVolume(deleter *cascadeDiskDeleter) error
|
|
3324
|
++}
|
|
3325
|
++
|
|
3326
|
++// cascadeDisk volumes are disk resources attached to the kubelet's host machine and exposed to the pod.
|
|
3327
|
++type cascadeDisk struct {
|
|
3328
|
++ volName string
|
|
3329
|
++ podUID types.UID
|
|
3330
|
++ diskID string
|
|
3331
|
++ fsType string
|
|
3332
|
++ manager diskManager
|
|
3333
|
++ mounter mount.Interface
|
|
3334
|
++ plugin *cascadeDiskPlugin
|
|
3335
|
++ volume.MetricsNil
|
|
3336
|
++}
|
|
3337
|
++
|
|
3338
|
++var _ volume.Mounter = &cascadeDiskMounter{}
|
|
3339
|
++
|
|
3340
|
++type cascadeDiskMounter struct {
|
|
3341
|
++ *cascadeDisk
|
|
3342
|
++ fsType string
|
|
3343
|
++ diskMounter *mount.SafeFormatAndMount
|
|
3344
|
++}
|
|
3345
|
++
|
|
3346
|
++// GetAttributes returns the attributes associated with a Cascade disk.
|
|
3347
|
++func (b *cascadeDiskMounter) GetAttributes() volume.Attributes {
|
|
3348
|
++ return volume.Attributes{
|
|
3349
|
++ SupportsSELinux: true,
|
|
3350
|
++ }
|
|
3351
|
++}
|
|
3352
|
++
|
|
3353
|
++// CanMount checks prior to mount operations to verify that the required components (binaries, etc.) to mount the
|
|
3354
|
++// volume are available on the underlying node. If not, it returns an error.
|
|
3355
|
++func (b *cascadeDiskMounter) CanMount() error {
|
|
3356
|
++ return nil
|
|
3357
|
++}
|
|
3358
|
++
|
|
3359
|
++// SetUp attaches the disk and bind mounts to the volume path.
|
|
3360
|
++func (b *cascadeDiskMounter) SetUp(fsGroup *int64) error {
|
|
3361
|
++ return b.SetUpAt(b.GetPath(), fsGroup)
|
|
3362
|
++}
|
|
3363
|
++
|
|
3364
|
++// SetUpAt attaches the disk and bind mounts to the volume path.
|
|
3365
|
++func (b *cascadeDiskMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|
3366
|
++ glog.V(4).Infof("Cascade Persistent Disk setup %s to %s", b.diskID, dir)
|
|
3367
|
++
|
|
3368
|
++ // TODO: handle failed mounts here.
|
|
3369
|
++ notmnt, err := b.mounter.IsLikelyNotMountPoint(dir)
|
|
3370
|
++ if err != nil && !os.IsNotExist(err) {
|
|
3371
|
++ glog.Errorf("cannot validate mount point: %s %v", dir, err)
|
|
3372
|
++ return err
|
|
3373
|
++ }
|
|
3374
|
++ if !notmnt {
|
|
3375
|
++ return nil
|
|
3376
|
++ }
|
|
3377
|
++
|
|
3378
|
++ if err := os.MkdirAll(dir, 0750); err != nil {
|
|
3379
|
++ glog.Errorf("mkdir failed on disk %s (%v)", dir, err)
|
|
3380
|
++ return err
|
|
3381
|
++ }
|
|
3382
|
++
|
|
3383
|
++ options := []string{"bind"}
|
|
3384
|
++
|
|
3385
|
++ // Perform a bind mount to the full path to allow duplicate mounts of the same PD.
|
|
3386
|
++ globalPDPath := makeGlobalPDPath(b.plugin.host, b.diskID)
|
|
3387
|
++ glog.V(4).Infof("attempting to mount %s", dir)
|
|
3388
|
++
|
|
3389
|
++ err = b.mounter.Mount(globalPDPath, dir, "", options)
|
|
3390
|
++ if err != nil {
|
|
3391
|
++ notmnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
|
|
3392
|
++ if mntErr != nil {
|
|
3393
|
++ glog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
|
|
3394
|
++ return err
|
|
3395
|
++ }
|
|
3396
|
++ if !notmnt {
|
|
3397
|
++ if mntErr = b.mounter.Unmount(dir); mntErr != nil {
|
|
3398
|
++ glog.Errorf("Failed to unmount: %v", mntErr)
|
|
3399
|
++ return err
|
|
3400
|
++ }
|
|
3401
|
++ notmnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
|
|
3402
|
++ if mntErr != nil {
|
|
3403
|
++ glog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
|
|
3404
|
++ return err
|
|
3405
|
++ }
|
|
3406
|
++ if !notmnt {
|
|
3407
|
++ glog.Errorf("%s is still mounted, despite call to unmount(). Will try again next sync loop.",
|
|
3408
|
++ b.GetPath())
|
|
3409
|
++ return err
|
|
3410
|
++ }
|
|
3411
|
++ }
|
|
3412
|
++ os.Remove(dir)
|
|
3413
|
++ glog.Errorf("Mount of disk %s failed: %v", dir, err)
|
|
3414
|
++ return err
|
|
3415
|
++ }
|
|
3416
|
++ volume.SetVolumeOwnership(b, fsGroup)
|
|
3417
|
++
|
|
3418
|
++ return nil
|
|
3419
|
++}
|
|
3420
|
++
|
|
3421
|
++var _ volume.Unmounter = &cascadeDiskUnmounter{}
|
|
3422
|
++
|
|
3423
|
++type cascadeDiskUnmounter struct {
|
|
3424
|
++ *cascadeDisk
|
|
3425
|
++}
|
|
3426
|
++
|
|
3427
|
++// TearDown unmounts the bind mount, and detaches the disk only if the disk resource was the last reference to that
|
|
3428
|
++// disk on the kubelet.
|
|
3429
|
++func (c *cascadeDiskUnmounter) TearDown() error {
|
|
3430
|
++ return c.TearDownAt(c.GetPath())
|
|
3431
|
++}
|
|
3432
|
++
|
|
3433
|
++// TearDownAt unmounts the bind mount, and detaches the disk only if the disk resource was the last reference to that
|
|
3434
|
++// disk on the kubelet.
|
|
3435
|
++func (c *cascadeDiskUnmounter) TearDownAt(dir string) error {
|
|
3436
|
++ return util.UnmountPath(dir, c.mounter)
|
|
3437
|
++}
|
|
3438
|
++
|
|
3439
|
++func makeGlobalPDPath(host volume.VolumeHost, diskID string) string {
|
|
3440
|
++ return path.Join(host.GetPluginDir(cascadeDiskPluginName), mount.MountsInGlobalPDPath, diskID)
|
|
3441
|
++}
|
|
3442
|
++
|
|
3443
|
++func (cd *cascadeDisk) GetPath() string {
|
|
3444
|
++ name := cascadeDiskPluginName
|
|
3445
|
++ return cd.plugin.host.GetPodVolumeDir(cd.podUID, utilstrings.EscapeQualifiedNameForDisk(name), cd.volName)
|
|
3446
|
++}
|
|
3447
|
++
|
|
3448
|
++func (plugin *cascadeDiskPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
|
|
3449
|
++ return []v1.PersistentVolumeAccessMode{
|
|
3450
|
++ v1.ReadWriteOnce,
|
|
3451
|
++ }
|
|
3452
|
++}
|
|
3453
|
++
|
|
3454
|
++type cascadeDiskDeleter struct {
|
|
3455
|
++ *cascadeDisk
|
|
3456
|
++}
|
|
3457
|
++
|
|
3458
|
++var _ volume.Deleter = &cascadeDiskDeleter{}
|
|
3459
|
++
|
|
3460
|
++// NewDeleter returns the deleter associated with the Cascade volume plugin.
|
|
3461
|
++func (plugin *cascadeDiskPlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) {
|
|
3462
|
++ return plugin.newDeleterInternal(spec, &CascadeDiskUtil{})
|
|
3463
|
++}
|
|
3464
|
++
|
|
3465
|
++func (plugin *cascadeDiskPlugin) newDeleterInternal(spec *volume.Spec, manager diskManager) (volume.Deleter, error) {
|
|
3466
|
++ if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.CascadeDisk == nil {
|
|
3467
|
++ return nil, fmt.Errorf("spec.PersistentVolumeSource.CascadeDisk is nil")
|
|
3468
|
++ }
|
|
3469
|
++ return &cascadeDiskDeleter{
|
|
3470
|
++ &cascadeDisk{
|
|
3471
|
++ volName: spec.Name(),
|
|
3472
|
++ diskID: spec.PersistentVolume.Spec.CascadeDisk.DiskID,
|
|
3473
|
++ manager: manager,
|
|
3474
|
++ plugin: plugin,
|
|
3475
|
++ }}, nil
|
|
3476
|
++}
|
|
3477
|
++
|
|
3478
|
++func (r *cascadeDiskDeleter) Delete() error {
|
|
3479
|
++ return r.manager.DeleteVolume(r)
|
|
3480
|
++}
|
|
3481
|
++
|
|
3482
|
++type cascadeDiskProvisioner struct {
|
|
3483
|
++ *cascadeDisk
|
|
3484
|
++ options volume.VolumeOptions
|
|
3485
|
++}
|
|
3486
|
++
|
|
3487
|
++var _ volume.Provisioner = &cascadeDiskProvisioner{}
|
|
3488
|
++
|
|
3489
|
++// NewProvisioner returns the provisioner associated with the Cascade volume plugin.
|
|
3490
|
++func (plugin *cascadeDiskPlugin) NewProvisioner(options volume.VolumeOptions) (volume.Provisioner, error) {
|
|
3491
|
++ return plugin.newProvisionerInternal(options, &CascadeDiskUtil{})
|
|
3492
|
++}
|
|
3493
|
++
|
|
3494
|
++func (plugin *cascadeDiskPlugin) newProvisionerInternal(options volume.VolumeOptions,
|
|
3495
|
++ manager diskManager) (volume.Provisioner, error) {
|
|
3496
|
++ return &cascadeDiskProvisioner{
|
|
3497
|
++ cascadeDisk: &cascadeDisk{
|
|
3498
|
++ manager: manager,
|
|
3499
|
++ plugin: plugin,
|
|
3500
|
++ },
|
|
3501
|
++ options: options,
|
|
3502
|
++ }, nil
|
|
3503
|
++}
|
|
3504
|
++
|
|
3505
|
++// Provision provisions the persistent volume by making a CreateDisk call to Cascade Controller.
|
|
3506
|
++func (p *cascadeDiskProvisioner) Provision() (*v1.PersistentVolume, error) {
|
|
3507
|
++ if !volume.AccessModesContainedInAll(p.plugin.GetAccessModes(), p.options.PVC.Spec.AccessModes) {
|
|
3508
|
++ return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported",
|
|
3509
|
++ p.options.PVC.Spec.AccessModes, p.plugin.GetAccessModes())
|
|
3510
|
++ }
|
|
3511
|
++
|
|
3512
|
++ diskID, sizeGB, fstype, err := p.manager.CreateVolume(p)
|
|
3513
|
++ if err != nil {
|
|
3514
|
++ return nil, err
|
|
3515
|
++ }
|
|
3516
|
++
|
|
3517
|
++ if fstype == "" {
|
|
3518
|
++ fstype = "ext4"
|
|
3519
|
++ }
|
|
3520
|
++
|
|
3521
|
++ pv := &v1.PersistentVolume{
|
|
3522
|
++ ObjectMeta: metav1.ObjectMeta{
|
|
3523
|
++ Name: p.options.PVName,
|
|
3524
|
++ Labels: map[string]string{},
|
|
3525
|
++ Annotations: map[string]string{
|
|
3526
|
++ volumehelper.VolumeDynamicallyCreatedByKey: "cascade-volume-dynamic-provisioner",
|
|
3527
|
++ },
|
|
3528
|
++ },
|
|
3529
|
++ Spec: v1.PersistentVolumeSpec{
|
|
3530
|
++ PersistentVolumeReclaimPolicy: p.options.PersistentVolumeReclaimPolicy,
|
|
3531
|
++ AccessModes: p.options.PVC.Spec.AccessModes,
|
|
3532
|
++ Capacity: v1.ResourceList{
|
|
3533
|
++ v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", sizeGB)),
|
|
3534
|
++ },
|
|
3535
|
++ PersistentVolumeSource: v1.PersistentVolumeSource{
|
|
3536
|
++ CascadeDisk: &v1.CascadeDiskVolumeSource{
|
|
3537
|
++ DiskID: diskID,
|
|
3538
|
++ FSType: fstype,
|
|
3539
|
++ },
|
|
3540
|
++ },
|
|
3541
|
++ MountOptions: p.options.MountOptions,
|
|
3542
|
++ },
|
|
3543
|
++ }
|
|
3544
|
++ if len(p.options.PVC.Spec.AccessModes) == 0 {
|
|
3545
|
++ pv.Spec.AccessModes = p.plugin.GetAccessModes()
|
|
3546
|
++ }
|
|
3547
|
++
|
|
3548
|
++ return pv, nil
|
|
3549
|
++}
|
|
3550
|
++
|
|
3551
|
++func getVolumeSource(spec *volume.Spec) (*v1.CascadeDiskVolumeSource, bool, error) {
|
|
3552
|
++ if spec.Volume != nil && spec.Volume.CascadeDisk != nil {
|
|
3553
|
++ return spec.Volume.CascadeDisk, spec.ReadOnly, nil
|
|
3554
|
++ } else if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.CascadeDisk != nil {
|
|
3555
|
++ return spec.PersistentVolume.Spec.CascadeDisk, spec.ReadOnly, nil
|
|
3556
|
++ }
|
|
3557
|
++
|
|
3558
|
++ return nil, false, fmt.Errorf("Spec does not reference a Cascade disk type")
|
|
3559
|
++}
|
|
3560
|
+\ No newline at end of file
|
|
3561
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/volume/cascade_disk/cascade_util.go cascade-kubernetes/pkg/volume/cascade_disk/cascade_util.go
|
|
3562
|
+--- kubernetes-1.8.1/pkg/volume/cascade_disk/cascade_util.go 1970-01-01 00:00:00.000000000 +0000
|
|
3563
|
+@@ -0,0 +1,107 @@
|
|
3564
|
++package cascade_disk
|
|
3565
|
++
|
|
3566
|
++import (
|
|
3567
|
++ "fmt"
|
|
3568
|
++ "strings"
|
|
3569
|
++ "time"
|
|
3570
|
++
|
|
3571
|
++ "github.com/golang/glog"
|
|
3572
|
++ "k8s.io/api/core/v1"
|
|
3573
|
++ "k8s.io/kubernetes/pkg/cloudprovider"
|
|
3574
|
++ "k8s.io/kubernetes/pkg/cloudprovider/providers/cascade"
|
|
3575
|
++ "k8s.io/kubernetes/pkg/volume"
|
|
3576
|
++ volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
|
3577
|
++)
|
|
3578
|
++
|
|
3579
|
++const (
|
|
3580
|
++ checkSleepDuration = time.Second
|
|
3581
|
++)
|
|
3582
|
++
|
|
3583
|
++type CascadeDiskUtil struct{}
|
|
3584
|
++
|
|
3585
|
++func verifyDevicePath(path string) (string, error) {
|
|
3586
|
++ if pathExists, err := volumeutil.PathExists(path); err != nil {
|
|
3587
|
++ return "", fmt.Errorf("Error checking if path exists: %v", err)
|
|
3588
|
++ } else if pathExists {
|
|
3589
|
++ return path, nil
|
|
3590
|
++ }
|
|
3591
|
++
|
|
3592
|
++ glog.V(4).Infof("verifyDevicePath: path does not exist yet")
|
|
3593
|
++ return "", nil
|
|
3594
|
++}
|
|
3595
|
++
|
|
3596
|
++// CreateVolume creates a Cascade persistent disk.
|
|
3597
|
++func (util *CascadeDiskUtil) CreateVolume(p *cascadeDiskProvisioner) (diskID string, capacityGB int, fstype string,
|
|
3598
|
++ err error) {
|
|
3599
|
++ cloud, err := getCloudProvider(p.plugin.host.GetCloudProvider())
|
|
3600
|
++ if err != nil {
|
|
3601
|
++ glog.Errorf("Cascade Util: CreateVolume failed to get cloud provider. Error [%v]", err)
|
|
3602
|
++ return "", 0, "", err
|
|
3603
|
++ }
|
|
3604
|
++
|
|
3605
|
++ capacity := p.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
|
|
3606
|
++ volSizeBytes := capacity.Value()
|
|
3607
|
++ // Cascade works with GB, convert to GB with rounding up
|
|
3608
|
++ volSizeGB := int(volume.RoundUpSize(volSizeBytes, 1024*1024*1024))
|
|
3609
|
++ name := volume.GenerateVolumeName(p.options.ClusterName, p.options.PVName, 255)
|
|
3610
|
++ volumeOptions := &cascade.VolumeOptions{
|
|
3611
|
++ CapacityGB: volSizeGB,
|
|
3612
|
++ Tags: *p.options.CloudTags,
|
|
3613
|
++ Name: name,
|
|
3614
|
++ }
|
|
3615
|
++
|
|
3616
|
++ for parameter, value := range p.options.Parameters {
|
|
3617
|
++ switch strings.ToLower(parameter) {
|
|
3618
|
++ case "flavor":
|
|
3619
|
++ volumeOptions.Flavor = value
|
|
3620
|
++ case volume.VolumeParameterFSType:
|
|
3621
|
++ fstype = value
|
|
3622
|
++ glog.V(4).Infof("Cascade Util: Setting fstype to %s", fstype)
|
|
3623
|
++ default:
|
|
3624
|
++ glog.Errorf("Cascade Util: invalid option %s for volume plugin %s.", parameter,
|
|
3625
|
++ p.plugin.GetPluginName())
|
|
3626
|
++ return "", 0, "", fmt.Errorf("Cascade Util: invalid option %s for volume plugin %s.", parameter,
|
|
3627
|
++ p.plugin.GetPluginName())
|
|
3628
|
++ }
|
|
3629
|
++ }
|
|
3630
|
++
|
|
3631
|
++ diskID, err = cloud.CreateDisk(volumeOptions)
|
|
3632
|
++ if err != nil {
|
|
3633
|
++ glog.Errorf("Cascade Util: failed to CreateDisk. Error [%v]", err)
|
|
3634
|
++ return "", 0, "", err
|
|
3635
|
++ }
|
|
3636
|
++
|
|
3637
|
++ glog.V(4).Infof("Successfully created Cascade persistent disk %s", name)
|
|
3638
|
++ return diskID, volSizeGB, "", nil
|
|
3639
|
++}
|
|
3640
|
++
|
|
3641
|
++// DeleteVolume deletes a Cascade volume.
|
|
3642
|
++func (util *CascadeDiskUtil) DeleteVolume(disk *cascadeDiskDeleter) error {
|
|
3643
|
++ cloud, err := getCloudProvider(disk.plugin.host.GetCloudProvider())
|
|
3644
|
++ if err != nil {
|
|
3645
|
++ glog.Errorf("Cascade Util: DeleteVolume failed to get cloud provider. Error [%v]", err)
|
|
3646
|
++ return err
|
|
3647
|
++ }
|
|
3648
|
++
|
|
3649
|
++ if err = cloud.DeleteDisk(disk.diskID); err != nil {
|
|
3650
|
++ glog.Errorf("Cascade Util: failed to DeleteDisk for diskID %s. Error [%v]", disk.diskID, err)
|
|
3651
|
++ return err
|
|
3652
|
++ }
|
|
3653
|
++
|
|
3654
|
++ glog.V(4).Infof("Successfully deleted Cascade persistent disk %s", disk.diskID)
|
|
3655
|
++ return nil
|
|
3656
|
++}
|
|
3657
|
++
|
|
3658
|
++func getCloudProvider(cloud cloudprovider.Interface) (*cascade.CascadeCloud, error) {
|
|
3659
|
++ if cloud == nil {
|
|
3660
|
++ glog.Errorf("Cascade Util: Cloud provider not initialized properly")
|
|
3661
|
++ return nil, fmt.Errorf("Cascade Util: Cloud provider not initialized properly")
|
|
3662
|
++ }
|
|
3663
|
++
|
|
3664
|
++ cc := cloud.(*cascade.CascadeCloud)
|
|
3665
|
++ if cc == nil {
|
|
3666
|
++ glog.Errorf("Invalid cloud provider: expected Cascade")
|
|
3667
|
++ return nil, fmt.Errorf("Invalid cloud provider: expected Cascade")
|
|
3668
|
++ }
|
|
3669
|
++ return cc, nil
|
|
3670
|
++}
|
|
3671
|
+diff -uNr --no-dereference kubernetes-1.8.1/pkg/volume/cascade_disk/OWNERS cascade-kubernetes/pkg/volume/cascade_disk/OWNERS
|
|
3672
|
+--- kubernetes-1.8.1/pkg/volume/cascade_disk/OWNERS 1970-01-01 00:00:00.000000000 +0000
|
|
3673
|
+@@ -0,0 +1,2 @@
|
|
3674
|
++maintainers:
|
|
3675
|
++- ashokc
|
|
3676
|
+diff -uNr --no-dereference kubernetes-1.8.1/plugin/pkg/admission/persistentvolume/label/admission.go cascade-kubernetes/plugin/pkg/admission/persistentvolume/label/admission.go
|
|
3677
|
+--- kubernetes-1.8.1/plugin/pkg/admission/persistentvolume/label/admission.go 2018-01-23 22:47:25.566819354 +0000
|
|
3678
|
+@@ -31,6 +31,7 @@
|
|
3679
|
+ kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
|
|
3680
|
+ kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
|
|
3681
|
+ vol "k8s.io/kubernetes/pkg/volume"
|
|
3682
|
++ "k8s.io/kubernetes/pkg/cloudprovider/providers/cascade"
|
|
3683
|
+ )
|
|
3684
|
+
|
|
3685
|
+ // Register registers a plugin
|
|
3686
|
+@@ -50,6 +51,7 @@
|
|
3687
|
+ ebsVolumes aws.Volumes
|
|
3688
|
+ cloudConfig []byte
|
|
3689
|
+ gceCloudProvider *gce.GCECloud
|
|
3690
|
++ cascadeDisks cascade.Disks
|
|
3691
|
+ }
|
|
3692
|
+
|
|
3693
|
+ var _ kubeapiserveradmission.WantsCloudConfig = &persistentVolumeLabel{}
|
|
3694
|
+@@ -101,6 +103,13 @@
|
|
3695
|
+ }
|
|
3696
|
+ volumeLabels = labels
|
|
3697
|
+ }
|
|
3698
|
++ if volume.Spec.CascadeDisk != nil {
|
|
3699
|
++ labels, err := l.findCascadeDiskLabels(volume)
|
|
3700
|
++ if err != nil {
|
|
3701
|
++ return admission.NewForbidden(a, fmt.Errorf("error querying Cascade volume %s: %v", volume.Spec.CascadeDisk.DiskID, err))
|
|
3702
|
++ }
|
|
3703
|
++ volumeLabels = labels
|
|
3704
|
++ }
|
|
3705
|
+
|
|
3706
|
+ if len(volumeLabels) != 0 {
|
|
3707
|
+ if volume.Labels == nil {
|
|
3708
|
+@@ -213,3 +222,48 @@
|
|
3709
|
+ }
|
|
3710
|
+ return l.gceCloudProvider, nil
|
|
3711
|
+ }
|
|
3712
|
++
|
|
3713
|
++func (l *persistentVolumeLabel) findCascadeDiskLabels(volume *api.PersistentVolume) (map[string]string, error) {
|
|
3714
|
++ // Ignore any volumes that are being provisioned
|
|
3715
|
++ if volume.Spec.CascadeDisk.DiskID == vol.ProvisionedVolumeName {
|
|
3716
|
++ return nil, nil
|
|
3717
|
++ }
|
|
3718
|
++ cascadeDisks, err := l.getCascadeDisks()
|
|
3719
|
++ if err != nil {
|
|
3720
|
++ return nil, err
|
|
3721
|
++ }
|
|
3722
|
++ if cascadeDisks == nil {
|
|
3723
|
++ return nil, fmt.Errorf("unable to build Cascade cloud provider for volumes")
|
|
3724
|
++ }
|
|
3725
|
++
|
|
3726
|
++ labels, err := cascadeDisks.GetVolumeLabels(volume.Spec.CascadeDisk.DiskID)
|
|
3727
|
++ if err != nil {
|
|
3728
|
++ return nil, err
|
|
3729
|
++ }
|
|
3730
|
++
|
|
3731
|
++ return labels, nil
|
|
3732
|
++}
|
|
3733
|
++
|
|
3734
|
++// getCascadeDisks returns the Cascade Disks interface
|
|
3735
|
++func (l *persistentVolumeLabel) getCascadeDisks() (cascade.Disks, error) {
|
|
3736
|
++ l.mutex.Lock()
|
|
3737
|
++ defer l.mutex.Unlock()
|
|
3738
|
++
|
|
3739
|
++ if l.cascadeDisks == nil {
|
|
3740
|
++ var cloudConfigReader io.Reader
|
|
3741
|
++ if len(l.cloudConfig) > 0 {
|
|
3742
|
++ cloudConfigReader = bytes.NewReader(l.cloudConfig)
|
|
3743
|
++ }
|
|
3744
|
++ cloudProvider, err := cloudprovider.GetCloudProvider("cascade", cloudConfigReader)
|
|
3745
|
++ if err != nil || cloudProvider == nil {
|
|
3746
|
++ return nil, err
|
|
3747
|
++ }
|
|
3748
|
++ provider, ok := cloudProvider.(*cascade.CascadeCloud)
|
|
3749
|
++ if !ok {
|
|
3750
|
++ // GetCloudProvider has gone very wrong
|
|
3751
|
++ return nil, fmt.Errorf("error retrieving Cascade cloud provider")
|
|
3752
|
++ }
|
|
3753
|
++ l.cascadeDisks = provider
|
|
3754
|
++ }
|
|
3755
|
++ return l.cascadeDisks, nil
|
|
3756
|
++}
|
|
3757
|
+\ No newline at end of file
|
|
3758
|
+diff -uNr --no-dereference kubernetes-1.8.1/staging/src/k8s.io/api/core/v1/generated.pb.go cascade-kubernetes/staging/src/k8s.io/api/core/v1/generated.pb.go
|
|
3759
|
+--- kubernetes-1.8.1/staging/src/k8s.io/api/core/v1/generated.pb.go 2018-01-23 22:47:25.594819355 +0000
|
|
3760
|
+@@ -34,6 +34,7 @@
|
|
3761
|
+ AzureFileVolumeSource
|
|
3762
|
+ Binding
|
|
3763
|
+ Capabilities
|
|
3764
|
++ CascadeDiskVolumeSource
|
|
3765
|
+ CephFSPersistentVolumeSource
|
|
3766
|
+ CephFSVolumeSource
|
|
3767
|
+ CinderVolumeSource
|
|
3768
|
+@@ -992,6 +993,12 @@
|
|
3769
|
+ return fileDescriptorGenerated, []int{177}
|
|
3770
|
+ }
|
|
3771
|
+
|
|
3772
|
++func (m *CascadeDiskVolumeSource) Reset() { *m = CascadeDiskVolumeSource{} }
|
|
3773
|
++func (*CascadeDiskVolumeSource) ProtoMessage() {}
|
|
3774
|
++func (*CascadeDiskVolumeSource) Descriptor() ([]byte, []int) {
|
|
3775
|
++ return fileDescriptorGenerated, []int{178}
|
|
3776
|
++}
|
|
3777
|
++
|
|
3778
|
+ func init() {
|
|
3779
|
+ proto.RegisterType((*AWSElasticBlockStoreVolumeSource)(nil), "k8s.io.api.core.v1.AWSElasticBlockStoreVolumeSource")
|
|
3780
|
+ proto.RegisterType((*Affinity)(nil), "k8s.io.api.core.v1.Affinity")
|
|
3781
|
+@@ -1002,6 +1009,7 @@
|
|
3782
|
+ proto.RegisterType((*AzureFileVolumeSource)(nil), "k8s.io.api.core.v1.AzureFileVolumeSource")
|
|
3783
|
+ proto.RegisterType((*Binding)(nil), "k8s.io.api.core.v1.Binding")
|
|
3784
|
+ proto.RegisterType((*Capabilities)(nil), "k8s.io.api.core.v1.Capabilities")
|
|
3785
|
++ proto.RegisterType((*CascadeDiskVolumeSource)(nil), "k8s.io.api.core.v1.CascadeDiskVolumeSource")
|
|
3786
|
+ proto.RegisterType((*CephFSPersistentVolumeSource)(nil), "k8s.io.api.core.v1.CephFSPersistentVolumeSource")
|
|
3787
|
+ proto.RegisterType((*CephFSVolumeSource)(nil), "k8s.io.api.core.v1.CephFSVolumeSource")
|
|
3788
|
+ proto.RegisterType((*CinderVolumeSource)(nil), "k8s.io.api.core.v1.CinderVolumeSource")
|
|
3789
|
+@@ -1523,6 +1531,32 @@
|
|
3790
|
+ return i, nil
|
|
3791
|
+ }
|
|
3792
|
+
|
|
3793
|
++func (m *CascadeDiskVolumeSource) Marshal() (dAtA []byte, err error) {
|
|
3794
|
++ size := m.Size()
|
|
3795
|
++ dAtA = make([]byte, size)
|
|
3796
|
++ n, err := m.MarshalTo(dAtA)
|
|
3797
|
++ if err != nil {
|
|
3798
|
++ return nil, err
|
|
3799
|
++ }
|
|
3800
|
++ return dAtA[:n], nil
|
|
3801
|
++}
|
|
3802
|
++
|
|
3803
|
++func (m *CascadeDiskVolumeSource) MarshalTo(dAtA []byte) (int, error) {
|
|
3804
|
++ var i int
|
|
3805
|
++ _ = i
|
|
3806
|
++ var l int
|
|
3807
|
++ _ = l
|
|
3808
|
++ dAtA[i] = 0xa
|
|
3809
|
++ i++
|
|
3810
|
++ i = encodeVarintGenerated(dAtA, i, uint64(len(m.DiskID)))
|
|
3811
|
++ i += copy(dAtA[i:], m.DiskID)
|
|
3812
|
++ dAtA[i] = 0x12
|
|
3813
|
++ i++
|
|
3814
|
++ i = encodeVarintGenerated(dAtA, i, uint64(len(m.FSType)))
|
|
3815
|
++ i += copy(dAtA[i:], m.FSType)
|
|
3816
|
++ return i, nil
|
|
3817
|
++}
|
|
3818
|
++
|
|
3819
|
+ func (m *CephFSPersistentVolumeSource) Marshal() (dAtA []byte, err error) {
|
|
3820
|
+ size := m.Size()
|
|
3821
|
+ dAtA = make([]byte, size)
|
|
3822
|
+@@ -6080,6 +6114,18 @@
|
|
3823
|
+ }
|
|
3824
|
+ i += n121
|
|
3825
|
+ }
|
|
3826
|
++ if m.CascadeDisk != nil {
|
|
3827
|
++ dAtA[i] = 0xb2
|
|
3828
|
++ i++
|
|
3829
|
++ dAtA[i] = 0x1
|
|
3830
|
++ i++
|
|
3831
|
++ i = encodeVarintGenerated(dAtA, i, uint64(m.CascadeDisk.Size()))
|
|
3832
|
++ n122, err := m.CascadeDisk.MarshalTo(dAtA[i:])
|
|
3833
|
++ if err != nil {
|
|
3834
|
++ return 0, err
|
|
3835
|
++ }
|
|
3836
|
++ i += n122
|
|
3837
|
++ }
|
|
3838
|
+ return i, nil
|
|
3839
|
+ }
|
|
3840
|
+
|
|
3841
|
+@@ -9824,6 +9870,18 @@
|
|
3842
|
+ }
|
|
3843
|
+ i += n220
|
|
3844
|
+ }
|
|
3845
|
++ if m.CascadeDisk != nil {
|
|
3846
|
++ dAtA[i] = 0xe2
|
|
3847
|
++ i++
|
|
3848
|
++ dAtA[i] = 0x1
|
|
3849
|
++ i++
|
|
3850
|
++ i = encodeVarintGenerated(dAtA, i, uint64(m.CascadeDisk.Size()))
|
|
3851
|
++ n221, err := m.CascadeDisk.MarshalTo(dAtA[i:])
|
|
3852
|
++ if err != nil {
|
|
3853
|
++ return 0, err
|
|
3854
|
++ }
|
|
3855
|
++ i += n221
|
|
3856
|
++ }
|
|
3857
|
+ return i, nil
|
|
3858
|
+ }
|
|
3859
|
+
|
|
3860
|
+@@ -10048,6 +10106,16 @@
|
|
3861
|
+ return n
|
|
3862
|
+ }
|
|
3863
|
+
|
|
3864
|
++func (m *CascadeDiskVolumeSource) Size() (n int) {
|
|
3865
|
++ var l int
|
|
3866
|
++ _ = l
|
|
3867
|
++ l = len(m.DiskID)
|
|
3868
|
++ n += 1 + l + sovGenerated(uint64(l))
|
|
3869
|
++ l = len(m.FSType)
|
|
3870
|
++ n += 1 + l + sovGenerated(uint64(l))
|
|
3871
|
++ return n
|
|
3872
|
++}
|
|
3873
|
++
|
|
3874
|
+ func (m *CephFSPersistentVolumeSource) Size() (n int) {
|
|
3875
|
+ var l int
|
|
3876
|
+ _ = l
|
|
3877
|
+@@ -11711,6 +11779,10 @@
|
|
3878
|
+ l = m.StorageOS.Size()
|
|
3879
|
+ n += 2 + l + sovGenerated(uint64(l))
|
|
3880
|
+ }
|
|
3881
|
++ if m.CascadeDisk != nil {
|
|
3882
|
++ l = m.CascadeDisk.Size()
|
|
3883
|
++ n += 2 + l + sovGenerated(uint64(l))
|
|
3884
|
++ }
|
|
3885
|
+ return n
|
|
3886
|
+ }
|
|
3887
|
+
|
|
3888
|
+@@ -13055,6 +13127,10 @@
|
|
3889
|
+ l = m.StorageOS.Size()
|
|
3890
|
+ n += 2 + l + sovGenerated(uint64(l))
|
|
3891
|
+ }
|
|
3892
|
++ if m.CascadeDisk != nil {
|
|
3893
|
++ l = m.CascadeDisk.Size()
|
|
3894
|
++ n += 2 + l + sovGenerated(uint64(l))
|
|
3895
|
++ }
|
|
3896
|
+ return n
|
|
3897
|
+ }
|
|
3898
|
+
|
|
3899
|
+@@ -13202,6 +13278,17 @@
|
|
3900
|
+ }, "")
|
|
3901
|
+ return s
|
|
3902
|
+ }
|
|
3903
|
++func (this *CascadeDiskVolumeSource) String() string {
|
|
3904
|
++ if this == nil {
|
|
3905
|
++ return "nil"
|
|
3906
|
++ }
|
|
3907
|
++ s := strings.Join([]string{`&CascadeDiskVolumeSource{`,
|
|
3908
|
++ `DiskID:` + fmt.Sprintf("%v", this.DiskID) + `,`,
|
|
3909
|
++ `FSType:` + fmt.Sprintf("%v", this.FSType) + `,`,
|
|
3910
|
++ `}`,
|
|
3911
|
++ }, "")
|
|
3912
|
++ return s
|
|
3913
|
++}
|
|
3914
|
+ func (this *CephFSPersistentVolumeSource) String() string {
|
|
3915
|
+ if this == nil {
|
|
3916
|
+ return "nil"
|
|
3917
|
+@@ -14532,6 +14619,7 @@
|
|
3918
|
+ `ScaleIO:` + strings.Replace(fmt.Sprintf("%v", this.ScaleIO), "ScaleIOVolumeSource", "ScaleIOVolumeSource", 1) + `,`,
|
|
3919
|
+ `Local:` + strings.Replace(fmt.Sprintf("%v", this.Local), "LocalVolumeSource", "LocalVolumeSource", 1) + `,`,
|
|
3920
|
+ `StorageOS:` + strings.Replace(fmt.Sprintf("%v", this.StorageOS), "StorageOSPersistentVolumeSource", "StorageOSPersistentVolumeSource", 1) + `,`,
|
|
3921
|
++ `CascadeDisk:` + strings.Replace(fmt.Sprintf("%v", this.CascadeDisk), "CascadeDiskVolumeSource", "CascadeDiskVolumeSource", 1) + `,`,
|
|
3922
|
+ `}`,
|
|
3923
|
+ }, "")
|
|
3924
|
+ return s
|
|
3925
|
+@@ -15592,6 +15680,7 @@
|
|
3926
|
+ `ScaleIO:` + strings.Replace(fmt.Sprintf("%v", this.ScaleIO), "ScaleIOVolumeSource", "ScaleIOVolumeSource", 1) + `,`,
|
|
3927
|
+ `Projected:` + strings.Replace(fmt.Sprintf("%v", this.Projected), "ProjectedVolumeSource", "ProjectedVolumeSource", 1) + `,`,
|
|
3928
|
+ `StorageOS:` + strings.Replace(fmt.Sprintf("%v", this.StorageOS), "StorageOSVolumeSource", "StorageOSVolumeSource", 1) + `,`,
|
|
3929
|
++ `CascadeDisk:` + strings.Replace(fmt.Sprintf("%v", this.CascadeDisk), "CascadeDiskVolumeSource", "CascadeDiskVolumeSource", 1) + `,`,
|
|
3930
|
+ `}`,
|
|
3931
|
+ }, "")
|
|
3932
|
+ return s
|
|
3933
|
+@@ -32799,6 +32888,39 @@
|
|
3934
|
+ return err
|
|
3935
|
+ }
|
|
3936
|
+ iNdEx = postIndex
|
|
3937
|
++ case 22:
|
|
3938
|
++ if wireType != 2 {
|
|
3939
|
++ return fmt.Errorf("proto: wrong wireType = %d for field CascadeDisk", wireType)
|
|
3940
|
++ }
|
|
3941
|
++ var msglen int
|
|
3942
|
++ for shift := uint(0); ; shift += 7 {
|
|
3943
|
++ if shift >= 64 {
|
|
3944
|
++ return ErrIntOverflowGenerated
|
|
3945
|
++ }
|
|
3946
|
++ if iNdEx >= l {
|
|
3947
|
++ return io.ErrUnexpectedEOF
|
|
3948
|
++ }
|
|
3949
|
++ b := dAtA[iNdEx]
|
|
3950
|
++ iNdEx++
|
|
3951
|
++ msglen |= (int(b) & 0x7F) << shift
|
|
3952
|
++ if b < 0x80 {
|
|
3953
|
++ break
|
|
3954
|
++ }
|
|
3955
|
++ }
|
|
3956
|
++ if msglen < 0 {
|
|
3957
|
++ return ErrInvalidLengthGenerated
|
|
3958
|
++ }
|
|
3959
|
++ postIndex := iNdEx + msglen
|
|
3960
|
++ if postIndex > l {
|
|
3961
|
++ return io.ErrUnexpectedEOF
|
|
3962
|
++ }
|
|
3963
|
++ if m.CascadeDisk == nil {
|
|
3964
|
++ m.CascadeDisk = &CascadeDiskVolumeSource{}
|
|
3965
|
++ }
|
|
3966
|
++ if err := m.CascadeDisk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
|
3967
|
++ return err
|
|
3968
|
++ }
|
|
3969
|
++ iNdEx = postIndex
|
|
3970
|
+ default:
|
|
3971
|
+ iNdEx = preIndex
|
|
3972
|
+ skippy, err := skipGenerated(dAtA[iNdEx:])
|
|
3973
|
+@@ -33307,6 +33429,114 @@
|
|
3974
|
+ }
|
|
3975
|
+ return nil
|
|
3976
|
+ }
|
|
3977
|
++func (m *CascadeDiskVolumeSource) Unmarshal(dAtA []byte) error {
|
|
3978
|
++ l := len(dAtA)
|
|
3979
|
++ iNdEx := 0
|
|
3980
|
++ for iNdEx < l {
|
|
3981
|
++ preIndex := iNdEx
|
|
3982
|
++ var wire uint64
|
|
3983
|
++ for shift := uint(0); ; shift += 7 {
|
|
3984
|
++ if shift >= 64 {
|
|
3985
|
++ return ErrIntOverflowGenerated
|
|
3986
|
++ }
|
|
3987
|
++ if iNdEx >= l {
|
|
3988
|
++ return io.ErrUnexpectedEOF
|
|
3989
|
++ }
|
|
3990
|
++ b := dAtA[iNdEx]
|
|
3991
|
++ iNdEx++
|
|
3992
|
++ wire |= (uint64(b) & 0x7F) << shift
|
|
3993
|
++ if b < 0x80 {
|
|
3994
|
++ break
|
|
3995
|
++ }
|
|
3996
|
++ }
|
|
3997
|
++ fieldNum := int32(wire >> 3)
|
|
3998
|
++ wireType := int(wire & 0x7)
|
|
3999
|
++ if wireType == 4 {
|
|
4000
|
++ return fmt.Errorf("proto: CascadeDiskVolumeSource: wiretype end group for non-group")
|
|
4001
|
++ }
|
|
4002
|
++ if fieldNum <= 0 {
|
|
4003
|
++ return fmt.Errorf("proto: CascadeDiskVolumeSource: illegal tag %d (wire type %d)", fieldNum, wire)
|
|
4004
|
++ }
|
|
4005
|
++ switch fieldNum {
|
|
4006
|
++ case 1:
|
|
4007
|
++ if wireType != 2 {
|
|
4008
|
++ return fmt.Errorf("proto: wrong wireType = %d for field DiskID", wireType)
|
|
4009
|
++ }
|
|
4010
|
++ var stringLen uint64
|
|
4011
|
++ for shift := uint(0); ; shift += 7 {
|
|
4012
|
++ if shift >= 64 {
|
|
4013
|
++ return ErrIntOverflowGenerated
|
|
4014
|
++ }
|
|
4015
|
++ if iNdEx >= l {
|
|
4016
|
++ return io.ErrUnexpectedEOF
|
|
4017
|
++ }
|
|
4018
|
++ b := dAtA[iNdEx]
|
|
4019
|
++ iNdEx++
|
|
4020
|
++ stringLen |= (uint64(b) & 0x7F) << shift
|
|
4021
|
++ if b < 0x80 {
|
|
4022
|
++ break
|
|
4023
|
++ }
|
|
4024
|
++ }
|
|
4025
|
++ intStringLen := int(stringLen)
|
|
4026
|
++ if intStringLen < 0 {
|
|
4027
|
++ return ErrInvalidLengthGenerated
|
|
4028
|
++ }
|
|
4029
|
++ postIndex := iNdEx + intStringLen
|
|
4030
|
++ if postIndex > l {
|
|
4031
|
++ return io.ErrUnexpectedEOF
|
|
4032
|
++ }
|
|
4033
|
++ m.DiskID = string(dAtA[iNdEx:postIndex])
|
|
4034
|
++ iNdEx = postIndex
|
|
4035
|
++ case 2:
|
|
4036
|
++ if wireType != 2 {
|
|
4037
|
++ return fmt.Errorf("proto: wrong wireType = %d for field FSType", wireType)
|
|
4038
|
++ }
|
|
4039
|
++ var stringLen uint64
|
|
4040
|
++ for shift := uint(0); ; shift += 7 {
|
|
4041
|
++ if shift >= 64 {
|
|
4042
|
++ return ErrIntOverflowGenerated
|
|
4043
|
++ }
|
|
4044
|
++ if iNdEx >= l {
|
|
4045
|
++ return io.ErrUnexpectedEOF
|
|
4046
|
++ }
|
|
4047
|
++ b := dAtA[iNdEx]
|
|
4048
|
++ iNdEx++
|
|
4049
|
++ stringLen |= (uint64(b) & 0x7F) << shift
|
|
4050
|
++ if b < 0x80 {
|
|
4051
|
++ break
|
|
4052
|
++ }
|
|
4053
|
++ }
|
|
4054
|
++ intStringLen := int(stringLen)
|
|
4055
|
++ if intStringLen < 0 {
|
|
4056
|
++ return ErrInvalidLengthGenerated
|
|
4057
|
++ }
|
|
4058
|
++ postIndex := iNdEx + intStringLen
|
|
4059
|
++ if postIndex > l {
|
|
4060
|
++ return io.ErrUnexpectedEOF
|
|
4061
|
++ }
|
|
4062
|
++ m.FSType = string(dAtA[iNdEx:postIndex])
|
|
4063
|
++ iNdEx = postIndex
|
|
4064
|
++ default:
|
|
4065
|
++ iNdEx = preIndex
|
|
4066
|
++ skippy, err := skipGenerated(dAtA[iNdEx:])
|
|
4067
|
++ if err != nil {
|
|
4068
|
++ return err
|
|
4069
|
++ }
|
|
4070
|
++ if skippy < 0 {
|
|
4071
|
++ return ErrInvalidLengthGenerated
|
|
4072
|
++ }
|
|
4073
|
++ if (iNdEx + skippy) > l {
|
|
4074
|
++ return io.ErrUnexpectedEOF
|
|
4075
|
++ }
|
|
4076
|
++ iNdEx += skippy
|
|
4077
|
++ }
|
|
4078
|
++ }
|
|
4079
|
++
|
|
4080
|
++ if iNdEx > l {
|
|
4081
|
++ return io.ErrUnexpectedEOF
|
|
4082
|
++ }
|
|
4083
|
++ return nil
|
|
4084
|
++}
|
|
4085
|
+ func (m *PhotonPersistentDiskVolumeSource) Unmarshal(dAtA []byte) error {
|
|
4086
|
+ l := len(dAtA)
|
|
4087
|
+ iNdEx := 0
|
|
4088
|
+@@ -45941,6 +46171,39 @@
|
|
4089
|
+ return err
|
|
4090
|
+ }
|
|
4091
|
+ iNdEx = postIndex
|
|
4092
|
++ case 28:
|
|
4093
|
++ if wireType != 2 {
|
|
4094
|
++ return fmt.Errorf("proto: wrong wireType = %d for field CascadeDisk", wireType)
|
|
4095
|
++ }
|
|
4096
|
++ var msglen int
|
|
4097
|
++ for shift := uint(0); ; shift += 7 {
|
|
4098
|
++ if shift >= 64 {
|
|
4099
|
++ return ErrIntOverflowGenerated
|
|
4100
|
++ }
|
|
4101
|
++ if iNdEx >= l {
|
|
4102
|
++ return io.ErrUnexpectedEOF
|
|
4103
|
++ }
|
|
4104
|
++ b := dAtA[iNdEx]
|
|
4105
|
++ iNdEx++
|
|
4106
|
++ msglen |= (int(b) & 0x7F) << shift
|
|
4107
|
++ if b < 0x80 {
|
|
4108
|
++ break
|
|
4109
|
++ }
|
|
4110
|
++ }
|
|
4111
|
++ if msglen < 0 {
|
|
4112
|
++ return ErrInvalidLengthGenerated
|
|
4113
|
++ }
|
|
4114
|
++ postIndex := iNdEx + msglen
|
|
4115
|
++ if postIndex > l {
|
|
4116
|
++ return io.ErrUnexpectedEOF
|
|
4117
|
++ }
|
|
4118
|
++ if m.CascadeDisk == nil {
|
|
4119
|
++ m.CascadeDisk = &CascadeDiskVolumeSource{}
|
|
4120
|
++ }
|
|
4121
|
++ if err := m.CascadeDisk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
|
4122
|
++ return err
|
|
4123
|
++ }
|
|
4124
|
++ iNdEx = postIndex
|
|
4125
|
+ default:
|
|
4126
|
+ iNdEx = preIndex
|
|
4127
|
+ skippy, err := skipGenerated(dAtA[iNdEx:])
|
|
4128
|
+diff -uNr --no-dereference kubernetes-1.8.1/staging/src/k8s.io/api/core/v1/types.go cascade-kubernetes/staging/src/k8s.io/api/core/v1/types.go
|
|
4129
|
+--- kubernetes-1.8.1/staging/src/k8s.io/api/core/v1/types.go 2018-01-23 22:47:25.594819355 +0000
|
|
4130
|
+@@ -350,6 +350,8 @@
|
|
4131
|
+ // StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.
|
|
4132
|
+ // +optional
|
|
4133
|
+ StorageOS *StorageOSVolumeSource `json:"storageos,omitempty" protobuf:"bytes,27,opt,name=storageos"`
|
|
4134
|
++ // CascadeDisk represents a Cascade persistent disk attached and mounted on kubelets host machine
|
|
4135
|
++ CascadeDisk *CascadeDiskVolumeSource `json:"cascadeDisk,omitempty" protobuf:"bytes,28,opt,name=cascadeDisk"`
|
|
4136
|
+ }
|
|
4137
|
+
|
|
4138
|
+ // PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace.
|
|
4139
|
+@@ -448,6 +450,8 @@
|
|
4140
|
+ // More info: https://releases.k8s.io/HEAD/examples/volumes/storageos/README.md
|
|
4141
|
+ // +optional
|
|
4142
|
+ StorageOS *StorageOSPersistentVolumeSource `json:"storageos,omitempty" protobuf:"bytes,21,opt,name=storageos"`
|
|
4143
|
++ // CascadeDisk represents a Cascade persistent disk attached and mounted on kubelets host machine
|
|
4144
|
++ CascadeDisk *CascadeDiskVolumeSource `json:"cascadeDisk,omitempty" protobuf:"bytes,22,opt,name=cascadeDisk"`
|
|
4145
|
+ }
|
|
4146
|
+
|
|
4147
|
+ const (
|
|
4148
|
+@@ -1431,6 +1435,16 @@
|
|
4149
|
+ SecretRef *ObjectReference `json:"secretRef,omitempty" protobuf:"bytes,5,opt,name=secretRef"`
|
|
4150
|
+ }
|
|
4151
|
+
|
|
4152
|
++// Represents a Photon Controller persistent disk resource.
|
|
4153
|
++type CascadeDiskVolumeSource struct {
|
|
4154
|
++ // ID that identifies Cascade persistent disk
|
|
4155
|
++ DiskID string `json:"diskID" protobuf:"bytes,1,opt,name=diskID"`
|
|
4156
|
++ // Filesystem type to mount.
|
|
4157
|
++ // Must be a filesystem type supported by the host operating system.
|
|
4158
|
++ // Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
|
|
4159
|
++ FSType string `json:"fsType,omitempty" protobuf:"bytes,2,opt,name=fsType"`
|
|
4160
|
++}
|
|
4161
|
++
|
|
4162
|
+ // Adapts a ConfigMap into a volume.
|
|
4163
|
+ //
|
|
4164
|
+ // The contents of the target ConfigMap's Data field will be presented in a
|