@@ -23,14 +23,18 @@ import (
23
23
24
24
const (
25
25
// environment variables for external plugins
26
+
26
27
EnvOperation = "DHV_OPERATION"
27
- EnvHostPath = "DHV_HOST_PATH"
28
- EnvNodeID = "DHV_NODE_ID"
28
+ EnvVolumesDir = "DHV_VOLUMES_DIR"
29
+ EnvPluginDir = "DHV_PLUGIN_DIR"
30
+ EnvCreatedPath = "DHV_CREATED_PATH"
31
+ EnvNamespace = "DHV_NAMESPACE"
29
32
EnvVolumeName = "DHV_VOLUME_NAME"
30
33
EnvVolumeID = "DHV_VOLUME_ID"
34
+ EnvNodeID = "DHV_NODE_ID"
35
+ EnvNodePool = "DHV_NODE_POOL"
31
36
EnvCapacityMin = "DHV_CAPACITY_MIN_BYTES"
32
37
EnvCapacityMax = "DHV_CAPACITY_MAX_BYTES"
33
- EnvPluginDir = "DHV_PLUGIN_DIR"
34
38
EnvParameters = "DHV_PARAMETERS"
35
39
)
36
40
@@ -62,10 +66,10 @@ const HostVolumePluginMkdirVersion = "0.0.1"
62
66
var _ HostVolumePlugin = & HostVolumePluginMkdir {}
63
67
64
68
// HostVolumePluginMkdir is a plugin that creates a directory within the
65
- // specified TargetPath . It is built-in to Nomad, so is always available.
69
+ // specified VolumesDir . It is built-in to Nomad, so is always available.
66
70
type HostVolumePluginMkdir struct {
67
71
ID string
68
- TargetPath string
72
+ VolumesDir string
69
73
70
74
log hclog.Logger
71
75
}
@@ -80,7 +84,7 @@ func (p *HostVolumePluginMkdir) Fingerprint(_ context.Context) (*PluginFingerpri
80
84
func (p * HostVolumePluginMkdir ) Create (_ context.Context ,
81
85
req * cstructs.ClientHostVolumeCreateRequest ) (* HostVolumePluginCreateResponse , error ) {
82
86
83
- path := filepath .Join (p .TargetPath , req .ID )
87
+ path := filepath .Join (p .VolumesDir , req .ID )
84
88
log := p .log .With (
85
89
"operation" , "create" ,
86
90
"volume_id" , req .ID ,
@@ -102,7 +106,7 @@ func (p *HostVolumePluginMkdir) Create(_ context.Context,
102
106
return nil , err
103
107
}
104
108
105
- err := os .Mkdir (path , 0o700 )
109
+ err := os .MkdirAll (path , 0o700 )
106
110
if err != nil {
107
111
log .Debug ("error with plugin" , "error" , err )
108
112
return nil , err
@@ -113,7 +117,7 @@ func (p *HostVolumePluginMkdir) Create(_ context.Context,
113
117
}
114
118
115
119
func (p * HostVolumePluginMkdir ) Delete (_ context.Context , req * cstructs.ClientHostVolumeDeleteRequest ) error {
116
- path := filepath .Join (p .TargetPath , req .ID )
120
+ path := filepath .Join (p .VolumesDir , req .ID )
117
121
log := p .log .With (
118
122
"operation" , "delete" ,
119
123
"volume_id" , req .ID ,
@@ -135,7 +139,7 @@ var _ HostVolumePlugin = &HostVolumePluginExternal{}
135
139
// NewHostVolumePluginExternal returns an external host volume plugin
136
140
// if the specified executable exists on disk.
137
141
func NewHostVolumePluginExternal (log hclog.Logger ,
138
- pluginDir , filename , targetPath string ) (* HostVolumePluginExternal , error ) {
142
+ pluginDir , filename , volumesDir , nodePool string ) (* HostVolumePluginExternal , error ) {
139
143
// this should only be called with already-detected executables,
140
144
// but we'll double-check it anyway, so we can provide a tidy error message
141
145
// if it has changed between fingerprinting and execution.
@@ -153,8 +157,9 @@ func NewHostVolumePluginExternal(log hclog.Logger,
153
157
return & HostVolumePluginExternal {
154
158
ID : filename ,
155
159
Executable : executable ,
156
- TargetPath : targetPath ,
160
+ VolumesDir : volumesDir ,
157
161
PluginDir : pluginDir ,
162
+ NodePool : nodePool ,
158
163
log : log ,
159
164
}, nil
160
165
}
@@ -166,16 +171,17 @@ func NewHostVolumePluginExternal(log hclog.Logger,
166
171
type HostVolumePluginExternal struct {
167
172
ID string
168
173
Executable string
169
- TargetPath string
174
+ VolumesDir string
170
175
PluginDir string
176
+ NodePool string
171
177
172
178
log hclog.Logger
173
179
}
174
180
175
181
// Fingerprint calls the executable with the following parameters:
176
- // arguments: fingerprint
182
+ // arguments: $1= fingerprint
177
183
// environment:
178
- // DHV_OPERATION=fingerprint
184
+ // - DHV_OPERATION=fingerprint
179
185
//
180
186
// Response should be valid JSON on stdout, with a "version" key, e.g.:
181
187
// {"version": "0.0.1"}
@@ -205,21 +211,23 @@ func (p *HostVolumePluginExternal) Fingerprint(ctx context.Context) (*PluginFing
205
211
}
206
212
207
213
// Create calls the executable with the following parameters:
208
- // arguments: create {path to create}
214
+ // arguments: $1= create
209
215
// environment:
210
- // DHV_OPERATION=create
211
- // DHV_HOST_PATH={path to create}
212
- // DHV_NODE_ID={Nomad node ID}
213
- // DHV_VOLUME_NAME={name from the volume specification}
214
- // DHV_VOLUME_ID={Nomad volume ID}
215
- // DHV_CAPACITY_MIN_BYTES={capacity_min from the volume spec}
216
- // DHV_CAPACITY_MAX_BYTES={capacity_max from the volume spec}
217
- // DHV_PARAMETERS={json of parameters from the volume spec}
218
- // DHV_PLUGIN_DIR={path to directory containing plugins}
216
+ // - DHV_OPERATION=create
217
+ // - DHV_VOLUMES_DIR={directory to put the volume in}
218
+ // - DHV_PLUGIN_DIR={path to directory containing plugins}
219
+ // - DHV_NAMESPACE={volume namespace}
220
+ // - DHV_VOLUME_NAME={name from the volume specification}
221
+ // - DHV_VOLUME_ID={volume ID generated by Nomad}
222
+ // - DHV_NODE_ID={Nomad node ID}
223
+ // - DHV_NODE_POOL={Nomad node pool}
224
+ // - DHV_CAPACITY_MIN_BYTES={capacity_min from the volume spec, expressed in bytes}
225
+ // - DHV_CAPACITY_MAX_BYTES={capacity_max from the volume spec, expressed in bytes}
226
+ // - DHV_PARAMETERS={stringified json of parameters from the volume spec}
219
227
//
220
228
// Response should be valid JSON on stdout with "path" and "bytes", e.g.:
221
- // {"path": $HOST_PATH , "bytes": 50000000}
222
- // "path" must be provided to confirm that the requested path is what was
229
+ // {"path": "/path/that/was/created" , "bytes": 50000000}
230
+ // "path" must be provided to confirm the requested path is what was
223
231
// created by the plugin. "bytes" is the actual size of the volume created
224
232
// by the plugin; if excluded, it will default to 0.
225
233
//
@@ -233,14 +241,22 @@ func (p *HostVolumePluginExternal) Create(ctx context.Context,
233
241
return nil , fmt .Errorf ("error marshaling volume pramaters: %w" , err )
234
242
}
235
243
envVars := []string {
236
- fmt .Sprintf ("%s=%s" , EnvNodeID , req .NodeID ),
244
+ fmt .Sprintf ("%s=%s" , EnvOperation , "create" ),
245
+ fmt .Sprintf ("%s=%s" , EnvVolumesDir , p .VolumesDir ),
246
+ fmt .Sprintf ("%s=%s" , EnvPluginDir , p .PluginDir ),
247
+ fmt .Sprintf ("%s=%s" , EnvNodePool , p .NodePool ),
248
+ // values from volume spec
249
+ fmt .Sprintf ("%s=%s" , EnvNamespace , req .Namespace ),
237
250
fmt .Sprintf ("%s=%s" , EnvVolumeName , req .Name ),
251
+ fmt .Sprintf ("%s=%s" , EnvVolumeID , req .ID ),
238
252
fmt .Sprintf ("%s=%d" , EnvCapacityMin , req .RequestedCapacityMinBytes ),
239
253
fmt .Sprintf ("%s=%d" , EnvCapacityMax , req .RequestedCapacityMaxBytes ),
254
+ fmt .Sprintf ("%s=%s" , EnvNodeID , req .NodeID ),
240
255
fmt .Sprintf ("%s=%s" , EnvParameters , params ),
241
256
}
242
257
243
- stdout , _ , err := p .runPlugin (ctx , "create" , req .ID , envVars )
258
+ log := p .log .With ("volume_name" , req .Name , "volume_id" , req .ID )
259
+ stdout , _ , err := p .runPlugin (ctx , log , "create" , envVars )
244
260
if err != nil {
245
261
return nil , fmt .Errorf ("error creating volume %q with plugin %q: %w" , req .ID , p .ID , err )
246
262
}
@@ -253,20 +269,22 @@ func (p *HostVolumePluginExternal) Create(ctx context.Context,
253
269
// an error here after the plugin has done who-knows-what.
254
270
return nil , err
255
271
}
256
- // TODO: validate returned host path
257
272
return & pluginResp , nil
258
273
}
259
274
260
275
// Delete calls the executable with the following parameters:
261
- // arguments: delete {path to create}
276
+ // arguments: $1= delete
262
277
// environment:
263
- // DHV_OPERATION=delete
264
- // DHV_HOST_PATH={path to create}
265
- // DHV_NODE_ID={Nomad node ID}
266
- // DHV_VOLUME_NAME={name from the volume specification}
267
- // DHV_VOLUME_ID={Nomad volume ID}
268
- // DHV_PARAMETERS={json of parameters from the volume spec}
269
- // DHV_PLUGIN_DIR={path to directory containing plugins}
278
+ // - DHV_OPERATION=delete
279
+ // - DHV_CREATED_PATH={path that `create` returned}
280
+ // - DHV_VOLUMES_DIR={directory that volumes should be put in}
281
+ // - DHV_PLUGIN_DIR={path to directory containing plugins}
282
+ // - DHV_NAMESPACE={volume namespace}
283
+ // - DHV_VOLUME_NAME={name from the volume specification}
284
+ // - DHV_VOLUME_ID={volume ID generated by Nomad}
285
+ // - DHV_NODE_ID={Nomad node ID}
286
+ // - DHV_NODE_POOL={Nomad node pool}
287
+ // - DHV_PARAMETERS={stringified json of parameters from the volume spec}
270
288
//
271
289
// Response on stdout is discarded.
272
290
//
@@ -280,42 +298,38 @@ func (p *HostVolumePluginExternal) Delete(ctx context.Context,
280
298
return fmt .Errorf ("error marshaling volume pramaters: %w" , err )
281
299
}
282
300
envVars := []string {
283
- fmt .Sprintf ("%s=%s" , EnvNodeID , req .NodeID ),
301
+ fmt .Sprintf ("%s=%s" , EnvOperation , "delete" ),
302
+ fmt .Sprintf ("%s=%s" , EnvVolumesDir , p .VolumesDir ),
303
+ fmt .Sprintf ("%s=%s" , EnvPluginDir , p .PluginDir ),
304
+ fmt .Sprintf ("%s=%s" , EnvNodePool , p .NodePool ),
305
+ // from create response
306
+ fmt .Sprintf ("%s=%s" , EnvCreatedPath , req .HostPath ),
307
+ // values from volume spec
308
+ fmt .Sprintf ("%s=%s" , EnvNamespace , req .Namespace ),
284
309
fmt .Sprintf ("%s=%s" , EnvVolumeName , req .Name ),
310
+ fmt .Sprintf ("%s=%s" , EnvVolumeID , req .ID ),
311
+ fmt .Sprintf ("%s=%s" , EnvNodeID , req .NodeID ),
285
312
fmt .Sprintf ("%s=%s" , EnvParameters , params ),
286
313
}
287
314
288
- _ , _ , err = p .runPlugin (ctx , "delete" , req .ID , envVars )
315
+ log := p .log .With ("volume_name" , req .Name , "volume_id" , req .ID )
316
+ _ , _ , err = p .runPlugin (ctx , log , "delete" , envVars )
289
317
if err != nil {
290
318
return fmt .Errorf ("error deleting volume %q with plugin %q: %w" , req .ID , p .ID , err )
291
319
}
292
320
return nil
293
321
}
294
322
295
- // runPlugin executes the... executable with these additional env vars:
296
- // DHV_OPERATION={op}
297
- // DHV_HOST_PATH={path to create}
298
- // DHV_VOLUME_ID={Nomad volume ID}
299
- // DHV_PLUGIN_DIR={path to directory containing plugins}
300
- func (p * HostVolumePluginExternal ) runPlugin (ctx context.Context ,
301
- op , volID string , env []string ) (stdout , stderr []byte , err error ) {
323
+ // runPlugin executes the... executable
324
+ func (p * HostVolumePluginExternal ) runPlugin (ctx context.Context , log hclog.Logger ,
325
+ op string , env []string ) (stdout , stderr []byte , err error ) {
302
326
303
- path := filepath .Join (p .TargetPath , volID )
304
- log := p .log .With (
305
- "operation" , op ,
306
- "volume_id" , volID ,
307
- "path" , path )
327
+ log = log .With ("operation" , op )
308
328
log .Debug ("running plugin" )
309
329
310
330
// set up plugin execution
311
- cmd := exec .CommandContext (ctx , p .Executable , op , path )
312
-
313
- cmd .Env = append ([]string {
314
- fmt .Sprintf ("%s=%s" , EnvOperation , op ),
315
- fmt .Sprintf ("%s=%s" , EnvHostPath , path ),
316
- fmt .Sprintf ("%s=%s" , EnvVolumeID , volID ),
317
- fmt .Sprintf ("%s=%s" , EnvPluginDir , p .PluginDir ),
318
- }, env ... )
331
+ cmd := exec .CommandContext (ctx , p .Executable , op )
332
+ cmd .Env = env
319
333
320
334
stdout , stderr , err = runCommand (cmd )
321
335
0 commit comments