k8s.io/kubernetes@v1.29.3/pkg/kubelet/cm/devicemanager/plugin/v1beta1/client.go (about) 1 /* 2 Copyright 2022 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 v1beta1 18 19 import ( 20 "context" 21 "fmt" 22 "net" 23 "sync" 24 "time" 25 26 "google.golang.org/grpc" 27 "google.golang.org/grpc/credentials/insecure" 28 29 "k8s.io/klog/v2" 30 api "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 31 ) 32 33 // DevicePlugin interface provides methods for accessing Device Plugin resources, API and unix socket. 34 type DevicePlugin interface { 35 API() api.DevicePluginClient 36 Resource() string 37 SocketPath() string 38 } 39 40 // Client interface provides methods for establishing/closing gRPC connection and running the device plugin gRPC client. 41 type Client interface { 42 Connect() error 43 Run() 44 Disconnect() error 45 } 46 47 type client struct { 48 mutex sync.Mutex 49 resource string 50 socket string 51 grpc *grpc.ClientConn 52 handler ClientHandler 53 client api.DevicePluginClient 54 } 55 56 // NewPluginClient returns an initialized device plugin client. 57 func NewPluginClient(r string, socketPath string, h ClientHandler) Client { 58 return &client{ 59 resource: r, 60 socket: socketPath, 61 handler: h, 62 } 63 } 64 65 // Connect is for establishing a gRPC connection between device manager and device plugin. 66 func (c *client) Connect() error { 67 client, conn, err := dial(c.socket) 68 if err != nil { 69 klog.ErrorS(err, "Unable to connect to device plugin client with socket path", "path", c.socket) 70 return err 71 } 72 c.mutex.Lock() 73 c.grpc = conn 74 c.client = client 75 c.mutex.Unlock() 76 return c.handler.PluginConnected(c.resource, c) 77 } 78 79 // Run is for running the device plugin gRPC client. 80 func (c *client) Run() { 81 stream, err := c.client.ListAndWatch(context.Background(), &api.Empty{}) 82 if err != nil { 83 klog.ErrorS(err, "ListAndWatch ended unexpectedly for device plugin", "resource", c.resource) 84 return 85 } 86 87 for { 88 response, err := stream.Recv() 89 if err != nil { 90 klog.ErrorS(err, "ListAndWatch ended unexpectedly for device plugin", "resource", c.resource) 91 return 92 } 93 klog.V(2).InfoS("State pushed for device plugin", "resource", c.resource, "resourceCapacity", len(response.Devices)) 94 c.handler.PluginListAndWatchReceiver(c.resource, response) 95 } 96 } 97 98 // Disconnect is for closing gRPC connection between device manager and device plugin. 99 func (c *client) Disconnect() error { 100 c.mutex.Lock() 101 if c.grpc != nil { 102 if err := c.grpc.Close(); err != nil { 103 klog.V(2).ErrorS(err, "Failed to close grcp connection", "resource", c.Resource()) 104 } 105 c.grpc = nil 106 } 107 c.mutex.Unlock() 108 c.handler.PluginDisconnected(c.resource) 109 return nil 110 } 111 112 func (c *client) Resource() string { 113 return c.resource 114 } 115 116 func (c *client) API() api.DevicePluginClient { 117 return c.client 118 } 119 120 func (c *client) SocketPath() string { 121 return c.socket 122 } 123 124 // dial establishes the gRPC communication with the registered device plugin. https://godoc.org/google.golang.org/grpc#Dial 125 func dial(unixSocketPath string) (api.DevicePluginClient, *grpc.ClientConn, error) { 126 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 127 defer cancel() 128 129 c, err := grpc.DialContext(ctx, unixSocketPath, 130 grpc.WithAuthority("localhost"), 131 grpc.WithTransportCredentials(insecure.NewCredentials()), 132 grpc.WithBlock(), 133 grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) { 134 return (&net.Dialer{}).DialContext(ctx, "unix", addr) 135 }), 136 ) 137 138 if err != nil { 139 return nil, nil, fmt.Errorf(errFailedToDialDevicePlugin+" %v", err) 140 } 141 142 return api.NewDevicePluginClient(c), c, nil 143 }