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  }