Skip to content

Commit 9261a18

Browse files
committed
DRA kubelet: separate beta and alpha gRPC APIs
Reusing types from the alpha in the beta made it possible to provide and use both versions without conversion. The downside was that removal of the alpha would have been harder, if not impossible. DRA drivers could continue to use the alpha types and provided the beta interface automatically. Now the two versions are completely separate gRPC APIs, although in practice there are no differences besides the name. Support for the alpha API in kubelet is provided via automatically generated conversion and manually written interface wrappers. Those are provided as part of the v1alpha4 package. The advantage of having all of that in a central place is that it'll be easier to remove when no longer needed.
1 parent e273349 commit 9261a18

File tree

10 files changed

+2924
-84
lines changed

10 files changed

+2924
-84
lines changed

pkg/kubelet/cm/dra/plugin/plugin.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ func (p *Plugin) NodePrepareResources(
128128
response, err = nodeClient.NodePrepareResources(ctx, req)
129129
case drapbv1alpha4.NodeService:
130130
nodeClient := drapbv1alpha4.NewNodeClient(conn)
131-
response, err = nodeClient.NodePrepareResources(ctx, req)
131+
response, err = drapbv1alpha4.V1Alpha4ClientWrapper{NodeClient: nodeClient}.NodePrepareResources(ctx, req)
132132
default:
133133
// Shouldn't happen, validateSupportedServices should only
134134
// return services we support here.
@@ -161,7 +161,7 @@ func (p *Plugin) NodeUnprepareResources(
161161
response, err = nodeClient.NodeUnprepareResources(ctx, req)
162162
case drapbv1alpha4.NodeService:
163163
nodeClient := drapbv1alpha4.NewNodeClient(conn)
164-
response, err = nodeClient.NodeUnprepareResources(ctx, req)
164+
response, err = drapbv1alpha4.V1Alpha4ClientWrapper{NodeClient: nodeClient}.NodeUnprepareResources(ctx, req)
165165
default:
166166
// Shouldn't happen, validateSupportedServices should only
167167
// return services we support here.

pkg/kubelet/cm/dra/plugin/plugin_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import (
3434
)
3535

3636
type fakeGRPCServer struct {
37-
drapbv1beta1.UnimplementedDRAPluginServer
3837
}
3938

4039
var _ drapbv1beta1.DRAPluginServer = &fakeGRPCServer{}
@@ -84,7 +83,7 @@ func setupFakeGRPCServer(service string) (string, tearDown, error) {
8483
case drapbv1beta1.DRAPluginService:
8584
drapbv1beta1.RegisterDRAPluginServer(s, fakeGRPCServer)
8685
case drapbv1alpha4.NodeService:
87-
drapbv1alpha4.RegisterNodeServer(s, fakeGRPCServer)
86+
drapbv1alpha4.RegisterNodeServer(s, drapbv1alpha4.V1Beta1ServerWrapper{DRAPluginServer: fakeGRPCServer})
8887
default:
8988
return "", nil, fmt.Errorf("unsupported gRPC service: %s", service)
9089
}

staging/src/k8s.io/dynamic-resource-allocation/kubeletplugin/draplugin.go

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -272,12 +272,12 @@ type draPlugin struct {
272272
// If the plugin will be used to publish resources, [KubeClient] and [NodeName]
273273
// options are mandatory.
274274
//
275-
// By default, the DRA driver gets registered so that the plugin is compatible
276-
// with Kubernetes >= 1.32. To be compatible with Kubernetes >= 1.31, a driver
277-
// has to ask specifically to register only the alpha gRPC API, i.e. use:
278-
//
279-
// Start(..., NodeV1beta1(false))
280-
func Start(ctx context.Context, nodeServer interface{}, opts ...Option) (result DRAPlugin, finalErr error) {
275+
// The DRA driver decides which gRPC interfaces it implements. At least one
276+
// implementation of [drapbv1alpha4.NodeServer] or [drapbv1beta1.DRAPluginServer]
277+
// is required. Implementing drapbv1beta1.DRAPluginServer is recommended for
278+
// DRA driver targeting Kubernetes >= 1.32. To be compatible with Kubernetes 1.31,
279+
// DRA drivers must implement only [drapbv1alpha4.NodeServer].
280+
func Start(ctx context.Context, nodeServers []interface{}, opts ...Option) (result DRAPlugin, finalErr error) {
281281
logger := klog.FromContext(ctx)
282282
o := options{
283283
logger: klog.Background(),
@@ -338,15 +338,17 @@ func Start(ctx context.Context, nodeServer interface{}, opts ...Option) (result
338338
// Run the node plugin gRPC server first to ensure that it is ready.
339339
var supportedServices []string
340340
plugin, err := startGRPCServer(klog.NewContext(ctx, klog.LoggerWithName(logger, "dra")), o.grpcVerbosity, o.unaryInterceptors, o.streamInterceptors, o.draEndpoint, func(grpcServer *grpc.Server) {
341-
if nodeServer, ok := nodeServer.(drapbv1alpha4.NodeServer); ok && o.nodeV1alpha4 {
342-
logger.V(5).Info("registering v1alpha4.Node gGRPC service")
343-
drapbv1alpha4.RegisterNodeServer(grpcServer, nodeServer)
344-
supportedServices = append(supportedServices, drapbv1alpha4.NodeService)
345-
}
346-
if nodeServer, ok := nodeServer.(drapbv1beta1.DRAPluginServer); ok && o.nodeV1beta1 {
347-
logger.V(5).Info("registering v1beta1.DRAPlugin gRPC service")
348-
drapbv1beta1.RegisterDRAPluginServer(grpcServer, nodeServer)
349-
supportedServices = append(supportedServices, drapbv1beta1.DRAPluginService)
341+
for _, nodeServer := range nodeServers {
342+
if nodeServer, ok := nodeServer.(drapbv1alpha4.NodeServer); ok && o.nodeV1alpha4 {
343+
logger.V(5).Info("registering v1alpha4.Node gGRPC service")
344+
drapbv1alpha4.RegisterNodeServer(grpcServer, nodeServer)
345+
supportedServices = append(supportedServices, drapbv1alpha4.NodeService)
346+
}
347+
if nodeServer, ok := nodeServer.(drapbv1beta1.DRAPluginServer); ok && o.nodeV1beta1 {
348+
logger.V(5).Info("registering v1beta1.DRAPlugin gRPC service")
349+
drapbv1beta1.RegisterDRAPluginServer(grpcServer, nodeServer)
350+
supportedServices = append(supportedServices, drapbv1beta1.DRAPluginService)
351+
}
350352
}
351353
})
352354
if err != nil {
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1alpha4
18+
19+
import (
20+
context "context"
21+
fmt "fmt"
22+
23+
grpc "google.golang.org/grpc"
24+
"k8s.io/apimachinery/pkg/runtime"
25+
"k8s.io/kubelet/pkg/apis/dra/v1beta1"
26+
)
27+
28+
var (
29+
localSchemeBuilder runtime.SchemeBuilder
30+
AddToScheme = localSchemeBuilder.AddToScheme
31+
)
32+
33+
// V1Beta1ServerWrapper implements the [NodeServer] interface by wrapping a [v1beta1.DRAPluginServer].
34+
type V1Beta1ServerWrapper struct {
35+
v1beta1.DRAPluginServer
36+
}
37+
38+
var _ NodeServer = V1Beta1ServerWrapper{}
39+
40+
func (w V1Beta1ServerWrapper) NodePrepareResources(ctx context.Context, req *NodePrepareResourcesRequest) (*NodePrepareResourcesResponse, error) {
41+
var convertedReq v1beta1.NodePrepareResourcesRequest
42+
if err := Convert_v1alpha4_NodePrepareResourcesRequest_To_v1beta1_NodePrepareResourcesRequest(req, &convertedReq, nil); err != nil {
43+
return nil, fmt.Errorf("internal error converting NodePrepareResourcesRequest from v1alpha4 to v1beta1: %w", err)
44+
}
45+
resp, err := w.DRAPluginServer.NodePrepareResources(ctx, &convertedReq)
46+
if err != nil {
47+
return nil, err
48+
}
49+
var convertedResp NodePrepareResourcesResponse
50+
if err := Convert_v1beta1_NodePrepareResourcesResponse_To_v1alpha4_NodePrepareResourcesResponse(resp, &convertedResp, nil); err != nil {
51+
return nil, fmt.Errorf("internal error converting NodePrepareResourcesResponse from v1beta1 to v1alpha4: %w", err)
52+
}
53+
return &convertedResp, nil
54+
}
55+
56+
func (w V1Beta1ServerWrapper) NodeUnprepareResources(ctx context.Context, req *NodeUnprepareResourcesRequest) (*NodeUnprepareResourcesResponse, error) {
57+
var convertedReq v1beta1.NodeUnprepareResourcesRequest
58+
if err := Convert_v1alpha4_NodeUnprepareResourcesRequest_To_v1beta1_NodeUnprepareResourcesRequest(req, &convertedReq, nil); err != nil {
59+
return nil, fmt.Errorf("internal error converting NodeUnprepareResourcesRequest from v1alpha4 to v1beta1: %w", err)
60+
}
61+
resp, err := w.DRAPluginServer.NodeUnprepareResources(ctx, &convertedReq)
62+
if err != nil {
63+
return nil, err
64+
}
65+
var convertedResp NodeUnprepareResourcesResponse
66+
if err := Convert_v1beta1_NodeUnprepareResourcesResponse_To_v1alpha4_NodeUnprepareResourcesResponse(resp, &convertedResp, nil); err != nil {
67+
return nil, fmt.Errorf("internal error converting NodeUnprepareResourcesResponse from v1beta1 to v1alpha4: %w", err)
68+
}
69+
return &convertedResp, nil
70+
}
71+
72+
// V1Alpha4ServerWrapper implements the [v1beta1.DRAPluginServer] interface by wrapping a [NodeServer].
73+
type V1Alpha4ServerWrapper struct {
74+
NodeServer
75+
}
76+
77+
var _ v1beta1.DRAPluginServer = V1Alpha4ServerWrapper{}
78+
79+
func (w V1Alpha4ServerWrapper) NodePrepareResources(ctx context.Context, req *v1beta1.NodePrepareResourcesRequest) (*v1beta1.NodePrepareResourcesResponse, error) {
80+
var convertedReq NodePrepareResourcesRequest
81+
if err := Convert_v1beta1_NodePrepareResourcesRequest_To_v1alpha4_NodePrepareResourcesRequest(req, &convertedReq, nil); err != nil {
82+
return nil, fmt.Errorf("internal error converting NodePrepareResourcesRequest from v1beta1 to v1alpha4: %w", err)
83+
}
84+
resp, err := w.NodeServer.NodePrepareResources(ctx, &convertedReq)
85+
if err != nil {
86+
return nil, err
87+
}
88+
var convertedResp v1beta1.NodePrepareResourcesResponse
89+
if err := Convert_v1alpha4_NodePrepareResourcesResponse_To_v1beta1_NodePrepareResourcesResponse(resp, &convertedResp, nil); err != nil {
90+
return nil, fmt.Errorf("internal error converting NodePrepareResourcesResponse from v1alpha4 to v1beta1: %w", err)
91+
}
92+
return &convertedResp, nil
93+
}
94+
95+
func (w V1Alpha4ServerWrapper) NodeUnprepareResources(ctx context.Context, req *v1beta1.NodeUnprepareResourcesRequest) (*v1beta1.NodeUnprepareResourcesResponse, error) {
96+
var convertedReq NodeUnprepareResourcesRequest
97+
if err := Convert_v1beta1_NodeUnprepareResourcesRequest_To_v1alpha4_NodeUnprepareResourcesRequest(req, &convertedReq, nil); err != nil {
98+
return nil, fmt.Errorf("internal error converting NodeUnprepareResourcesRequest from v1beta1 to v1alpha4: %w", err)
99+
}
100+
resp, err := w.NodeServer.NodeUnprepareResources(ctx, &convertedReq)
101+
if err != nil {
102+
return nil, err
103+
}
104+
var convertedResp v1beta1.NodeUnprepareResourcesResponse
105+
if err := Convert_v1alpha4_NodeUnprepareResourcesResponse_To_v1beta1_NodeUnprepareResourcesResponse(resp, &convertedResp, nil); err != nil {
106+
return nil, fmt.Errorf("internal error converting NodeUnprepareResourcesResponse from v1alpha4 to v1beta1: %w", err)
107+
}
108+
return &convertedResp, nil
109+
}
110+
111+
// V1Beta1ClientWrapper implements the [NodeClient] interface by wrapping a [v1beta1.DRAPluginClient].
112+
type V1Beta1ClientWrapper struct {
113+
v1beta1.DRAPluginClient
114+
}
115+
116+
var _ NodeClient = V1Beta1ClientWrapper{}
117+
118+
func (w V1Beta1ClientWrapper) NodePrepareResources(ctx context.Context, req *NodePrepareResourcesRequest, options ...grpc.CallOption) (*NodePrepareResourcesResponse, error) {
119+
var convertedReq v1beta1.NodePrepareResourcesRequest
120+
if err := Convert_v1alpha4_NodePrepareResourcesRequest_To_v1beta1_NodePrepareResourcesRequest(req, &convertedReq, nil); err != nil {
121+
return nil, fmt.Errorf("internal error converting NodePrepareResourcesRequest from v1alpha4 to v1beta1: %w", err)
122+
}
123+
resp, err := w.DRAPluginClient.NodePrepareResources(ctx, &convertedReq, options...)
124+
if err != nil {
125+
return nil, err
126+
}
127+
var convertedResp NodePrepareResourcesResponse
128+
if err := Convert_v1beta1_NodePrepareResourcesResponse_To_v1alpha4_NodePrepareResourcesResponse(resp, &convertedResp, nil); err != nil {
129+
return nil, fmt.Errorf("internal error converting NodePrepareResourcesResponse from v1beta1 to v1alpha4: %w", err)
130+
}
131+
return &convertedResp, nil
132+
}
133+
134+
func (w V1Beta1ClientWrapper) NodeUnprepareResources(ctx context.Context, req *NodeUnprepareResourcesRequest, options ...grpc.CallOption) (*NodeUnprepareResourcesResponse, error) {
135+
var convertedReq v1beta1.NodeUnprepareResourcesRequest
136+
if err := Convert_v1alpha4_NodeUnprepareResourcesRequest_To_v1beta1_NodeUnprepareResourcesRequest(req, &convertedReq, nil); err != nil {
137+
return nil, fmt.Errorf("internal error converting NodeUnprepareResourcesRequest from v1alpha4 to v1beta1: %w", err)
138+
}
139+
resp, err := w.DRAPluginClient.NodeUnprepareResources(ctx, &convertedReq, options...)
140+
if err != nil {
141+
return nil, err
142+
}
143+
var convertedResp NodeUnprepareResourcesResponse
144+
if err := Convert_v1beta1_NodeUnprepareResourcesResponse_To_v1alpha4_NodeUnprepareResourcesResponse(resp, &convertedResp, nil); err != nil {
145+
return nil, fmt.Errorf("internal error converting NodeUnprepareResourcesResponse from v1beta1 to v1alpha4: %w", err)
146+
}
147+
return &convertedResp, nil
148+
}
149+
150+
// V1Alpha4ClientWrapper implements the [v1beta1.DRAPluginClient] interface by wrapping a [NodeClient].
151+
type V1Alpha4ClientWrapper struct {
152+
NodeClient
153+
}
154+
155+
var _ v1beta1.DRAPluginClient = V1Alpha4ClientWrapper{}
156+
157+
func (w V1Alpha4ClientWrapper) NodePrepareResources(ctx context.Context, req *v1beta1.NodePrepareResourcesRequest, options ...grpc.CallOption) (*v1beta1.NodePrepareResourcesResponse, error) {
158+
var convertedReq NodePrepareResourcesRequest
159+
if err := Convert_v1beta1_NodePrepareResourcesRequest_To_v1alpha4_NodePrepareResourcesRequest(req, &convertedReq, nil); err != nil {
160+
return nil, fmt.Errorf("internal error converting NodePrepareResourcesRequest from v1beta1 to v1alpha4: %w", err)
161+
}
162+
resp, err := w.NodeClient.NodePrepareResources(ctx, &convertedReq, options...)
163+
if err != nil {
164+
return nil, err
165+
}
166+
var convertedResp v1beta1.NodePrepareResourcesResponse
167+
if err := Convert_v1alpha4_NodePrepareResourcesResponse_To_v1beta1_NodePrepareResourcesResponse(resp, &convertedResp, nil); err != nil {
168+
return nil, fmt.Errorf("internal error converting NodePrepareResourcesResponse from v1alpha4 to v1beta1: %w", err)
169+
}
170+
return &convertedResp, nil
171+
}
172+
173+
func (w V1Alpha4ClientWrapper) NodeUnprepareResources(ctx context.Context, req *v1beta1.NodeUnprepareResourcesRequest, options ...grpc.CallOption) (*v1beta1.NodeUnprepareResourcesResponse, error) {
174+
var convertedReq NodeUnprepareResourcesRequest
175+
if err := Convert_v1beta1_NodeUnprepareResourcesRequest_To_v1alpha4_NodeUnprepareResourcesRequest(req, &convertedReq, nil); err != nil {
176+
return nil, fmt.Errorf("internal error converting NodeUnprepareResourcesRequest from v1beta1 to v1alpha4: %w", err)
177+
}
178+
resp, err := w.NodeClient.NodeUnprepareResources(ctx, &convertedReq, options...)
179+
if err != nil {
180+
return nil, err
181+
}
182+
var convertedResp v1beta1.NodeUnprepareResourcesResponse
183+
if err := Convert_v1alpha4_NodeUnprepareResourcesResponse_To_v1beta1_NodeUnprepareResourcesResponse(resp, &convertedResp, nil); err != nil {
184+
return nil, fmt.Errorf("internal error converting NodeUnprepareResourcesResponse from v1alpha4 to v1beta1: %w", err)
185+
}
186+
return &convertedResp, nil
187+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Package v1alpha4 contains a legacy implementation of the DRA gRPC
18+
// interface. Support for it in kubelet is provided via conversion.
19+
//
20+
// +k8s:conversion-gen=k8s.io/kubelet/pkg/apis/dra/v1beta1
21+
package v1alpha4

0 commit comments

Comments
 (0)