k8s.io/kubernetes@v1.29.3/pkg/kubelet/pluginmanager/cache/desired_state_of_world.go (about) 1 /* 2 Copyright 2019 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 /* 18 Package cache implements data structures used by the kubelet plugin manager to 19 keep track of registered plugins. 20 */ 21 package cache 22 23 import ( 24 "fmt" 25 "sync" 26 "time" 27 28 "k8s.io/klog/v2" 29 ) 30 31 // DesiredStateOfWorld defines a set of thread-safe operations for the kubelet 32 // plugin manager's desired state of the world cache. 33 // This cache contains a map of socket file path to plugin information of 34 // all plugins attached to this node. 35 type DesiredStateOfWorld interface { 36 // AddOrUpdatePlugin add the given plugin in the cache if it doesn't already exist. 37 // If it does exist in the cache, then the timestamp of the PluginInfo object in the cache will be updated. 38 // An error will be returned if socketPath is empty. 39 AddOrUpdatePlugin(socketPath string) error 40 41 // RemovePlugin deletes the plugin with the given socket path from the desired 42 // state of world. 43 // If a plugin does not exist with the given socket path, this is a no-op. 44 RemovePlugin(socketPath string) 45 46 // GetPluginsToRegister generates and returns a list of plugins 47 // in the current desired state of world. 48 GetPluginsToRegister() []PluginInfo 49 50 // PluginExists checks if the given socket path exists in the current desired 51 // state of world cache 52 PluginExists(socketPath string) bool 53 } 54 55 // NewDesiredStateOfWorld returns a new instance of DesiredStateOfWorld. 56 func NewDesiredStateOfWorld() DesiredStateOfWorld { 57 return &desiredStateOfWorld{ 58 socketFileToInfo: make(map[string]PluginInfo), 59 } 60 } 61 62 type desiredStateOfWorld struct { 63 64 // socketFileToInfo is a map containing the set of successfully registered plugins 65 // The keys are plugin socket file paths. The values are PluginInfo objects 66 socketFileToInfo map[string]PluginInfo 67 sync.RWMutex 68 } 69 70 var _ DesiredStateOfWorld = &desiredStateOfWorld{} 71 72 // Generate a detailed error msg for logs 73 func generatePluginMsgDetailed(prefixMsg, suffixMsg, socketPath, details string) (detailedMsg string) { 74 return fmt.Sprintf("%v for plugin at %q %v %v", prefixMsg, socketPath, details, suffixMsg) 75 } 76 77 // Generate a simplified error msg for events and a detailed error msg for logs 78 func generatePluginMsg(prefixMsg, suffixMsg, socketPath, details string) (simpleMsg, detailedMsg string) { 79 simpleMsg = fmt.Sprintf("%v for plugin at %q %v", prefixMsg, socketPath, suffixMsg) 80 return simpleMsg, generatePluginMsgDetailed(prefixMsg, suffixMsg, socketPath, details) 81 } 82 83 // GenerateMsgDetailed returns detailed msgs for plugins to register 84 // that can be used in logs. 85 // The msg format follows the pattern "<prefixMsg> <plugin details> <suffixMsg>" 86 func (plugin *PluginInfo) GenerateMsgDetailed(prefixMsg, suffixMsg string) (detailedMsg string) { 87 detailedStr := fmt.Sprintf("(plugin details: %v)", plugin) 88 return generatePluginMsgDetailed(prefixMsg, suffixMsg, plugin.SocketPath, detailedStr) 89 } 90 91 // GenerateMsg returns simple and detailed msgs for plugins to register 92 // that is user friendly and a detailed msg that can be used in logs. 93 // The msg format follows the pattern "<prefixMsg> <plugin details> <suffixMsg>". 94 func (plugin *PluginInfo) GenerateMsg(prefixMsg, suffixMsg string) (simpleMsg, detailedMsg string) { 95 detailedStr := fmt.Sprintf("(plugin details: %v)", plugin) 96 return generatePluginMsg(prefixMsg, suffixMsg, plugin.SocketPath, detailedStr) 97 } 98 99 // GenerateErrorDetailed returns detailed errors for plugins to register 100 // that can be used in logs. 101 // The msg format follows the pattern "<prefixMsg> <plugin details>: <err> ", 102 func (plugin *PluginInfo) GenerateErrorDetailed(prefixMsg string, err error) (detailedErr error) { 103 return fmt.Errorf(plugin.GenerateMsgDetailed(prefixMsg, errSuffix(err))) 104 } 105 106 // GenerateError returns simple and detailed errors for plugins to register 107 // that is user friendly and a detailed error that can be used in logs. 108 // The msg format follows the pattern "<prefixMsg> <plugin details>: <err> ". 109 func (plugin *PluginInfo) GenerateError(prefixMsg string, err error) (simpleErr, detailedErr error) { 110 simpleMsg, detailedMsg := plugin.GenerateMsg(prefixMsg, errSuffix(err)) 111 return fmt.Errorf(simpleMsg), fmt.Errorf(detailedMsg) 112 } 113 114 // Generates an error string with the format ": <err>" if err exists 115 func errSuffix(err error) string { 116 errStr := "" 117 if err != nil { 118 errStr = fmt.Sprintf(": %v", err) 119 } 120 return errStr 121 } 122 123 func (dsw *desiredStateOfWorld) AddOrUpdatePlugin(socketPath string) error { 124 dsw.Lock() 125 defer dsw.Unlock() 126 127 if socketPath == "" { 128 return fmt.Errorf("socket path is empty") 129 } 130 if _, ok := dsw.socketFileToInfo[socketPath]; ok { 131 klog.V(2).InfoS("Plugin exists in desired state cache, timestamp will be updated", "path", socketPath) 132 } 133 134 // Update the PluginInfo object. 135 // Note that we only update the timestamp in the desired state of world, not the actual state of world 136 // because in the reconciler, we need to check if the plugin in the actual state of world is the same 137 // version as the plugin in the desired state of world 138 dsw.socketFileToInfo[socketPath] = PluginInfo{ 139 SocketPath: socketPath, 140 Timestamp: time.Now(), 141 } 142 return nil 143 } 144 145 func (dsw *desiredStateOfWorld) RemovePlugin(socketPath string) { 146 dsw.Lock() 147 defer dsw.Unlock() 148 149 delete(dsw.socketFileToInfo, socketPath) 150 } 151 152 func (dsw *desiredStateOfWorld) GetPluginsToRegister() []PluginInfo { 153 dsw.RLock() 154 defer dsw.RUnlock() 155 156 pluginsToRegister := []PluginInfo{} 157 for _, pluginInfo := range dsw.socketFileToInfo { 158 pluginsToRegister = append(pluginsToRegister, pluginInfo) 159 } 160 return pluginsToRegister 161 } 162 163 func (dsw *desiredStateOfWorld) PluginExists(socketPath string) bool { 164 dsw.RLock() 165 defer dsw.RUnlock() 166 167 _, exists := dsw.socketFileToInfo[socketPath] 168 return exists 169 }