@@ -21,6 +21,7 @@ import (
21
21
"errors"
22
22
"fmt"
23
23
"net"
24
+ "path"
24
25
"sync"
25
26
26
27
"google.golang.org/grpc"
@@ -36,6 +37,13 @@ import (
36
37
registerapi "k8s.io/kubelet/pkg/apis/pluginregistration/v1"
37
38
)
38
39
40
+ const (
41
+ // KubeletPluginsDir is the default directory for [PluginDataDirectoryPath].
42
+ KubeletPluginsDir = "/var/lib/kubelet/plugins"
43
+ // KubeletRegistryDir is the default for [RegistrarDirectoryPath]
44
+ KubeletRegistryDir = "/var/lib/kubelet/plugins_registry"
45
+ )
46
+
39
47
// DRAPlugin is the interface that needs to be implemented by a DRA driver to
40
48
// use this helper package. The helper package then implements the gRPC
41
49
// interface expected by the kubelet by wrapping the DRAPlugin implementation.
@@ -165,63 +173,77 @@ func GRPCVerbosity(level int) Option {
165
173
}
166
174
}
167
175
168
- // RegistrarSocketPath sets the file path for a Unix domain socket.
169
- // If RegistrarListener is not used, then Start will remove
170
- // a file at that path, should one exist, and creates a socket
171
- // itself. Otherwise it uses the provided listener and only
172
- // removes the socket at the specified path during shutdown.
176
+ // RegistrarDirectoryPath sets the path to the directory where the kubelet
177
+ // expects to find registration sockets of plugins. Typically this is
178
+ // /var/lib/kubelet/plugins_registry with /var/lib/kubelet being the kubelet's
179
+ // data directory.
173
180
//
174
- // At least one of these two options is required.
175
- func RegistrarSocketPath (path string ) Option {
181
+ // This is also the default. Some Kubernetes clusters may use a different data directory.
182
+ // This path must be the same inside and outside of the driver's container.
183
+ // The directory must exist.
184
+ func RegistrarDirectoryPath (path string ) Option {
176
185
return func (o * options ) error {
177
- o .pluginRegistrationEndpoint .path = path
186
+ o .pluginRegistrationEndpoint .dir = path
178
187
return nil
179
188
}
180
189
}
181
190
182
- // RegistrarListener sets an already created listener for the plugin
183
- // registration API. Can be combined with RegistrarSocketPath.
191
+ // RegistrarSocketFilename sets the name of the socket inside the directory where
192
+ // the kubelet watches for registration sockets (see RegistrarDirectoryPath).
193
+ //
194
+ // Usually DRA drivers should not need this option. It is provided to
195
+ // support updates from an installation which used an older release of
196
+ // of the helper code.
184
197
//
185
- // At least one of these two options is required.
186
- func RegistrarListener (listener net.Listener ) Option {
198
+ // The default is <driver name>-reg.sock. When rolling updates are enabled (not supported yet),
199
+ // it is <driver name>-<uid>-reg.sock.
200
+ func RegistrarSocketFilename (name string ) Option {
187
201
return func (o * options ) error {
188
- o .pluginRegistrationEndpoint .listener = listener
202
+ o .pluginRegistrationEndpoint .file = name
189
203
return nil
190
204
}
191
205
}
192
206
193
- // PluginSocketPath sets the file path for a Unix domain socket.
194
- // If PluginListener is not used, then Start will remove
195
- // a file at that path, should one exist, and creates a socket
196
- // itself. Otherwise it uses the provided listener and only
197
- // removes the socket at the specified path during shutdown.
207
+ // RegistrarListener configures how to create the registrar socket.
208
+ // The default is to remove the file if it exists and to then
209
+ // create a socket.
198
210
//
199
- // At least one of these two options is required.
200
- func PluginSocketPath (path string ) Option {
211
+ // This is used in Kubernetes for end-to-end testing. The default should
212
+ // be fine for DRA drivers.
213
+ func RegistrarListener (listen func (ctx context.Context , path string ) (net.Listener , error )) Option {
201
214
return func (o * options ) error {
202
- o .draEndpoint . path = path
215
+ o .pluginRegistrationEndpoint . listenFunc = listen
203
216
return nil
204
217
}
205
218
}
206
219
207
- // PluginListener sets an already created listener for the dynamic resource
208
- // allocation plugin API. Can be combined with PluginSocketPath.
220
+ // PluginDataDirectoryPath sets the path where the DRA driver creates the
221
+ // "dra.sock" socket that the kubelet connects to for the DRA-specific gRPC calls.
222
+ // It is also used to coordinate between different Pods when using rolling
223
+ // updates. It must not be shared with other kubelet plugins.
224
+ //
225
+ // The default is /var/lib/kubelet/plugins/<driver name>. This directory
226
+ // does not need to be inside the kubelet data directory, as long as
227
+ // the kubelet can access it.
209
228
//
210
- // At least one of these two options is required.
211
- func PluginListener (listener net.Listener ) Option {
229
+ // This path must be the same inside and outside of the driver's container.
230
+ // The directory must exist.
231
+ func PluginDataDirectoryPath (path string ) Option {
212
232
return func (o * options ) error {
213
- o .draEndpoint . listener = listener
233
+ o .pluginDataDirectoryPath = path
214
234
return nil
215
235
}
216
236
}
217
237
218
- // KubeletPluginSocketPath defines how kubelet will connect to the dynamic
219
- // resource allocation plugin. This corresponds to PluginSocketPath, except
220
- // that PluginSocketPath defines the path in the filesystem of the caller and
221
- // KubeletPluginSocketPath in the filesystem of kubelet.
222
- func KubeletPluginSocketPath (path string ) Option {
238
+ // PluginListener configures how to create the registrar socket.
239
+ // The default is to remove the file if it exists and to then
240
+ // create a socket.
241
+ //
242
+ // This is used in Kubernetes for end-to-end testing. The default should
243
+ // be fine for DRA drivers.
244
+ func PluginListener (listen func (ctx context.Context , path string ) (net.Listener , error )) Option {
223
245
return func (o * options ) error {
224
- o .draAddress = path
246
+ o .draEndpointListen = listen
225
247
return nil
226
248
}
227
249
}
@@ -306,9 +328,9 @@ type options struct {
306
328
driverName string
307
329
nodeName string
308
330
nodeUID types.UID
309
- draEndpoint endpoint
310
- draAddress string
311
331
pluginRegistrationEndpoint endpoint
332
+ pluginDataDirectoryPath string
333
+ draEndpointListen func (ctx context.Context , path string ) (net.Listener , error )
312
334
unaryInterceptors []grpc.UnaryServerInterceptor
313
335
streamInterceptors []grpc.StreamServerInterceptor
314
336
kubeClient kubernetes.Interface
@@ -349,14 +371,17 @@ type Helper struct {
349
371
// a name to all log entries.
350
372
//
351
373
// If the plugin will be used to publish resources, [KubeClient] and [NodeName]
352
- // options are mandatory.
374
+ // options are mandatory. Otherwise only [DriverName] is mandatory.
353
375
func Start (ctx context.Context , plugin DRAPlugin , opts ... Option ) (result * Helper , finalErr error ) {
354
376
logger := klog .FromContext (ctx )
355
377
o := options {
356
378
logger : klog .Background (),
357
379
grpcVerbosity : 6 , // Logs requests and responses, which can be large.
358
380
serialize : true ,
359
381
nodeV1beta1 : true ,
382
+ pluginRegistrationEndpoint : endpoint {
383
+ dir : KubeletRegistryDir ,
384
+ },
360
385
}
361
386
for _ , option := range opts {
362
387
if err := option (& o ); err != nil {
@@ -367,17 +392,12 @@ func Start(ctx context.Context, plugin DRAPlugin, opts ...Option) (result *Helpe
367
392
if o .driverName == "" {
368
393
return nil , errors .New ("driver name must be set" )
369
394
}
370
- if o .draAddress == "" {
371
- return nil , errors . New ( "DRA address must be set" )
395
+ if o .pluginRegistrationEndpoint . file == "" {
396
+ o . pluginRegistrationEndpoint . file = o . driverName + "-reg.sock"
372
397
}
373
- var emptyEndpoint endpoint
374
- if o .draEndpoint == emptyEndpoint {
375
- return nil , errors .New ("a Unix domain socket path and/or listener must be set for the kubelet plugin" )
398
+ if o .pluginDataDirectoryPath == "" {
399
+ o .pluginDataDirectoryPath = path .Join (KubeletPluginsDir , o .driverName )
376
400
}
377
- if o .pluginRegistrationEndpoint == emptyEndpoint {
378
- return nil , errors .New ("a Unix domain socket path and/or listener must be set for the registrar" )
379
- }
380
-
381
401
d := & Helper {
382
402
driverName : o .driverName ,
383
403
nodeName : o .nodeName ,
@@ -412,7 +432,12 @@ func Start(ctx context.Context, plugin DRAPlugin, opts ...Option) (result *Helpe
412
432
413
433
// Run the node plugin gRPC server first to ensure that it is ready.
414
434
var supportedServices []string
415
- pluginServer , err := startGRPCServer (klog .NewContext (ctx , klog .LoggerWithName (logger , "dra" )), o .grpcVerbosity , o .unaryInterceptors , o .streamInterceptors , o .draEndpoint , func (grpcServer * grpc.Server ) {
435
+ draEndpoint := endpoint {
436
+ dir : o .pluginDataDirectoryPath ,
437
+ file : "dra.sock" , // "dra" is hard-coded.
438
+ listenFunc : o .draEndpointListen ,
439
+ }
440
+ pluginServer , err := startGRPCServer (klog .LoggerWithName (logger , "dra" ), o .grpcVerbosity , o .unaryInterceptors , o .streamInterceptors , draEndpoint , func (grpcServer * grpc.Server ) {
416
441
if o .nodeV1beta1 {
417
442
logger .V (5 ).Info ("registering v1beta1.DRAPlugin gRPC service" )
418
443
drapb .RegisterDRAPluginServer (grpcServer , & nodePluginImplementation {Helper : d })
@@ -428,7 +453,7 @@ func Start(ctx context.Context, plugin DRAPlugin, opts ...Option) (result *Helpe
428
453
}
429
454
430
455
// Now make it available to kubelet.
431
- registrar , err := startRegistrar (klog .NewContext ( ctx , klog . LoggerWithName (logger , "registrar" )) , o .grpcVerbosity , o .unaryInterceptors , o .streamInterceptors , o .driverName , supportedServices , o . draAddress , o .pluginRegistrationEndpoint )
456
+ registrar , err := startRegistrar (klog .LoggerWithName (logger , "registrar" ), o .grpcVerbosity , o .unaryInterceptors , o .streamInterceptors , o .driverName , supportedServices , draEndpoint . path () , o .pluginRegistrationEndpoint )
432
457
if err != nil {
433
458
return nil , fmt .Errorf ("start registrar: %v" , err )
434
459
}
@@ -510,6 +535,11 @@ func (d *Helper) PublishResources(_ context.Context, resources resourceslice.Dri
510
535
// our background context, not the one passed into this
511
536
// function, and thus is connected to the lifecycle of the
512
537
// plugin.
538
+ //
539
+ // TODO: don't delete ResourceSlices, not even on a clean shutdown.
540
+ // We either support rolling updates and want to hand over seamlessly
541
+ // or don't and then perhaps restart the pod quickly enough that
542
+ // the kubelet hasn't deleted ResourceSlices yet.
513
543
controllerCtx := d .backgroundCtx
514
544
controllerLogger := klog .FromContext (controllerCtx )
515
545
controllerLogger = klog .LoggerWithName (controllerLogger , "ResourceSlice controller" )
0 commit comments