k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/volume/testing/volume_host.go (about) 1 /* 2 Copyright 2020 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 testing 18 19 import ( 20 "bytes" 21 "context" 22 "fmt" 23 "net" 24 "path/filepath" 25 "strings" 26 "sync" 27 "testing" 28 "time" 29 30 authenticationv1 "k8s.io/api/authentication/v1" 31 v1 "k8s.io/api/core/v1" 32 storagev1 "k8s.io/api/storage/v1" 33 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 34 "k8s.io/apimachinery/pkg/labels" 35 "k8s.io/apimachinery/pkg/types" 36 "k8s.io/apimachinery/pkg/util/sets" 37 "k8s.io/apimachinery/pkg/util/wait" 38 "k8s.io/client-go/informers" 39 clientset "k8s.io/client-go/kubernetes" 40 storagelistersv1 "k8s.io/client-go/listers/storage/v1" 41 "k8s.io/client-go/tools/cache" 42 "k8s.io/client-go/tools/record" 43 csilibplugins "k8s.io/csi-translation-lib/plugins" 44 . "k8s.io/kubernetes/pkg/volume" 45 "k8s.io/kubernetes/pkg/volume/util/hostutil" 46 "k8s.io/kubernetes/pkg/volume/util/subpath" 47 "k8s.io/mount-utils" 48 "k8s.io/utils/exec" 49 testingexec "k8s.io/utils/exec/testing" 50 ) 51 52 type FakeVolumeHost interface { 53 VolumeHost 54 55 GetPluginMgr() *VolumePluginMgr 56 } 57 58 // fakeVolumeHost is useful for testing volume plugins. 59 // TODO: Extract fields specific to fakeKubeletVolumeHost and fakeAttachDetachVolumeHost. 60 type fakeVolumeHost struct { 61 rootDir string 62 kubeClient clientset.Interface 63 pluginMgr *VolumePluginMgr 64 mounter mount.Interface 65 hostUtil hostutil.HostUtils 66 exec *testingexec.FakeExec 67 nodeLabels map[string]string 68 nodeName string 69 subpather subpath.Interface 70 node *v1.Node 71 csiDriverLister storagelistersv1.CSIDriverLister 72 volumeAttachmentLister storagelistersv1.VolumeAttachmentLister 73 informerFactory informers.SharedInformerFactory 74 kubeletErr error 75 mux sync.Mutex 76 } 77 78 var _ VolumeHost = &fakeVolumeHost{} 79 var _ FakeVolumeHost = &fakeVolumeHost{} 80 81 func NewFakeVolumeHost(t *testing.T, rootDir string, kubeClient clientset.Interface, plugins []VolumePlugin) FakeVolumeHost { 82 return newFakeVolumeHost(t, rootDir, kubeClient, plugins, nil, "", nil, nil) 83 } 84 85 func NewFakeVolumeHostWithCloudProvider(t *testing.T, rootDir string, kubeClient clientset.Interface, plugins []VolumePlugin) FakeVolumeHost { 86 return newFakeVolumeHost(t, rootDir, kubeClient, plugins, nil, "", nil, nil) 87 } 88 89 func NewFakeVolumeHostWithCSINodeName(t *testing.T, rootDir string, kubeClient clientset.Interface, plugins []VolumePlugin, nodeName string, driverLister storagelistersv1.CSIDriverLister, volumeAttachLister storagelistersv1.VolumeAttachmentLister) FakeVolumeHost { 90 return newFakeVolumeHost(t, rootDir, kubeClient, plugins, nil, nodeName, driverLister, volumeAttachLister) 91 } 92 93 func newFakeVolumeHost(t *testing.T, rootDir string, kubeClient clientset.Interface, plugins []VolumePlugin, pathToTypeMap map[string]hostutil.FileType, nodeName string, driverLister storagelistersv1.CSIDriverLister, volumeAttachLister storagelistersv1.VolumeAttachmentLister) FakeVolumeHost { 94 host := &fakeVolumeHost{rootDir: rootDir, kubeClient: kubeClient, nodeName: nodeName, csiDriverLister: driverLister, volumeAttachmentLister: volumeAttachLister} 95 host.mounter = mount.NewFakeMounter(nil) 96 host.hostUtil = hostutil.NewFakeHostUtil(pathToTypeMap) 97 host.exec = &testingexec.FakeExec{DisableScripts: true} 98 host.pluginMgr = &VolumePluginMgr{} 99 if err := host.pluginMgr.InitPlugins(plugins, nil /* prober */, host); err != nil { 100 t.Fatalf("Failed to init plugins while creating fake volume host: %v", err) 101 } 102 host.subpather = &subpath.FakeSubpath{} 103 host.informerFactory = informers.NewSharedInformerFactory(kubeClient, time.Minute) 104 // Wait until the InitPlugins setup is finished before returning from this setup func 105 if err := host.WaitForKubeletErrNil(); err != nil { 106 t.Fatalf("Failed to wait for kubelet err to be nil while creating fake volume host: %v", err) 107 } 108 return host 109 } 110 111 func (f *fakeVolumeHost) GetPluginDir(podUID string) string { 112 return filepath.Join(f.rootDir, "plugins", podUID) 113 } 114 115 func (f *fakeVolumeHost) GetVolumeDevicePluginDir(pluginName string) string { 116 return filepath.Join(f.rootDir, "plugins", pluginName, "volumeDevices") 117 } 118 119 func (f *fakeVolumeHost) GetPodsDir() string { 120 return filepath.Join(f.rootDir, "pods") 121 } 122 123 func (f *fakeVolumeHost) GetPodVolumeDir(podUID types.UID, pluginName, volumeName string) string { 124 return filepath.Join(f.rootDir, "pods", string(podUID), "volumes", pluginName, volumeName) 125 } 126 127 func (f *fakeVolumeHost) GetPodVolumeDeviceDir(podUID types.UID, pluginName string) string { 128 return filepath.Join(f.rootDir, "pods", string(podUID), "volumeDevices", pluginName) 129 } 130 131 func (f *fakeVolumeHost) GetPodPluginDir(podUID types.UID, pluginName string) string { 132 return filepath.Join(f.rootDir, "pods", string(podUID), "plugins", pluginName) 133 } 134 135 func (f *fakeVolumeHost) GetKubeClient() clientset.Interface { 136 return f.kubeClient 137 } 138 139 func (f *fakeVolumeHost) GetMounter(pluginName string) mount.Interface { 140 return f.mounter 141 } 142 143 func (f *fakeVolumeHost) GetSubpather() subpath.Interface { 144 return f.subpather 145 } 146 147 func (f *fakeVolumeHost) GetPluginMgr() *VolumePluginMgr { 148 return f.pluginMgr 149 } 150 151 func (f *fakeVolumeHost) GetAttachedVolumesFromNodeStatus() (map[v1.UniqueVolumeName]string, error) { 152 return map[v1.UniqueVolumeName]string{}, nil 153 } 154 155 func (f *fakeVolumeHost) NewWrapperMounter(volName string, spec Spec, pod *v1.Pod, opts VolumeOptions) (Mounter, error) { 156 // The name of wrapper volume is set to "wrapped_{wrapped_volume_name}" 157 wrapperVolumeName := "wrapped_" + volName 158 if spec.Volume != nil { 159 spec.Volume.Name = wrapperVolumeName 160 } 161 plug, err := f.pluginMgr.FindPluginBySpec(&spec) 162 if err != nil { 163 return nil, err 164 } 165 return plug.NewMounter(&spec, pod, opts) 166 } 167 168 func (f *fakeVolumeHost) NewWrapperUnmounter(volName string, spec Spec, podUID types.UID) (Unmounter, error) { 169 // The name of wrapper volume is set to "wrapped_{wrapped_volume_name}" 170 wrapperVolumeName := "wrapped_" + volName 171 if spec.Volume != nil { 172 spec.Volume.Name = wrapperVolumeName 173 } 174 plug, err := f.pluginMgr.FindPluginBySpec(&spec) 175 if err != nil { 176 return nil, err 177 } 178 return plug.NewUnmounter(spec.Name(), podUID) 179 } 180 181 // Returns the hostname of the host kubelet is running on 182 func (f *fakeVolumeHost) GetHostName() string { 183 return "fakeHostName" 184 } 185 186 // Returns host IP or nil in the case of error. 187 func (f *fakeVolumeHost) GetHostIP() (net.IP, error) { 188 return nil, fmt.Errorf("GetHostIP() not implemented") 189 } 190 191 func (f *fakeVolumeHost) GetNodeAllocatable() (v1.ResourceList, error) { 192 return v1.ResourceList{}, nil 193 } 194 195 func (f *fakeVolumeHost) GetSecretFunc() func(namespace, name string) (*v1.Secret, error) { 196 return func(namespace, name string) (*v1.Secret, error) { 197 return f.kubeClient.CoreV1().Secrets(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 198 } 199 } 200 201 func (f *fakeVolumeHost) GetExec(pluginName string) exec.Interface { 202 return f.exec 203 } 204 205 func (f *fakeVolumeHost) GetConfigMapFunc() func(namespace, name string) (*v1.ConfigMap, error) { 206 return func(namespace, name string) (*v1.ConfigMap, error) { 207 return f.kubeClient.CoreV1().ConfigMaps(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 208 } 209 } 210 211 func (f *fakeVolumeHost) GetServiceAccountTokenFunc() func(string, string, *authenticationv1.TokenRequest) (*authenticationv1.TokenRequest, error) { 212 return func(namespace, name string, tr *authenticationv1.TokenRequest) (*authenticationv1.TokenRequest, error) { 213 return f.kubeClient.CoreV1().ServiceAccounts(namespace).CreateToken(context.TODO(), name, tr, metav1.CreateOptions{}) 214 } 215 } 216 217 func (f *fakeVolumeHost) DeleteServiceAccountTokenFunc() func(types.UID) { 218 return func(types.UID) {} 219 } 220 221 func (f *fakeVolumeHost) GetNodeLabels() (map[string]string, error) { 222 if f.nodeLabels == nil { 223 f.nodeLabels = map[string]string{"test-label": "test-value"} 224 } 225 return f.nodeLabels, nil 226 } 227 228 func (f *fakeVolumeHost) GetNodeName() types.NodeName { 229 return types.NodeName(f.nodeName) 230 } 231 232 func (f *fakeVolumeHost) GetEventRecorder() record.EventRecorder { 233 return nil 234 } 235 236 func (f *fakeVolumeHost) ScriptCommands(scripts []CommandScript) { 237 ScriptCommands(f.exec, scripts) 238 } 239 240 func (f *fakeVolumeHost) WaitForKubeletErrNil() error { 241 return wait.PollImmediate(10*time.Millisecond, 10*time.Second, func() (bool, error) { 242 f.mux.Lock() 243 defer f.mux.Unlock() 244 return f.kubeletErr == nil, nil 245 }) 246 } 247 248 type fakeAttachDetachVolumeHost struct { 249 fakeVolumeHost 250 } 251 252 var _ AttachDetachVolumeHost = &fakeAttachDetachVolumeHost{} 253 var _ FakeVolumeHost = &fakeAttachDetachVolumeHost{} 254 255 func NewFakeAttachDetachVolumeHostWithCSINodeName(t *testing.T, rootDir string, kubeClient clientset.Interface, plugins []VolumePlugin, nodeName string, driverLister storagelistersv1.CSIDriverLister, volumeAttachLister storagelistersv1.VolumeAttachmentLister) FakeVolumeHost { 256 return newFakeAttachDetachVolumeHost(t, rootDir, kubeClient, plugins, nil, nodeName, driverLister, volumeAttachLister) 257 } 258 259 func newFakeAttachDetachVolumeHost(t *testing.T, rootDir string, kubeClient clientset.Interface, plugins []VolumePlugin, pathToTypeMap map[string]hostutil.FileType, nodeName string, driverLister storagelistersv1.CSIDriverLister, volumeAttachLister storagelistersv1.VolumeAttachmentLister) FakeVolumeHost { 260 host := &fakeAttachDetachVolumeHost{} 261 host.rootDir = rootDir 262 host.kubeClient = kubeClient 263 host.nodeName = nodeName 264 host.csiDriverLister = driverLister 265 host.volumeAttachmentLister = volumeAttachLister 266 host.mounter = mount.NewFakeMounter(nil) 267 host.hostUtil = hostutil.NewFakeHostUtil(pathToTypeMap) 268 host.exec = &testingexec.FakeExec{DisableScripts: true} 269 host.pluginMgr = &VolumePluginMgr{} 270 if err := host.pluginMgr.InitPlugins(plugins, nil /* prober */, host); err != nil { 271 t.Fatalf("Failed to init plugins while creating fake volume host: %v", err) 272 } 273 host.subpather = &subpath.FakeSubpath{} 274 host.informerFactory = informers.NewSharedInformerFactory(kubeClient, time.Minute) 275 // Wait until the InitPlugins setup is finished before returning from this setup func 276 if err := host.WaitForKubeletErrNil(); err != nil { 277 t.Fatalf("Failed to wait for kubelet err to be nil while creating fake volume host: %v", err) 278 } 279 return host 280 } 281 282 func (f *fakeAttachDetachVolumeHost) CSINodeLister() storagelistersv1.CSINodeLister { 283 csiNode := &storagev1.CSINode{ 284 ObjectMeta: metav1.ObjectMeta{Name: f.nodeName}, 285 Spec: storagev1.CSINodeSpec{ 286 Drivers: []storagev1.CSINodeDriver{}, 287 }, 288 } 289 enableMigrationOnNode(csiNode, csilibplugins.GCEPDInTreePluginName) 290 return getFakeCSINodeLister(csiNode) 291 } 292 293 func enableMigrationOnNode(csiNode *storagev1.CSINode, pluginName string) { 294 nodeInfoAnnotations := csiNode.GetAnnotations() 295 if nodeInfoAnnotations == nil { 296 nodeInfoAnnotations = map[string]string{} 297 } 298 299 newAnnotationSet := sets.NewString() 300 newAnnotationSet.Insert(pluginName) 301 nas := strings.Join(newAnnotationSet.List(), ",") 302 nodeInfoAnnotations[v1.MigratedPluginsAnnotationKey] = nas 303 304 csiNode.Annotations = nodeInfoAnnotations 305 } 306 307 func (f *fakeAttachDetachVolumeHost) CSIDriverLister() storagelistersv1.CSIDriverLister { 308 return f.csiDriverLister 309 } 310 311 func (f *fakeAttachDetachVolumeHost) VolumeAttachmentLister() storagelistersv1.VolumeAttachmentLister { 312 return f.volumeAttachmentLister 313 } 314 315 func (f *fakeAttachDetachVolumeHost) IsAttachDetachController() bool { 316 return true 317 } 318 319 type fakeKubeletVolumeHost struct { 320 fakeVolumeHost 321 } 322 323 var _ KubeletVolumeHost = &fakeKubeletVolumeHost{} 324 var _ FakeVolumeHost = &fakeKubeletVolumeHost{} 325 326 func NewFakeKubeletVolumeHost(t *testing.T, rootDir string, kubeClient clientset.Interface, plugins []VolumePlugin) *fakeKubeletVolumeHost { 327 return newFakeKubeletVolumeHost(t, rootDir, kubeClient, plugins, nil, "", nil, nil) 328 } 329 330 func NewFakeKubeletVolumeHostWithCSINodeName(t *testing.T, rootDir string, kubeClient clientset.Interface, plugins []VolumePlugin, nodeName string, driverLister storagelistersv1.CSIDriverLister, volumeAttachLister storagelistersv1.VolumeAttachmentLister) *fakeKubeletVolumeHost { 331 return newFakeKubeletVolumeHost(t, rootDir, kubeClient, plugins, nil, nodeName, driverLister, volumeAttachLister) 332 } 333 334 func NewFakeKubeletVolumeHostWithMounterFSType(t *testing.T, rootDir string, kubeClient clientset.Interface, plugins []VolumePlugin, pathToTypeMap map[string]hostutil.FileType) *fakeKubeletVolumeHost { 335 return newFakeKubeletVolumeHost(t, rootDir, kubeClient, plugins, pathToTypeMap, "", nil, nil) 336 } 337 338 func newFakeKubeletVolumeHost(t *testing.T, rootDir string, kubeClient clientset.Interface, plugins []VolumePlugin, pathToTypeMap map[string]hostutil.FileType, nodeName string, driverLister storagelistersv1.CSIDriverLister, volumeAttachLister storagelistersv1.VolumeAttachmentLister) *fakeKubeletVolumeHost { 339 host := &fakeKubeletVolumeHost{} 340 host.rootDir = rootDir 341 host.kubeClient = kubeClient 342 host.nodeName = nodeName 343 host.csiDriverLister = driverLister 344 host.volumeAttachmentLister = volumeAttachLister 345 host.mounter = mount.NewFakeMounter(nil) 346 host.hostUtil = hostutil.NewFakeHostUtil(pathToTypeMap) 347 host.exec = &testingexec.FakeExec{DisableScripts: true} 348 host.pluginMgr = &VolumePluginMgr{} 349 if err := host.pluginMgr.InitPlugins(plugins, nil /* prober */, host); err != nil { 350 t.Fatalf("Failed to init plugins while creating fake volume host: %v", err) 351 } 352 host.subpather = &subpath.FakeSubpath{} 353 host.informerFactory = informers.NewSharedInformerFactory(kubeClient, time.Minute) 354 // Wait until the InitPlugins setup is finished before returning from this setup func 355 if err := host.WaitForKubeletErrNil(); err != nil { 356 t.Fatalf("Failed to wait for kubelet err to be nil while creating fake volume host: %v", err) 357 } 358 return host 359 } 360 361 func (f *fakeKubeletVolumeHost) WithNode(node *v1.Node) *fakeKubeletVolumeHost { 362 f.node = node 363 return f 364 } 365 366 type CSINodeLister []storagev1.CSINode 367 368 // Get returns a fake CSINode object. 369 func (n CSINodeLister) Get(name string) (*storagev1.CSINode, error) { 370 for _, cn := range n { 371 if cn.Name == name { 372 return &cn, nil 373 } 374 } 375 return nil, fmt.Errorf("csiNode %q not found", name) 376 } 377 378 // List lists all CSINodes in the indexer. 379 func (n CSINodeLister) List(selector labels.Selector) (ret []*storagev1.CSINode, err error) { 380 return nil, fmt.Errorf("not implemented") 381 } 382 383 func getFakeCSINodeLister(csiNode *storagev1.CSINode) CSINodeLister { 384 csiNodeLister := CSINodeLister{} 385 if csiNode != nil { 386 csiNodeLister = append(csiNodeLister, *csiNode.DeepCopy()) 387 } 388 return csiNodeLister 389 } 390 391 func (f *fakeKubeletVolumeHost) SetKubeletError(err error) { 392 f.mux.Lock() 393 defer f.mux.Unlock() 394 f.kubeletErr = err 395 } 396 397 func (f *fakeKubeletVolumeHost) GetInformerFactory() informers.SharedInformerFactory { 398 return f.informerFactory 399 } 400 401 func (f *fakeKubeletVolumeHost) GetAttachedVolumesFromNodeStatus() (map[v1.UniqueVolumeName]string, error) { 402 result := map[v1.UniqueVolumeName]string{} 403 if f.node != nil { 404 for _, av := range f.node.Status.VolumesAttached { 405 result[av.Name] = av.DevicePath 406 } 407 } 408 409 return result, nil 410 } 411 412 func (f *fakeKubeletVolumeHost) CSIDriverLister() storagelistersv1.CSIDriverLister { 413 return f.csiDriverLister 414 } 415 416 func (f *fakeKubeletVolumeHost) CSIDriversSynced() cache.InformerSynced { 417 // not needed for testing 418 return nil 419 } 420 421 func (f *fakeKubeletVolumeHost) WaitForCacheSync() error { 422 return nil 423 } 424 425 func (f *fakeKubeletVolumeHost) GetHostUtil() hostutil.HostUtils { 426 return f.hostUtil 427 } 428 429 func (f *fakeKubeletVolumeHost) GetTrustAnchorsByName(name string, allowMissing bool) ([]byte, error) { 430 ctb, err := f.kubeClient.CertificatesV1alpha1().ClusterTrustBundles().Get(context.Background(), name, metav1.GetOptions{}) 431 if err != nil { 432 return nil, fmt.Errorf("while getting ClusterTrustBundle %s: %w", name, err) 433 } 434 435 return []byte(ctb.Spec.TrustBundle), nil 436 } 437 438 // Note: we do none of the deduplication and sorting that the real deal should do. 439 func (f *fakeKubeletVolumeHost) GetTrustAnchorsBySigner(signerName string, labelSelector *metav1.LabelSelector, allowMissing bool) ([]byte, error) { 440 ctbList, err := f.kubeClient.CertificatesV1alpha1().ClusterTrustBundles().List(context.Background(), metav1.ListOptions{}) 441 if err != nil { 442 return nil, fmt.Errorf("while listing all ClusterTrustBundles: %w", err) 443 } 444 445 fullSet := bytes.Buffer{} 446 for i, ctb := range ctbList.Items { 447 fullSet.WriteString(ctb.Spec.TrustBundle) 448 if i != len(ctbList.Items)-1 { 449 fullSet.WriteString("\n") 450 } 451 } 452 453 return fullSet.Bytes(), nil 454 }