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 }