-
Notifications
You must be signed in to change notification settings - Fork 2k
/
csi.go
407 lines (337 loc) · 13 KB
/
csi.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
package structs
import (
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/plugins/csi"
)
// CSIVolumeMountOptions contains the mount options that should be provided when
// attaching and mounting a volume with the CSIVolumeAttachmentModeFilesystem
// attachment mode.
type CSIVolumeMountOptions struct {
// Filesystem is the desired filesystem type that should be used by the volume
// (e.g ext4, aufs, zfs). This field is optional.
Filesystem string
// MountFlags contain the mount options that should be used for the volume.
// These may contain _sensitive_ data and should not be leaked to logs or
// returned in debugging data.
// The total size of this field must be under 4KiB.
MountFlags []string
}
func (c *CSIVolumeMountOptions) ToCSIMountOptions() *structs.CSIMountOptions {
if c == nil {
return nil
}
return &structs.CSIMountOptions{
FSType: c.Filesystem,
MountFlags: c.MountFlags,
}
}
// CSIControllerRequest interface lets us set embedded CSIControllerQuery
// fields in the server
type CSIControllerRequest interface {
SetControllerNodeID(string)
}
// CSIControllerQuery is used to specify various flags for queries against CSI
// Controllers
type CSIControllerQuery struct {
// ControllerNodeID is the node that should be targeted by the request
ControllerNodeID string
// PluginID is the plugin that should be targeted on the given node.
PluginID string
}
func (c *CSIControllerQuery) SetControllerNodeID(nodeID string) {
c.ControllerNodeID = nodeID
}
type ClientCSIControllerValidateVolumeRequest struct {
VolumeID string // note: this is the external ID
VolumeCapabilities []*structs.CSIVolumeCapability
MountOptions *structs.CSIMountOptions
Secrets structs.CSISecrets
// COMPAT(1.1.1): the AttachmentMode and AccessMode fields are deprecated
// and replaced by the VolumeCapabilities field above
AttachmentMode structs.CSIVolumeAttachmentMode
AccessMode structs.CSIVolumeAccessMode
// Parameters as returned by storage provider in CreateVolumeResponse.
// This field is optional.
Parameters map[string]string
// Volume context as returned by storage provider in CreateVolumeResponse.
// This field is optional.
Context map[string]string
CSIControllerQuery
}
func (c *ClientCSIControllerValidateVolumeRequest) ToCSIRequest() (*csi.ControllerValidateVolumeRequest, error) {
if c == nil {
return &csi.ControllerValidateVolumeRequest{}, nil
}
creq := &csi.ControllerValidateVolumeRequest{
ExternalID: c.VolumeID,
Secrets: c.Secrets,
Capabilities: []*csi.VolumeCapability{},
Parameters: c.Parameters,
Context: c.Context,
}
for _, cap := range c.VolumeCapabilities {
ccap, err := csi.VolumeCapabilityFromStructs(
cap.AttachmentMode, cap.AccessMode, c.MountOptions)
if err != nil {
return nil, err
}
creq.Capabilities = append(creq.Capabilities, ccap)
}
return creq, nil
}
type ClientCSIControllerValidateVolumeResponse struct {
}
type ClientCSIControllerAttachVolumeRequest struct {
// The external ID of the volume to be used on a node.
// This field is REQUIRED.
VolumeID string
// The ID of the node. This field is REQUIRED. This must match the NodeID that
// is fingerprinted by the target node for this plugin name.
ClientCSINodeID string
// AttachmentMode indicates how the volume should be attached and mounted into
// a task.
AttachmentMode structs.CSIVolumeAttachmentMode
// AccessMode indicates the desired concurrent access model for the volume
AccessMode structs.CSIVolumeAccessMode
// MountOptions is an optional field that contains additional configuration
// when providing an AttachmentMode of CSIVolumeAttachmentModeFilesystem
MountOptions *CSIVolumeMountOptions
// ReadOnly indicates that the volume will be used in a readonly fashion. This
// only works when the Controller has the PublishReadonly capability.
ReadOnly bool
// Secrets required by plugin to complete the controller publish
// volume request. This field is OPTIONAL.
Secrets structs.CSISecrets
// Volume context as returned by storage provider in CreateVolumeResponse.
// This field is optional.
VolumeContext map[string]string
CSIControllerQuery
}
func (c *ClientCSIControllerAttachVolumeRequest) ToCSIRequest() (*csi.ControllerPublishVolumeRequest, error) {
if c == nil {
return &csi.ControllerPublishVolumeRequest{}, nil
}
var opts = c.MountOptions.ToCSIMountOptions()
caps, err := csi.VolumeCapabilityFromStructs(c.AttachmentMode, c.AccessMode, opts)
if err != nil {
return nil, err
}
return &csi.ControllerPublishVolumeRequest{
ExternalID: c.VolumeID,
NodeID: c.ClientCSINodeID,
VolumeCapability: caps,
ReadOnly: c.ReadOnly,
Secrets: c.Secrets,
VolumeContext: c.VolumeContext,
}, nil
}
// ClientCSIControllerDetachVolumeRequest is the RPC made from the server to
// a Nomad client to tell a CSI controller plugin on that client to perform
// ControllerUnpublish for a volume on a specific client.
type ClientCSIControllerAttachVolumeResponse struct {
// Opaque static publish properties of the volume. SP MAY use this
// field to ensure subsequent `NodeStageVolume` or `NodePublishVolume`
// calls calls have contextual information.
// The contents of this field SHALL be opaque to nomad.
// The contents of this field SHALL NOT be mutable.
// The contents of this field SHALL be safe for the nomad to cache.
// The contents of this field SHOULD NOT contain sensitive
// information.
// The contents of this field SHOULD NOT be used for uniquely
// identifying a volume. The `volume_id` alone SHOULD be sufficient to
// identify the volume.
// This field is OPTIONAL and when present MUST be passed to
// subsequent `NodeStageVolume` or `NodePublishVolume` calls
PublishContext map[string]string
}
type ClientCSIControllerDetachVolumeRequest struct {
// The external ID of the volume to be unpublished for the node
// This field is REQUIRED.
VolumeID string
// The CSI Node ID for the Node that the volume should be detached from.
// This field is REQUIRED. This must match the NodeID that is fingerprinted
// by the target node for this plugin name.
ClientCSINodeID string
// Secrets required by plugin to complete the controller unpublish
// volume request. This field is OPTIONAL.
Secrets structs.CSISecrets
CSIControllerQuery
}
func (c *ClientCSIControllerDetachVolumeRequest) ToCSIRequest() *csi.ControllerUnpublishVolumeRequest {
if c == nil {
return &csi.ControllerUnpublishVolumeRequest{}
}
return &csi.ControllerUnpublishVolumeRequest{
ExternalID: c.VolumeID,
NodeID: c.ClientCSINodeID,
}
}
type ClientCSIControllerDetachVolumeResponse struct{}
// ClientCSIControllerCreateVolumeRequest the RPC made from the server to a
// Nomad client to tell a CSI controller plugin on that client to perform
// CreateVolume
type ClientCSIControllerCreateVolumeRequest struct {
Name string
VolumeCapabilities []*structs.CSIVolumeCapability
MountOptions *structs.CSIMountOptions
Parameters map[string]string
Secrets structs.CSISecrets
CapacityMin int64
CapacityMax int64
SnapshotID string
CloneID string
// TODO: topology is not yet supported
// TopologyRequirement
CSIControllerQuery
}
func (req *ClientCSIControllerCreateVolumeRequest) ToCSIRequest() (*csi.ControllerCreateVolumeRequest, error) {
creq := &csi.ControllerCreateVolumeRequest{
Name: req.Name,
VolumeCapabilities: []*csi.VolumeCapability{},
Parameters: req.Parameters,
Secrets: req.Secrets,
ContentSource: &csi.VolumeContentSource{
CloneID: req.CloneID,
SnapshotID: req.SnapshotID,
},
// TODO: topology is not yet supported
AccessibilityRequirements: &csi.TopologyRequirement{},
}
// The CSI spec requires that at least one of the fields in CapacityRange
// must be defined. Fields set to 0 are considered unspecified and the
// CreateVolumeRequest should not send an invalid value.
if req.CapacityMin != 0 || req.CapacityMax != 0 {
creq.CapacityRange = &csi.CapacityRange{
RequiredBytes: req.CapacityMin,
LimitBytes: req.CapacityMax,
}
}
for _, cap := range req.VolumeCapabilities {
ccap, err := csi.VolumeCapabilityFromStructs(cap.AttachmentMode, cap.AccessMode, req.MountOptions)
if err != nil {
return nil, err
}
creq.VolumeCapabilities = append(creq.VolumeCapabilities, ccap)
}
return creq, nil
}
type ClientCSIControllerCreateVolumeResponse struct {
ExternalVolumeID string
CapacityBytes int64
VolumeContext map[string]string
// TODO: topology is not yet supported
// AccessibleTopology []*Topology
}
// ClientCSIControllerDeleteVolumeRequest the RPC made from the server to a
// Nomad client to tell a CSI controller plugin on that client to perform
// DeleteVolume
type ClientCSIControllerDeleteVolumeRequest struct {
ExternalVolumeID string
Secrets structs.CSISecrets
CSIControllerQuery
}
func (req *ClientCSIControllerDeleteVolumeRequest) ToCSIRequest() *csi.ControllerDeleteVolumeRequest {
return &csi.ControllerDeleteVolumeRequest{
ExternalVolumeID: req.ExternalVolumeID,
Secrets: req.Secrets,
}
}
type ClientCSIControllerDeleteVolumeResponse struct{}
// ClientCSIControllerListVolumesVolumeRequest the RPC made from the server to
// a Nomad client to tell a CSI controller plugin on that client to perform
// ListVolumes
type ClientCSIControllerListVolumesRequest struct {
// these pagination fields match the pagination fields of the plugins and
// not Nomad's own fields, for clarity when mapping between the two RPCs
MaxEntries int32
StartingToken string
CSIControllerQuery
}
func (req *ClientCSIControllerListVolumesRequest) ToCSIRequest() *csi.ControllerListVolumesRequest {
return &csi.ControllerListVolumesRequest{
MaxEntries: req.MaxEntries,
StartingToken: req.StartingToken,
}
}
type ClientCSIControllerListVolumesResponse struct {
Entries []*structs.CSIVolumeExternalStub
NextToken string
}
// ClientCSIControllerCreateSnapshotRequest the RPC made from the server to a
// Nomad client to tell a CSI controller plugin on that client to perform
// CreateSnapshot
type ClientCSIControllerCreateSnapshotRequest struct {
ExternalSourceVolumeID string
Name string
Secrets structs.CSISecrets
Parameters map[string]string
CSIControllerQuery
}
func (req *ClientCSIControllerCreateSnapshotRequest) ToCSIRequest() (*csi.ControllerCreateSnapshotRequest, error) {
return &csi.ControllerCreateSnapshotRequest{
VolumeID: req.ExternalSourceVolumeID,
Name: req.Name,
Secrets: req.Secrets,
Parameters: req.Parameters,
}, nil
}
type ClientCSIControllerCreateSnapshotResponse struct {
ID string
ExternalSourceVolumeID string
SizeBytes int64
CreateTime int64
IsReady bool
}
// ClientCSIControllerDeleteSnapshotRequest the RPC made from the server to a
// Nomad client to tell a CSI controller plugin on that client to perform
// DeleteSnapshot
type ClientCSIControllerDeleteSnapshotRequest struct {
ID string
Secrets structs.CSISecrets
CSIControllerQuery
}
func (req *ClientCSIControllerDeleteSnapshotRequest) ToCSIRequest() *csi.ControllerDeleteSnapshotRequest {
return &csi.ControllerDeleteSnapshotRequest{
SnapshotID: req.ID,
Secrets: req.Secrets,
}
}
type ClientCSIControllerDeleteSnapshotResponse struct{}
// ClientCSIControllerListSnapshotsRequest is the RPC made from the server to
// a Nomad client to tell a CSI controller plugin on that client to perform
// ListSnapshots
type ClientCSIControllerListSnapshotsRequest struct {
// these pagination fields match the pagination fields of the plugins and
// not Nomad's own fields, for clarity when mapping between the two RPCs
MaxEntries int32
StartingToken string
Secrets structs.CSISecrets
CSIControllerQuery
}
func (req *ClientCSIControllerListSnapshotsRequest) ToCSIRequest() *csi.ControllerListSnapshotsRequest {
return &csi.ControllerListSnapshotsRequest{
MaxEntries: req.MaxEntries,
StartingToken: req.StartingToken,
Secrets: req.Secrets,
}
}
type ClientCSIControllerListSnapshotsResponse struct {
Entries []*structs.CSISnapshot
NextToken string
}
// ClientCSINodeDetachVolumeRequest is the RPC made from the server to
// a Nomad client to tell a CSI node plugin on that client to perform
// NodeUnpublish and NodeUnstage.
type ClientCSINodeDetachVolumeRequest struct {
PluginID string // ID of the plugin that manages the volume (required)
VolumeID string // ID of the volume to be unpublished (required)
AllocID string // ID of the allocation we're unpublishing for (required)
NodeID string // ID of the Nomad client targeted
ExternalID string // External ID of the volume to be unpublished (required)
// These fields should match the original volume request so that
// we can find the mount points on the client
AttachmentMode structs.CSIVolumeAttachmentMode
AccessMode structs.CSIVolumeAccessMode
ReadOnly bool
}
type ClientCSINodeDetachVolumeResponse struct{}