k8s.io/kubernetes@v1.29.3/pkg/volume/rbd/rbd.go (about) 1 /* 2 Copyright 2014 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 rbd 18 19 import ( 20 "context" 21 "fmt" 22 "os" 23 "path/filepath" 24 "regexp" 25 dstrings "strings" 26 27 v1 "k8s.io/api/core/v1" 28 "k8s.io/apimachinery/pkg/api/resource" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/types" 31 "k8s.io/apimachinery/pkg/util/sets" 32 "k8s.io/apimachinery/pkg/util/uuid" 33 utilfeature "k8s.io/apiserver/pkg/util/feature" 34 clientset "k8s.io/client-go/kubernetes" 35 "k8s.io/klog/v2" 36 "k8s.io/kubernetes/pkg/features" 37 "k8s.io/kubernetes/pkg/volume" 38 volutil "k8s.io/kubernetes/pkg/volume/util" 39 "k8s.io/kubernetes/pkg/volume/util/volumepathhandler" 40 "k8s.io/mount-utils" 41 utilexec "k8s.io/utils/exec" 42 utilstrings "k8s.io/utils/strings" 43 ) 44 45 var ( 46 supportedFeatures = sets.NewString("layering") 47 pathSeparator = string(os.PathSeparator) 48 ) 49 50 // ProbeVolumePlugins is the primary entrypoint for volume plugins. 51 func ProbeVolumePlugins() []volume.VolumePlugin { 52 return []volume.VolumePlugin{&rbdPlugin{}} 53 } 54 55 // rbdPlugin implements Volume.VolumePlugin. 56 type rbdPlugin struct { 57 host volume.VolumeHost 58 } 59 60 var _ volume.VolumePlugin = &rbdPlugin{} 61 var _ volume.PersistentVolumePlugin = &rbdPlugin{} 62 var _ volume.DeletableVolumePlugin = &rbdPlugin{} 63 var _ volume.ProvisionableVolumePlugin = &rbdPlugin{} 64 var _ volume.AttachableVolumePlugin = &rbdPlugin{} 65 var _ volume.ExpandableVolumePlugin = &rbdPlugin{} 66 var _ volume.BlockVolumePlugin = &rbdPlugin{} 67 var _ volume.DeviceMountableVolumePlugin = &rbdPlugin{} 68 69 const ( 70 rbdPluginName = "kubernetes.io/rbd" 71 secretKeyName = "key" // key name used in secret 72 rbdImageFormat1 = "1" 73 rbdImageFormat2 = "2" 74 rbdDefaultAdminID = "admin" 75 rbdDefaultAdminSecretNamespace = "default" 76 rbdDefaultPool = "rbd" 77 ) 78 79 func getPath(uid types.UID, volName string, host volume.VolumeHost) string { 80 return host.GetPodVolumeDir(uid, utilstrings.EscapeQualifiedName(rbdPluginName), volName) 81 } 82 83 func (plugin *rbdPlugin) IsMigratedToCSI() bool { 84 return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationRBD) 85 } 86 87 func (plugin *rbdPlugin) Init(host volume.VolumeHost) error { 88 plugin.host = host 89 return nil 90 } 91 92 func (plugin *rbdPlugin) GetPluginName() string { 93 return rbdPluginName 94 } 95 96 func (plugin *rbdPlugin) GetVolumeName(spec *volume.Spec) (string, error) { 97 pool, err := getVolumeSourcePool(spec) 98 if err != nil { 99 return "", err 100 } 101 img, err := getVolumeSourceImage(spec) 102 if err != nil { 103 return "", err 104 } 105 106 return fmt.Sprintf( 107 "%v:%v", 108 pool, 109 img), nil 110 } 111 112 func (plugin *rbdPlugin) CanSupport(spec *volume.Spec) bool { 113 return (spec.Volume != nil && spec.Volume.RBD != nil) || (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.RBD != nil) 114 } 115 116 func (plugin *rbdPlugin) RequiresRemount(spec *volume.Spec) bool { 117 return false 118 } 119 120 func (plugin *rbdPlugin) SupportsMountOption() bool { 121 return true 122 } 123 124 func (plugin *rbdPlugin) SupportsBulkVolumeVerification() bool { 125 return false 126 } 127 128 func (plugin *rbdPlugin) SupportsSELinuxContextMount(spec *volume.Spec) (bool, error) { 129 return true, nil 130 } 131 132 func (plugin *rbdPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode { 133 return []v1.PersistentVolumeAccessMode{ 134 v1.ReadWriteOnce, 135 v1.ReadOnlyMany, 136 } 137 } 138 139 type rbdVolumeExpander struct { 140 *rbdMounter 141 } 142 143 func (plugin *rbdPlugin) getAdminAndSecret(spec *volume.Spec) (string, string, error) { 144 class, err := volutil.GetClassForVolume(plugin.host.GetKubeClient(), spec.PersistentVolume) 145 if err != nil { 146 return "", "", err 147 } 148 adminSecretName := "" 149 adminSecretNamespace := rbdDefaultAdminSecretNamespace 150 admin := "" 151 152 for k, v := range class.Parameters { 153 switch dstrings.ToLower(k) { 154 case "adminid": 155 admin = v 156 case "adminsecretname": 157 adminSecretName = v 158 case "adminsecretnamespace": 159 adminSecretNamespace = v 160 } 161 } 162 163 if admin == "" { 164 admin = rbdDefaultAdminID 165 } 166 secret, err := parsePVSecret(adminSecretNamespace, adminSecretName, plugin.host.GetKubeClient()) 167 if err != nil { 168 return admin, "", fmt.Errorf("failed to get admin secret from [%q/%q]: %v", adminSecretNamespace, adminSecretName, err) 169 } 170 171 return admin, secret, nil 172 } 173 174 func (plugin *rbdPlugin) ExpandVolumeDevice(spec *volume.Spec, newSize resource.Quantity, oldSize resource.Quantity) (resource.Quantity, error) { 175 if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.RBD == nil { 176 return oldSize, fmt.Errorf("spec.PersistentVolume.Spec.RBD is nil") 177 } 178 179 // get admin and secret 180 admin, secret, err := plugin.getAdminAndSecret(spec) 181 if err != nil { 182 return oldSize, err 183 } 184 185 expander := &rbdVolumeExpander{ 186 rbdMounter: &rbdMounter{ 187 rbd: &rbd{ 188 volName: spec.Name(), 189 Image: spec.PersistentVolume.Spec.RBD.RBDImage, 190 Pool: spec.PersistentVolume.Spec.RBD.RBDPool, 191 plugin: plugin, 192 manager: &rbdUtil{}, 193 mounter: &mount.SafeFormatAndMount{Interface: plugin.host.GetMounter(plugin.GetPluginName())}, 194 exec: plugin.host.GetExec(plugin.GetPluginName()), 195 }, 196 Mon: spec.PersistentVolume.Spec.RBD.CephMonitors, 197 adminID: admin, 198 adminSecret: secret, 199 }, 200 } 201 202 expandedSize, err := expander.ResizeImage(oldSize, newSize) 203 if err != nil { 204 return oldSize, err 205 } 206 return expandedSize, nil 207 208 } 209 210 func (plugin *rbdPlugin) NodeExpand(resizeOptions volume.NodeResizeOptions) (bool, error) { 211 fsVolume, err := volutil.CheckVolumeModeFilesystem(resizeOptions.VolumeSpec) 212 if err != nil { 213 return false, fmt.Errorf("error checking VolumeMode: %v", err) 214 } 215 // if volume is not a fs file system, there is nothing for us to do here. 216 if !fsVolume { 217 return true, nil 218 } 219 _, err = volutil.GenericResizeFS(plugin.host, plugin.GetPluginName(), resizeOptions.DevicePath, resizeOptions.DeviceMountPath) 220 if err != nil { 221 return false, err 222 } 223 return true, nil 224 } 225 226 var _ volume.NodeExpandableVolumePlugin = &rbdPlugin{} 227 228 func (expander *rbdVolumeExpander) ResizeImage(oldSize resource.Quantity, newSize resource.Quantity) (resource.Quantity, error) { 229 return expander.manager.ExpandImage(expander, oldSize, newSize) 230 } 231 232 func (plugin *rbdPlugin) RequiresFSResize() bool { 233 return true 234 } 235 236 func (plugin *rbdPlugin) createMounterFromVolumeSpecAndPod(spec *volume.Spec, pod *v1.Pod) (*rbdMounter, error) { 237 var err error 238 mon, err := getVolumeSourceMonitors(spec) 239 if err != nil { 240 return nil, err 241 } 242 img, err := getVolumeSourceImage(spec) 243 if err != nil { 244 return nil, err 245 } 246 fstype, err := getVolumeSourceFSType(spec) 247 if err != nil { 248 return nil, err 249 } 250 pool, err := getVolumeSourcePool(spec) 251 if err != nil { 252 return nil, err 253 } 254 id, err := getVolumeSourceUser(spec) 255 if err != nil { 256 return nil, err 257 } 258 keyring, err := getVolumeSourceKeyRing(spec) 259 if err != nil { 260 return nil, err 261 } 262 ro, err := getVolumeSourceReadOnly(spec) 263 if err != nil { 264 return nil, err 265 } 266 ams, err := getVolumeAccessModes(spec) 267 if err != nil { 268 return nil, err 269 } 270 271 secretName, secretNs, err := getSecretNameAndNamespace(spec, pod.Namespace) 272 if err != nil { 273 return nil, err 274 } 275 secret := "" 276 if len(secretName) > 0 && len(secretNs) > 0 { 277 // if secret is provideded, retrieve it 278 kubeClient := plugin.host.GetKubeClient() 279 if kubeClient == nil { 280 return nil, fmt.Errorf("cannot get kube client") 281 } 282 secrets, err := kubeClient.CoreV1().Secrets(secretNs).Get(context.TODO(), secretName, metav1.GetOptions{}) 283 if err != nil { 284 err = fmt.Errorf("couldn't get secret %v/%v err: %v", secretNs, secretName, err) 285 return nil, err 286 } 287 for _, data := range secrets.Data { 288 secret = string(data) 289 } 290 } 291 292 return &rbdMounter{ 293 rbd: newRBD("", spec.Name(), img, pool, ro, plugin, &rbdUtil{}), 294 Mon: mon, 295 ID: id, 296 Keyring: keyring, 297 Secret: secret, 298 fsType: fstype, 299 accessModes: ams, 300 }, nil 301 } 302 303 func (plugin *rbdPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) { 304 secretName, secretNs, err := getSecretNameAndNamespace(spec, pod.Namespace) 305 if err != nil { 306 return nil, err 307 } 308 secret := "" 309 if len(secretName) > 0 && len(secretNs) > 0 { 310 // if secret is provideded, retrieve it 311 kubeClient := plugin.host.GetKubeClient() 312 if kubeClient == nil { 313 return nil, fmt.Errorf("cannot get kube client") 314 } 315 secrets, err := kubeClient.CoreV1().Secrets(secretNs).Get(context.TODO(), secretName, metav1.GetOptions{}) 316 if err != nil { 317 err = fmt.Errorf("couldn't get secret %v/%v err: %v", secretNs, secretName, err) 318 return nil, err 319 } 320 for _, data := range secrets.Data { 321 secret = string(data) 322 } 323 } 324 325 // Inject real implementations here, test through the internal function. 326 return plugin.newMounterInternal(spec, pod.UID, &rbdUtil{}, secret) 327 } 328 329 func (plugin *rbdPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager diskManager, secret string) (volume.Mounter, error) { 330 mon, err := getVolumeSourceMonitors(spec) 331 if err != nil { 332 return nil, err 333 } 334 img, err := getVolumeSourceImage(spec) 335 if err != nil { 336 return nil, err 337 } 338 fstype, err := getVolumeSourceFSType(spec) 339 if err != nil { 340 return nil, err 341 } 342 pool, err := getVolumeSourcePool(spec) 343 if err != nil { 344 return nil, err 345 } 346 id, err := getVolumeSourceUser(spec) 347 if err != nil { 348 return nil, err 349 } 350 keyring, err := getVolumeSourceKeyRing(spec) 351 if err != nil { 352 return nil, err 353 } 354 ro, err := getVolumeSourceReadOnly(spec) 355 if err != nil { 356 return nil, err 357 } 358 ams, err := getVolumeAccessModes(spec) 359 if err != nil { 360 return nil, err 361 } 362 363 return &rbdMounter{ 364 rbd: newRBD(podUID, spec.Name(), img, pool, ro, plugin, manager), 365 Mon: mon, 366 ID: id, 367 Keyring: keyring, 368 Secret: secret, 369 fsType: fstype, 370 mountOptions: volutil.MountOptionFromSpec(spec), 371 accessModes: ams, 372 }, nil 373 } 374 375 func (plugin *rbdPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) { 376 // Inject real implementations here, test through the internal function. 377 return plugin.newUnmounterInternal(volName, podUID, &rbdUtil{}) 378 } 379 380 func (plugin *rbdPlugin) newUnmounterInternal(volName string, podUID types.UID, manager diskManager) (volume.Unmounter, error) { 381 return &rbdUnmounter{ 382 rbdMounter: &rbdMounter{ 383 rbd: newRBD(podUID, volName, "", "", false, plugin, manager), 384 Mon: make([]string, 0), 385 }, 386 }, nil 387 } 388 389 func (plugin *rbdPlugin) ConstructVolumeSpec(volumeName, mountPath string) (volume.ReconstructedVolume, error) { 390 mounter := plugin.host.GetMounter(plugin.GetPluginName()) 391 kvh, ok := plugin.host.(volume.KubeletVolumeHost) 392 if !ok { 393 return volume.ReconstructedVolume{}, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface") 394 } 395 hu := kvh.GetHostUtil() 396 pluginMntDir := volutil.GetPluginMountDir(plugin.host, plugin.GetPluginName()) 397 sourceName, err := hu.GetDeviceNameFromMount(mounter, mountPath, pluginMntDir) 398 if err != nil { 399 return volume.ReconstructedVolume{}, err 400 } 401 s := dstrings.Split(sourceName, "-image-") 402 if len(s) != 2 { 403 // The mountPath parameter is the volume mount path for a specific pod, its format 404 // is /var/lib/kubelet/pods/{podUID}/volumes/{volumePluginName}/{volumeName}. 405 // mounter.GetDeviceNameFromMount will find the device path(such as /dev/rbd0) by 406 // mountPath first, and then try to find the global device mount path from the mounted 407 // path list of this device. sourceName is extracted from this global device mount path. 408 // mounter.GetDeviceNameFromMount expects the global device mount path conforms to canonical 409 // format: /var/lib/kubelet/plugins/kubernetes.io/rbd/mounts/{pool}-image-{image}. 410 // If this assertion failed, it means that the global device mount path is created by 411 // the deprecated format: /var/lib/kubelet/plugins/kubernetes.io/rbd/rbd/{pool}-image-{image}. 412 // So we will try to check whether this old style global device mount path exist or not. 413 // If existed, extract the sourceName from this old style path, otherwise return an error. 414 klog.V(3).Infof("SourceName %s wrong, fallback to old format", sourceName) 415 sourceName, err = plugin.getDeviceNameFromOldMountPath(mounter, mountPath) 416 if err != nil { 417 return volume.ReconstructedVolume{}, err 418 } 419 s = dstrings.Split(sourceName, "-image-") 420 if len(s) != 2 { 421 return volume.ReconstructedVolume{}, fmt.Errorf("sourceName %s wrong, should be pool+\"-image-\"+imageName", sourceName) 422 } 423 } 424 rbdVolume := &v1.Volume{ 425 Name: volumeName, 426 VolumeSource: v1.VolumeSource{ 427 RBD: &v1.RBDVolumeSource{ 428 RBDPool: s[0], 429 RBDImage: s[1], 430 }, 431 }, 432 } 433 434 var mountContext string 435 if utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) { 436 mountContext, err = hu.GetSELinuxMountContext(mountPath) 437 if err != nil { 438 return volume.ReconstructedVolume{}, err 439 } 440 } 441 442 return volume.ReconstructedVolume{ 443 Spec: volume.NewSpecFromVolume(rbdVolume), 444 SELinuxMountContext: mountContext, 445 }, nil 446 } 447 448 func (plugin *rbdPlugin) ConstructBlockVolumeSpec(podUID types.UID, volumeName, mapPath string) (*volume.Spec, error) { 449 pluginDir := plugin.host.GetVolumeDevicePluginDir(rbdPluginName) 450 blkutil := volumepathhandler.NewBlockVolumePathHandler() 451 452 globalMapPathUUID, err := blkutil.FindGlobalMapPathUUIDFromPod(pluginDir, mapPath, podUID) 453 if err != nil { 454 return nil, err 455 } 456 klog.V(5).Infof("globalMapPathUUID: %v, err: %v", globalMapPathUUID, err) 457 globalMapPath := filepath.Dir(globalMapPathUUID) 458 if len(globalMapPath) == 1 { 459 return nil, fmt.Errorf("failed to retrieve volume plugin information from globalMapPathUUID: %v", globalMapPathUUID) 460 } 461 return getVolumeSpecFromGlobalMapPath(globalMapPath, volumeName) 462 } 463 464 func getVolumeSpecFromGlobalMapPath(globalMapPath, volumeName string) (*volume.Spec, error) { 465 // Retrieve volume spec information from globalMapPath 466 // globalMapPath example: 467 // plugins/kubernetes.io/{PluginName}/{DefaultKubeletVolumeDevicesDirName}/{volumePluginDependentPath} 468 pool, image, err := getPoolAndImageFromMapPath(globalMapPath) 469 if err != nil { 470 return nil, err 471 } 472 block := v1.PersistentVolumeBlock 473 rbdVolume := &v1.PersistentVolume{ 474 ObjectMeta: metav1.ObjectMeta{ 475 Name: volumeName, 476 }, 477 Spec: v1.PersistentVolumeSpec{ 478 PersistentVolumeSource: v1.PersistentVolumeSource{ 479 RBD: &v1.RBDPersistentVolumeSource{ 480 RBDImage: image, 481 RBDPool: pool, 482 }, 483 }, 484 VolumeMode: &block, 485 }, 486 } 487 488 return volume.NewSpecFromPersistentVolume(rbdVolume, true), nil 489 } 490 491 func (plugin *rbdPlugin) NewBlockVolumeMapper(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.BlockVolumeMapper, error) { 492 493 var uid types.UID 494 if pod != nil { 495 uid = pod.UID 496 } 497 secret := "" 498 if pod != nil { 499 secretName, secretNs, err := getSecretNameAndNamespace(spec, pod.Namespace) 500 if err != nil { 501 return nil, err 502 } 503 if len(secretName) > 0 && len(secretNs) > 0 { 504 // if secret is provideded, retrieve it 505 kubeClient := plugin.host.GetKubeClient() 506 if kubeClient == nil { 507 return nil, fmt.Errorf("cannot get kube client") 508 } 509 secrets, err := kubeClient.CoreV1().Secrets(secretNs).Get(context.TODO(), secretName, metav1.GetOptions{}) 510 if err != nil { 511 err = fmt.Errorf("couldn't get secret %v/%v err: %v", secretNs, secretName, err) 512 return nil, err 513 } 514 for _, data := range secrets.Data { 515 secret = string(data) 516 } 517 } 518 } 519 520 return plugin.newBlockVolumeMapperInternal(spec, uid, &rbdUtil{}, secret, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName())) 521 } 522 523 func (plugin *rbdPlugin) newBlockVolumeMapperInternal(spec *volume.Spec, podUID types.UID, manager diskManager, secret string, mounter mount.Interface, exec utilexec.Interface) (volume.BlockVolumeMapper, error) { 524 mon, err := getVolumeSourceMonitors(spec) 525 if err != nil { 526 return nil, err 527 } 528 img, err := getVolumeSourceImage(spec) 529 if err != nil { 530 return nil, err 531 } 532 pool, err := getVolumeSourcePool(spec) 533 if err != nil { 534 return nil, err 535 } 536 id, err := getVolumeSourceUser(spec) 537 if err != nil { 538 return nil, err 539 } 540 keyring, err := getVolumeSourceKeyRing(spec) 541 if err != nil { 542 return nil, err 543 } 544 ro, err := getVolumeSourceReadOnly(spec) 545 if err != nil { 546 return nil, err 547 } 548 549 mapper := &rbdDiskMapper{ 550 rbd: newRBD(podUID, spec.Name(), img, pool, ro, plugin, manager), 551 mon: mon, 552 id: id, 553 keyring: keyring, 554 secret: secret, 555 } 556 557 blockPath, err := mapper.GetGlobalMapPath(spec) 558 if err != nil { 559 return nil, fmt.Errorf("failed to get device path: %v", err) 560 } 561 mapper.MetricsProvider = volume.NewMetricsBlock(filepath.Join(blockPath, string(podUID))) 562 563 return mapper, nil 564 } 565 566 func (plugin *rbdPlugin) NewBlockVolumeUnmapper(volName string, podUID types.UID) (volume.BlockVolumeUnmapper, error) { 567 return plugin.newUnmapperInternal(volName, podUID, &rbdUtil{}) 568 } 569 570 func (plugin *rbdPlugin) newUnmapperInternal(volName string, podUID types.UID, manager diskManager) (volume.BlockVolumeUnmapper, error) { 571 return &rbdDiskUnmapper{ 572 rbdDiskMapper: &rbdDiskMapper{ 573 rbd: newRBD(podUID, volName, "", "", false, plugin, manager), 574 mon: make([]string, 0), 575 }, 576 }, nil 577 } 578 579 func (plugin *rbdPlugin) getDeviceNameFromOldMountPath(mounter mount.Interface, mountPath string) (string, error) { 580 refs, err := mounter.GetMountRefs(mountPath) 581 if err != nil { 582 return "", err 583 } 584 // baseMountPath is the prefix of deprecated device global mounted path, 585 // such as: /var/lib/kubelet/plugins/kubernetes.io/rbd/rbd 586 baseMountPath := filepath.Join(plugin.host.GetPluginDir(rbdPluginName), "rbd") 587 for _, ref := range refs { 588 if dstrings.HasPrefix(ref, baseMountPath) { 589 return filepath.Rel(baseMountPath, ref) 590 } 591 } 592 return "", fmt.Errorf("can't find source name from mounted path: %s", mountPath) 593 } 594 595 func (plugin *rbdPlugin) NewDeleter(logger klog.Logger, spec *volume.Spec) (volume.Deleter, error) { 596 if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.RBD == nil { 597 return nil, fmt.Errorf("spec.PersistentVolume.Spec.RBD is nil") 598 } 599 600 admin, secret, err := plugin.getAdminAndSecret(spec) 601 if err != nil { 602 return nil, err 603 } 604 605 return plugin.newDeleterInternal(spec, admin, secret, &rbdUtil{}) 606 } 607 608 func (plugin *rbdPlugin) newDeleterInternal(spec *volume.Spec, admin, secret string, manager diskManager) (volume.Deleter, error) { 609 return &rbdVolumeDeleter{ 610 rbdMounter: &rbdMounter{ 611 rbd: newRBD("", spec.Name(), spec.PersistentVolume.Spec.RBD.RBDImage, spec.PersistentVolume.Spec.RBD.RBDPool, false, plugin, manager), 612 Mon: spec.PersistentVolume.Spec.RBD.CephMonitors, 613 adminID: admin, 614 adminSecret: secret, 615 }}, nil 616 } 617 618 func (plugin *rbdPlugin) NewProvisioner(logger klog.Logger, options volume.VolumeOptions) (volume.Provisioner, error) { 619 return plugin.newProvisionerInternal(options, &rbdUtil{}) 620 } 621 622 func (plugin *rbdPlugin) newProvisionerInternal(options volume.VolumeOptions, manager diskManager) (volume.Provisioner, error) { 623 return &rbdVolumeProvisioner{ 624 rbdMounter: &rbdMounter{ 625 rbd: newRBD("", "", "", "", false, plugin, manager), 626 }, 627 options: options, 628 }, nil 629 } 630 631 // rbdVolumeProvisioner implements volume.Provisioner interface. 632 type rbdVolumeProvisioner struct { 633 *rbdMounter 634 options volume.VolumeOptions 635 } 636 637 var _ volume.Provisioner = &rbdVolumeProvisioner{} 638 639 func (r *rbdVolumeProvisioner) Provision(selectedNode *v1.Node, allowedTopologies []v1.TopologySelectorTerm) (*v1.PersistentVolume, error) { 640 if !volutil.ContainsAllAccessModes(r.plugin.GetAccessModes(), r.options.PVC.Spec.AccessModes) { 641 return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", r.options.PVC.Spec.AccessModes, r.plugin.GetAccessModes()) 642 } 643 644 if r.options.PVC.Spec.Selector != nil { 645 return nil, fmt.Errorf("claim Selector is not supported") 646 } 647 var err error 648 adminSecretName := "" 649 adminSecretNamespace := rbdDefaultAdminSecretNamespace 650 secret := "" 651 secretName := "" 652 secretNamespace := "" 653 keyring := "" 654 imageFormat := rbdImageFormat2 655 fstype := "" 656 657 for k, v := range r.options.Parameters { 658 switch dstrings.ToLower(k) { 659 case "monitors": 660 arr := dstrings.Split(v, ",") 661 r.Mon = append(r.Mon, arr...) 662 case "adminid": 663 r.adminID = v 664 case "adminsecretname": 665 adminSecretName = v 666 case "adminsecretnamespace": 667 adminSecretNamespace = v 668 case "userid": 669 r.ID = v 670 case "pool": 671 r.Pool = v 672 case "usersecretname": 673 secretName = v 674 case "usersecretnamespace": 675 secretNamespace = v 676 case "keyring": 677 keyring = v 678 case "imageformat": 679 imageFormat = v 680 case "imagefeatures": 681 arr := dstrings.Split(v, ",") 682 for _, f := range arr { 683 if !supportedFeatures.Has(f) { 684 return nil, fmt.Errorf("invalid feature %q for volume plugin %s, supported features are: %v", f, r.plugin.GetPluginName(), supportedFeatures) 685 } 686 r.imageFeatures = append(r.imageFeatures, f) 687 } 688 case volume.VolumeParameterFSType: 689 fstype = v 690 default: 691 return nil, fmt.Errorf("invalid option %q for volume plugin %s", k, r.plugin.GetPluginName()) 692 } 693 } 694 // sanity check 695 if imageFormat != rbdImageFormat1 && imageFormat != rbdImageFormat2 { 696 return nil, fmt.Errorf("invalid ceph imageformat %s, expecting %s or %s", 697 imageFormat, rbdImageFormat1, rbdImageFormat2) 698 } 699 r.imageFormat = imageFormat 700 if adminSecretName == "" { 701 return nil, fmt.Errorf("missing Ceph admin secret name") 702 } 703 if secret, err = parsePVSecret(adminSecretNamespace, adminSecretName, r.plugin.host.GetKubeClient()); err != nil { 704 return nil, fmt.Errorf("failed to get admin secret from [%q/%q]: %v", adminSecretNamespace, adminSecretName, err) 705 } 706 r.adminSecret = secret 707 if len(r.Mon) < 1 { 708 return nil, fmt.Errorf("missing Ceph monitors") 709 } 710 if secretName == "" && keyring == "" { 711 return nil, fmt.Errorf("must specify either keyring or user secret name") 712 } 713 if r.adminID == "" { 714 r.adminID = rbdDefaultAdminID 715 } 716 if r.Pool == "" { 717 r.Pool = rbdDefaultPool 718 } 719 if r.ID == "" { 720 r.ID = r.adminID 721 } 722 723 // create random image name 724 image := fmt.Sprintf("kubernetes-dynamic-pvc-%s", uuid.NewUUID()) 725 r.rbdMounter.Image = image 726 rbd, sizeMB, err := r.manager.CreateImage(r) 727 if err != nil { 728 klog.Errorf("rbd: create volume failed, err: %v", err) 729 return nil, err 730 } 731 klog.Infof("successfully created rbd image %q", image) 732 pv := new(v1.PersistentVolume) 733 metav1.SetMetaDataAnnotation(&pv.ObjectMeta, volutil.VolumeDynamicallyCreatedByKey, "rbd-dynamic-provisioner") 734 735 if secretName != "" { 736 rbd.SecretRef = new(v1.SecretReference) 737 rbd.SecretRef.Name = secretName 738 rbd.SecretRef.Namespace = secretNamespace 739 } else { 740 var filePathRegex = regexp.MustCompile(`^(?:/[^/!;` + "`" + ` ]+)+$`) 741 if keyring != "" && !filePathRegex.MatchString(keyring) { 742 return nil, fmt.Errorf("keyring field must contain a path to a file") 743 } 744 rbd.Keyring = keyring 745 } 746 747 volumeMode := r.options.PVC.Spec.VolumeMode 748 if volumeMode != nil && *volumeMode == v1.PersistentVolumeBlock { 749 // Block volumes should not have any FSType 750 fstype = "" 751 } 752 753 rbd.RadosUser = r.ID 754 rbd.FSType = fstype 755 pv.Spec.PersistentVolumeSource.RBD = rbd 756 pv.Spec.PersistentVolumeReclaimPolicy = r.options.PersistentVolumeReclaimPolicy 757 pv.Spec.AccessModes = r.options.PVC.Spec.AccessModes 758 if len(pv.Spec.AccessModes) == 0 { 759 pv.Spec.AccessModes = r.plugin.GetAccessModes() 760 } 761 pv.Spec.Capacity = v1.ResourceList{ 762 v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dMi", sizeMB)), 763 } 764 pv.Spec.MountOptions = r.options.MountOptions 765 pv.Spec.VolumeMode = volumeMode 766 767 return pv, nil 768 } 769 770 // rbdVolumeDeleter implements volume.Deleter interface. 771 type rbdVolumeDeleter struct { 772 *rbdMounter 773 } 774 775 var _ volume.Deleter = &rbdVolumeDeleter{} 776 777 func (r *rbdVolumeDeleter) GetPath() string { 778 return getPath(r.podUID, r.volName, r.plugin.host) 779 } 780 781 func (r *rbdVolumeDeleter) Delete() error { 782 return r.manager.DeleteImage(r) 783 } 784 785 // rbd implements volume.Volume interface. 786 // It's embedded in Mounter/Unmounter/Deleter. 787 type rbd struct { 788 volName string 789 podUID types.UID 790 Pool string 791 Image string 792 ReadOnly bool 793 plugin *rbdPlugin 794 mounter *mount.SafeFormatAndMount 795 exec utilexec.Interface 796 // Utility interface that provides API calls to the provider to attach/detach disks. 797 manager diskManager 798 volume.MetricsProvider `json:"-"` 799 mountedWithSELinuxContext bool 800 } 801 802 var _ volume.Volume = &rbd{} 803 804 func (rbd *rbd) GetPath() string { 805 // safe to use PodVolumeDir now: volume teardown occurs before pod is cleaned up 806 return getPath(rbd.podUID, rbd.volName, rbd.plugin.host) 807 } 808 809 // newRBD creates a new rbd. 810 func newRBD(podUID types.UID, volName string, image string, pool string, readOnly bool, plugin *rbdPlugin, manager diskManager) *rbd { 811 return &rbd{ 812 podUID: podUID, 813 volName: volName, 814 Image: image, 815 Pool: pool, 816 ReadOnly: readOnly, 817 plugin: plugin, 818 mounter: volutil.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host), 819 exec: plugin.host.GetExec(plugin.GetPluginName()), 820 manager: manager, 821 MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, volName, plugin.host)), 822 } 823 } 824 825 // rbdMounter implements volume.Mounter interface. 826 // It contains information which need to be persisted in whole life cycle of PV 827 // on the node. It is persisted at the very beginning in the pod mount point 828 // directory. 829 // Note: Capitalized field names of this struct determines the information 830 // persisted on the disk, DO NOT change them. (TODO: refactoring to use a dedicated struct?) 831 type rbdMounter struct { 832 *rbd 833 // capitalized so they can be exported in persistRBD() 834 Mon []string 835 ID string 836 Keyring string 837 Secret string `datapolicy:"token"` 838 fsType string 839 adminSecret string `datapolicy:"token"` 840 adminID string 841 mountOptions []string 842 imageFormat string 843 imageFeatures []string 844 accessModes []v1.PersistentVolumeAccessMode 845 } 846 847 var _ volume.Mounter = &rbdMounter{} 848 849 func (rbd *rbd) GetAttributes() volume.Attributes { 850 return volume.Attributes{ 851 ReadOnly: rbd.ReadOnly, 852 Managed: !rbd.ReadOnly, 853 SELinuxRelabel: !rbd.mountedWithSELinuxContext, 854 } 855 } 856 857 func (b *rbdMounter) SetUp(mounterArgs volume.MounterArgs) error { 858 return b.SetUpAt(b.GetPath(), mounterArgs) 859 } 860 861 func (b *rbdMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error { 862 // diskSetUp checks mountpoints and prevent repeated calls 863 klog.V(4).Infof("rbd: attempting to setup at %s", dir) 864 err := diskSetUp(b.manager, *b, dir, b.mounter, mounterArgs.FsGroup, mounterArgs.FSGroupChangePolicy) 865 if err != nil { 866 klog.Errorf("rbd: failed to setup at %s %v", dir, err) 867 return err 868 } 869 if utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) { 870 // The volume must have been mounted in MountDevice with -o context. 871 b.mountedWithSELinuxContext = mounterArgs.SELinuxLabel != "" 872 } 873 874 klog.V(3).Infof("rbd: successfully setup at %s", dir) 875 return err 876 } 877 878 // rbdUnmounter implements volume.Unmounter interface. 879 type rbdUnmounter struct { 880 *rbdMounter 881 } 882 883 var _ volume.Unmounter = &rbdUnmounter{} 884 885 // Unmounts the bind mount, and detaches the disk only if the disk 886 // resource was the last reference to that disk on the kubelet. 887 func (c *rbdUnmounter) TearDown() error { 888 return c.TearDownAt(c.GetPath()) 889 } 890 891 func (c *rbdUnmounter) TearDownAt(dir string) error { 892 klog.V(4).Infof("rbd: attempting to teardown at %s", dir) 893 if pathExists, pathErr := mount.PathExists(dir); pathErr != nil { 894 return fmt.Errorf("error checking if path exists: %v", pathErr) 895 } else if !pathExists { 896 klog.Warningf("Warning: Unmount skipped because path does not exist: %v", dir) 897 return nil 898 } 899 err := diskTearDown(c.manager, *c, dir, c.mounter) 900 if err != nil { 901 return err 902 } 903 klog.V(3).Infof("rbd: successfully teardown at %s", dir) 904 return nil 905 } 906 907 var _ volume.BlockVolumeMapper = &rbdDiskMapper{} 908 909 type rbdDiskMapper struct { 910 *rbd 911 mon []string 912 id string 913 keyring string 914 secret string 915 } 916 917 var _ volume.BlockVolumeUnmapper = &rbdDiskUnmapper{} 918 var _ volume.CustomBlockVolumeUnmapper = &rbdDiskUnmapper{} 919 920 // GetGlobalMapPath returns global map path and error 921 // path: plugins/kubernetes.io/{PluginName}/volumeDevices/{rbd pool}-image-{rbd image-name}/{podUid} 922 func (rbd *rbd) GetGlobalMapPath(spec *volume.Spec) (string, error) { 923 return rbd.rbdGlobalMapPath(spec) 924 } 925 926 // GetPodDeviceMapPath returns pod device map path and volume name 927 // path: pods/{podUid}/volumeDevices/kubernetes.io~rbd 928 // volumeName: pv0001 929 func (rbd *rbd) GetPodDeviceMapPath() (string, string) { 930 return rbd.rbdPodDeviceMapPath() 931 } 932 933 func (rbd *rbd) rbdGlobalMapPath(spec *volume.Spec) (string, error) { 934 mon, err := getVolumeSourceMonitors(spec) 935 if err != nil { 936 return "", err 937 } 938 img, err := getVolumeSourceImage(spec) 939 if err != nil { 940 return "", err 941 } 942 pool, err := getVolumeSourcePool(spec) 943 if err != nil { 944 return "", err 945 } 946 ro, err := getVolumeSourceReadOnly(spec) 947 if err != nil { 948 return "", err 949 } 950 951 mounter := &rbdMounter{ 952 rbd: newRBD("", spec.Name(), img, pool, ro, rbd.plugin, &rbdUtil{}), 953 Mon: mon, 954 } 955 return rbd.manager.MakeGlobalVDPDName(*mounter.rbd), nil 956 } 957 958 func (rbd *rbd) rbdPodDeviceMapPath() (string, string) { 959 name := rbdPluginName 960 return rbd.plugin.host.GetPodVolumeDeviceDir(rbd.podUID, utilstrings.EscapeQualifiedName(name)), rbd.volName 961 } 962 963 // SupportsMetrics returns true for rbdDiskMapper as it initializes the 964 // MetricsProvider. 965 func (rdm *rbdDiskMapper) SupportsMetrics() bool { 966 return true 967 } 968 969 type rbdDiskUnmapper struct { 970 *rbdDiskMapper 971 } 972 973 func getPoolAndImageFromMapPath(mapPath string) (string, string, error) { 974 975 pathParts := dstrings.Split(mapPath, pathSeparator) 976 if len(pathParts) < 2 { 977 return "", "", fmt.Errorf("corrupted mapPath") 978 } 979 rbdParts := dstrings.Split(pathParts[len(pathParts)-1], "-image-") 980 981 if len(rbdParts) < 2 { 982 return "", "", fmt.Errorf("corrupted mapPath") 983 } 984 return string(rbdParts[0]), string(rbdParts[1]), nil 985 } 986 987 func getBlockVolumeDevice(mapPath string) (string, error) { 988 pool, image, err := getPoolAndImageFromMapPath(mapPath) 989 if err != nil { 990 return "", err 991 } 992 // Getting full device path 993 device, found := getDevFromImageAndPool(pool, image) 994 if !found { 995 return "", err 996 } 997 return device, nil 998 } 999 1000 func (rbd *rbdDiskUnmapper) TearDownDevice(mapPath, _ string) error { 1001 1002 device, err := getBlockVolumeDevice(mapPath) 1003 if err != nil { 1004 return fmt.Errorf("rbd: failed to get loopback for device: %v, err: %v", device, err) 1005 } 1006 1007 err = rbd.manager.DetachBlockDisk(*rbd, mapPath) 1008 if err != nil { 1009 return fmt.Errorf("rbd: failed to detach disk: %s\nError: %v", mapPath, err) 1010 } 1011 klog.V(4).Infof("rbd: %q is unmapped, deleting the directory", mapPath) 1012 1013 err = os.RemoveAll(mapPath) 1014 if err != nil { 1015 return fmt.Errorf("rbd: failed to delete the directory: %s\nError: %v", mapPath, err) 1016 } 1017 klog.V(4).Infof("rbd: successfully detached disk: %s", mapPath) 1018 1019 return nil 1020 } 1021 1022 func (rbd *rbdDiskUnmapper) UnmapPodDevice() error { 1023 return nil 1024 } 1025 1026 func getVolumeSourceMonitors(spec *volume.Spec) ([]string, error) { 1027 if spec.Volume != nil && spec.Volume.RBD != nil { 1028 return spec.Volume.RBD.CephMonitors, nil 1029 } else if spec.PersistentVolume != nil && 1030 spec.PersistentVolume.Spec.RBD != nil { 1031 return spec.PersistentVolume.Spec.RBD.CephMonitors, nil 1032 } 1033 1034 return nil, fmt.Errorf("spec does not reference a RBD volume type") 1035 } 1036 1037 func getVolumeSourceImage(spec *volume.Spec) (string, error) { 1038 if spec.Volume != nil && spec.Volume.RBD != nil { 1039 return spec.Volume.RBD.RBDImage, nil 1040 } else if spec.PersistentVolume != nil && 1041 spec.PersistentVolume.Spec.RBD != nil { 1042 return spec.PersistentVolume.Spec.RBD.RBDImage, nil 1043 } 1044 1045 return "", fmt.Errorf("spec does not reference a RBD volume type") 1046 } 1047 1048 func getVolumeSourceFSType(spec *volume.Spec) (string, error) { 1049 if spec.Volume != nil && spec.Volume.RBD != nil { 1050 return spec.Volume.RBD.FSType, nil 1051 } else if spec.PersistentVolume != nil && 1052 spec.PersistentVolume.Spec.RBD != nil { 1053 return spec.PersistentVolume.Spec.RBD.FSType, nil 1054 } 1055 1056 return "", fmt.Errorf("spec does not reference a RBD volume type") 1057 } 1058 1059 func getVolumeSourcePool(spec *volume.Spec) (string, error) { 1060 if spec.Volume != nil && spec.Volume.RBD != nil { 1061 return spec.Volume.RBD.RBDPool, nil 1062 } else if spec.PersistentVolume != nil && 1063 spec.PersistentVolume.Spec.RBD != nil { 1064 return spec.PersistentVolume.Spec.RBD.RBDPool, nil 1065 } 1066 1067 return "", fmt.Errorf("spec does not reference a RBD volume type") 1068 } 1069 1070 func getVolumeSourceUser(spec *volume.Spec) (string, error) { 1071 if spec.Volume != nil && spec.Volume.RBD != nil { 1072 return spec.Volume.RBD.RadosUser, nil 1073 } else if spec.PersistentVolume != nil && 1074 spec.PersistentVolume.Spec.RBD != nil { 1075 return spec.PersistentVolume.Spec.RBD.RadosUser, nil 1076 } 1077 1078 return "", fmt.Errorf("spec does not reference a RBD volume type") 1079 } 1080 1081 func getVolumeSourceKeyRing(spec *volume.Spec) (string, error) { 1082 if spec.Volume != nil && spec.Volume.RBD != nil { 1083 return spec.Volume.RBD.Keyring, nil 1084 } else if spec.PersistentVolume != nil && 1085 spec.PersistentVolume.Spec.RBD != nil { 1086 return spec.PersistentVolume.Spec.RBD.Keyring, nil 1087 } 1088 1089 return "", fmt.Errorf("spec does not reference a RBD volume type") 1090 } 1091 1092 func getVolumeSourceReadOnly(spec *volume.Spec) (bool, error) { 1093 if spec.Volume != nil && spec.Volume.RBD != nil { 1094 return spec.Volume.RBD.ReadOnly, nil 1095 } else if spec.PersistentVolume != nil && 1096 spec.PersistentVolume.Spec.RBD != nil { 1097 // rbd volumes used as a PersistentVolume gets the ReadOnly flag indirectly through 1098 // the persistent-claim volume used to mount the PV 1099 return spec.ReadOnly, nil 1100 } 1101 1102 return false, fmt.Errorf("spec does not reference a RBD volume type") 1103 } 1104 1105 func getVolumeAccessModes(spec *volume.Spec) ([]v1.PersistentVolumeAccessMode, error) { 1106 // Only PersistentVolumeSpec has AccessModes 1107 if spec.PersistentVolume != nil { 1108 if spec.PersistentVolume.Spec.RBD != nil { 1109 return spec.PersistentVolume.Spec.AccessModes, nil 1110 } 1111 return nil, fmt.Errorf("spec does not reference a RBD volume type") 1112 } 1113 1114 return nil, nil 1115 } 1116 1117 func parsePVSecret(namespace, secretName string, kubeClient clientset.Interface) (string, error) { 1118 secret, err := volutil.GetSecretForPV(namespace, secretName, rbdPluginName, kubeClient) 1119 if err != nil { 1120 klog.Errorf("failed to get secret from [%q/%q]: %+v", namespace, secretName, err) 1121 return "", fmt.Errorf("failed to get secret from [%q/%q]: %+v", namespace, secretName, err) 1122 } 1123 return parseSecretMap(secret) 1124 } 1125 1126 // parseSecretMap locates the secret by key name. 1127 func parseSecretMap(secretMap map[string]string) (string, error) { 1128 if len(secretMap) == 0 { 1129 return "", fmt.Errorf("empty secret map") 1130 } 1131 secret := "" 1132 for k, v := range secretMap { 1133 if k == secretKeyName { 1134 return v, nil 1135 } 1136 secret = v 1137 } 1138 // If not found, the last secret in the map wins as done before 1139 return secret, nil 1140 } 1141 1142 func getSecretNameAndNamespace(spec *volume.Spec, defaultNamespace string) (string, string, error) { 1143 if spec.Volume != nil && spec.Volume.RBD != nil { 1144 localSecretRef := spec.Volume.RBD.SecretRef 1145 if localSecretRef != nil { 1146 return localSecretRef.Name, defaultNamespace, nil 1147 } 1148 return "", "", nil 1149 1150 } else if spec.PersistentVolume != nil && 1151 spec.PersistentVolume.Spec.RBD != nil { 1152 secretRef := spec.PersistentVolume.Spec.RBD.SecretRef 1153 secretNs := defaultNamespace 1154 if secretRef != nil { 1155 if len(secretRef.Namespace) != 0 { 1156 secretNs = secretRef.Namespace 1157 } 1158 return secretRef.Name, secretNs, nil 1159 } 1160 return "", "", nil 1161 } 1162 return "", "", fmt.Errorf("spec does not reference an RBD volume type") 1163 }