github.com/kubewharf/katalyst-core@v0.5.3/pkg/agent/orm/endpoint/resource_plugin_stub.go (about)

     1  /*
     2  Copyright 2022 The Katalyst 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 endpoint
    18  
    19  import (
    20  	"context"
    21  	"log"
    22  	"net"
    23  	"os"
    24  	"path"
    25  	"sync"
    26  	"time"
    27  
    28  	"google.golang.org/grpc/credentials/insecure"
    29  
    30  	"google.golang.org/grpc"
    31  	watcherapi "k8s.io/kubelet/pkg/apis/pluginregistration/v1"
    32  	pluginapi "k8s.io/kubelet/pkg/apis/resourceplugin/v1alpha1"
    33  )
    34  
    35  type Stub struct {
    36  	socket                string
    37  	resourceName          string
    38  	preStartContainerFlag bool
    39  
    40  	stop chan interface{}
    41  	wg   sync.WaitGroup
    42  
    43  	server *grpc.Server
    44  
    45  	// allocFunc1 is used for handling allocation request
    46  	allocFunc1 stubAllocFunc1
    47  	// handling get allocation request
    48  	allocFunc2 stubAllocFunc2
    49  
    50  	getTopologyAwareAllocatableResourcesFunc stubGetTopologyAwareAllocatableResourcesFunc
    51  	getTopologyAwareResourcesFunc            stubGetTopologyAwareResourcesFunc
    52  
    53  	registrationStatus chan watcherapi.RegistrationStatus // for testing
    54  	endpoint           string                             // for testing
    55  }
    56  
    57  // stubAllocFunc1 is the function called when an allocation request is received from Kubelet
    58  type stubAllocFunc1 func(r *pluginapi.ResourceRequest) (*pluginapi.ResourceAllocationResponse, error)
    59  
    60  // stubAllocFYnc2 is the function called when a get allocation request is received form Kubelet
    61  type stubAllocFunc2 func(r *pluginapi.GetResourcesAllocationRequest) (*pluginapi.GetResourcesAllocationResponse, error)
    62  
    63  type stubGetTopologyAwareAllocatableResourcesFunc func(r *pluginapi.GetTopologyAwareAllocatableResourcesRequest) (*pluginapi.GetTopologyAwareAllocatableResourcesResponse, error)
    64  
    65  type stubGetTopologyAwareResourcesFunc func(r *pluginapi.GetTopologyAwareResourcesRequest) (*pluginapi.GetTopologyAwareResourcesResponse, error)
    66  
    67  func defaultAllocFunc(r *pluginapi.ResourceRequest) (*pluginapi.ResourceAllocationResponse, error) {
    68  	var response pluginapi.ResourceAllocationResponse
    69  
    70  	return &response, nil
    71  }
    72  
    73  func defaultGetAllocFunc(r *pluginapi.GetResourcesAllocationRequest) (*pluginapi.GetResourcesAllocationResponse, error) {
    74  	var response pluginapi.GetResourcesAllocationResponse
    75  	return &response, nil
    76  }
    77  
    78  // NewResourcePluginStub returns an initialized ResourcePlugin Stub.
    79  func NewResourcePluginStub(socket string, name string, preStartContainerFlag bool) *Stub {
    80  	return &Stub{
    81  		socket:                socket,
    82  		resourceName:          name,
    83  		preStartContainerFlag: preStartContainerFlag,
    84  
    85  		stop: make(chan interface{}),
    86  
    87  		allocFunc1: defaultAllocFunc,
    88  		allocFunc2: defaultGetAllocFunc,
    89  	}
    90  }
    91  
    92  // SetAllocFunc sets allocFunc of the resource plugin
    93  func (m *Stub) SetAllocFunc(f stubAllocFunc1) {
    94  	m.allocFunc1 = f
    95  }
    96  
    97  func (m *Stub) SetGetAllocFunc(f stubAllocFunc2) {
    98  	m.allocFunc2 = f
    99  }
   100  
   101  func (m *Stub) SetGetTopologyAwareAllocatableResourcesFunc(f stubGetTopologyAwareAllocatableResourcesFunc) {
   102  	m.getTopologyAwareAllocatableResourcesFunc = f
   103  }
   104  
   105  func (m *Stub) SetGetTopologyAwareResourcesFunc(f stubGetTopologyAwareResourcesFunc) {
   106  	m.getTopologyAwareResourcesFunc = f
   107  }
   108  
   109  // Start starts the gRPC server of the resource plugin. Can only
   110  // be called once.
   111  func (m *Stub) Start() error {
   112  	err := m.cleanup()
   113  	if err != nil {
   114  		return err
   115  	}
   116  
   117  	sock, err := net.Listen("unix", m.socket)
   118  	if err != nil {
   119  		return err
   120  	}
   121  
   122  	m.wg.Add(1)
   123  	m.server = grpc.NewServer([]grpc.ServerOption{}...)
   124  	pluginapi.RegisterResourcePluginServer(m.server, m)
   125  	watcherapi.RegisterRegistrationServer(m.server, m)
   126  
   127  	go func() {
   128  		defer func() {
   129  			m.wg.Done()
   130  
   131  			if err := recover(); err != nil {
   132  				log.Fatalf("Start recover from err: %v", err)
   133  			}
   134  		}()
   135  		m.server.Serve(sock)
   136  	}()
   137  	_, conn, err := dial(m.socket)
   138  	if err != nil {
   139  		return err
   140  	}
   141  	conn.Close()
   142  	log.Printf("Starting to serve on %v", m.socket)
   143  
   144  	return nil
   145  }
   146  
   147  // Stop stops the gRPC server. Can be called without a prior Start
   148  // and more than once. Not safe to be called concurrently by different
   149  // goroutines!
   150  func (m *Stub) Stop() error {
   151  	if m.server == nil {
   152  		return nil
   153  	}
   154  	m.server.Stop()
   155  	m.wg.Wait()
   156  	m.server = nil
   157  	close(m.stop) // This prevents re-starting the server.
   158  
   159  	return m.cleanup()
   160  }
   161  
   162  // GetInfo is the RPC which return pluginInfo
   163  func (m *Stub) GetInfo(ctx context.Context, req *watcherapi.InfoRequest) (*watcherapi.PluginInfo, error) {
   164  	log.Println("GetInfo")
   165  	return &watcherapi.PluginInfo{
   166  		Type:              watcherapi.ResourcePlugin,
   167  		Name:              m.resourceName,
   168  		Endpoint:          m.endpoint,
   169  		SupportedVersions: []string{pluginapi.Version},
   170  	}, nil
   171  }
   172  
   173  // NotifyRegistrationStatus receives the registration notification from watcher
   174  func (m *Stub) NotifyRegistrationStatus(ctx context.Context, status *watcherapi.RegistrationStatus) (*watcherapi.RegistrationStatusResponse, error) {
   175  	if m.registrationStatus != nil {
   176  		m.registrationStatus <- *status
   177  	}
   178  	if !status.PluginRegistered {
   179  		log.Printf("Registration failed: %v", status.Error)
   180  	}
   181  	return &watcherapi.RegistrationStatusResponse{}, nil
   182  }
   183  
   184  // Register registers the resource plugin for the given resourceName with Kubelet.
   185  func (m *Stub) Register(kubeletEndpoint, resourceName string, pluginSockDir string) error {
   186  	if pluginSockDir != "" {
   187  		if _, err := os.Stat(pluginSockDir + "DEPRECATION"); err == nil {
   188  			log.Println("Deprecation file found. Skip registration.")
   189  			return nil
   190  		}
   191  	}
   192  	log.Println("Deprecation file not found. Invoke registration")
   193  	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
   194  	defer cancel()
   195  
   196  	conn, err := grpc.DialContext(ctx, kubeletEndpoint, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock(),
   197  		grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) {
   198  			return (&net.Dialer{}).DialContext(ctx, "unix", addr)
   199  		}))
   200  	if err != nil {
   201  		return err
   202  	}
   203  	defer conn.Close()
   204  	client := pluginapi.NewRegistrationClient(conn)
   205  	reqt := &pluginapi.RegisterRequest{
   206  		Version:      pluginapi.Version,
   207  		Endpoint:     path.Base(m.socket),
   208  		ResourceName: resourceName,
   209  		Options: &pluginapi.ResourcePluginOptions{
   210  			PreStartRequired: m.preStartContainerFlag,
   211  		},
   212  	}
   213  
   214  	_, err = client.Register(context.Background(), reqt)
   215  	if err != nil {
   216  		return err
   217  	}
   218  	return nil
   219  }
   220  
   221  // GetResourcePluginOptions returns ResourcePluginOptions settings for the resource plugin.
   222  func (m *Stub) GetResourcePluginOptions(ctx context.Context, e *pluginapi.Empty) (*pluginapi.ResourcePluginOptions, error) {
   223  	options := &pluginapi.ResourcePluginOptions{
   224  		PreStartRequired: m.preStartContainerFlag,
   225  	}
   226  	return options, nil
   227  }
   228  
   229  // PreStartContainer resets the resources received
   230  func (m *Stub) PreStartContainer(ctx context.Context, r *pluginapi.PreStartContainerRequest) (*pluginapi.PreStartContainerResponse, error) {
   231  	log.Printf("PreStartContainer, %+v", r)
   232  	return &pluginapi.PreStartContainerResponse{}, nil
   233  }
   234  
   235  // Allocate does a mock allocation
   236  func (m *Stub) Allocate(ctx context.Context, r *pluginapi.ResourceRequest) (*pluginapi.ResourceAllocationResponse, error) {
   237  	log.Printf("Allocate, %+v", r)
   238  
   239  	return m.allocFunc1(r)
   240  }
   241  
   242  func (m *Stub) cleanup() error {
   243  	if err := os.Remove(m.socket); err != nil && !os.IsNotExist(err) {
   244  		return err
   245  	}
   246  
   247  	return nil
   248  }
   249  
   250  // GetResourcesAllocation returns allocation results of corresponding resources
   251  func (m *Stub) GetResourcesAllocation(ctx context.Context, r *pluginapi.GetResourcesAllocationRequest) (*pluginapi.GetResourcesAllocationResponse, error) {
   252  	log.Printf("GetResourcesAllocation, %+v", r)
   253  	return m.allocFunc2(r)
   254  }
   255  
   256  // GetTopologyAwareResources returns allocation results of corresponding resources as topology aware format
   257  func (m *Stub) GetTopologyAwareResources(ctx context.Context, r *pluginapi.GetTopologyAwareResourcesRequest) (*pluginapi.GetTopologyAwareResourcesResponse, error) {
   258  	log.Printf("GetTopologyAwareResources, %+v", r)
   259  	return m.getTopologyAwareResourcesFunc(r)
   260  }
   261  
   262  // GetTopologyAwareResources returns corresponding allocatable resources as topology aware format
   263  func (m *Stub) GetTopologyAwareAllocatableResources(ctx context.Context, r *pluginapi.GetTopologyAwareAllocatableResourcesRequest) (*pluginapi.GetTopologyAwareAllocatableResourcesResponse, error) {
   264  	log.Printf("GetTopologyAwareAllocatableResources, %+v", r)
   265  	return m.getTopologyAwareAllocatableResourcesFunc(r)
   266  }
   267  
   268  // GetTopologyHints returns hints of corresponding resources
   269  func (m *Stub) GetTopologyHints(ctx context.Context, r *pluginapi.ResourceRequest) (*pluginapi.ResourceHintsResponse, error) {
   270  	log.Printf("GetTopologyHints, %+v", r)
   271  	return &pluginapi.ResourceHintsResponse{}, nil
   272  }
   273  
   274  // Notify the resource plugin that the pod has beed deleted,
   275  // and the plugin should do some clear-up work.
   276  func (m *Stub) RemovePod(ctx context.Context, r *pluginapi.RemovePodRequest) (*pluginapi.RemovePodResponse, error) {
   277  	log.Printf("RemovePod, %+v", r)
   278  	return &pluginapi.RemovePodResponse{}, nil
   279  }