mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
Merge pull request #36280 from rkouj/better-mount-error
Automatic merge from submit-queue Better messaging for missing volume binaries on host **What this PR does / why we need it**: When mount binaries are not present on a host, the error returned is a generic one. This change is to check the mount binaries before the mount and return a user-friendly error message. This change is specific to GCI and the flag is experimental now. https://github.com/kubernetes/kubernetes/issues/36098 **Release note**: Introduces a flag `check-node-capabilities-before-mount` which if set, enables a check (`CanMount()`) prior to mount operations to verify that the required components (binaries, etc.) to mount the volume are available on the underlying node. If the check is enabled and `CanMount()` returns an error, the mount operation fails. Implements the `CanMount()` check for NFS. Sample output post change : rkouj@rkouj0:~/go/src/k8s.io/kubernetes$ kubectl describe pods Name: sleepyrc-fzhyl Namespace: default Node: e2e-test-rkouj-minion-group-oxxa/10.240.0.3 Start Time: Mon, 07 Nov 2016 21:28:36 -0800 Labels: name=sleepy Status: Pending IP: Controllers: ReplicationController/sleepyrc Containers: sleepycontainer1: Container ID: Image: gcr.io/google_containers/busybox Image ID: Port: Command: sleep 6000 QoS Tier: cpu: Burstable memory: BestEffort Requests: cpu: 100m State: Waiting Reason: ContainerCreating Ready: False Restart Count: 0 Environment Variables: Conditions: Type Status Initialized True Ready False PodScheduled True Volumes: data: Type: NFS (an NFS mount that lasts the lifetime of a pod) Server: 127.0.0.1 Path: /export ReadOnly: false default-token-d13tj: Type: Secret (a volume populated by a Secret) SecretName: default-token-d13tj Events: FirstSeen LastSeen Count From SubobjectPath Type Reason Message --------- -------- ----- ---- ------------- -------- ------ ------- 7s 7s 1 {default-scheduler } Normal Scheduled Successfully assigned sleepyrc-fzhyl to e2e-test-rkouj-minion-group-oxxa 6s 3s 4 {kubelet e2e-test-rkouj-minion-group-oxxa} Warning FailedMount Unable to mount volume kubernetes.io/nfs/32c7ef16-a574-11e6-813d-42010af00002-data (spec.Name: data) on pod sleepyrc-fzhyl (UID: 32c7ef16-a574-11e6-813d-42010af00002). Verify that your node machine has the required components before attempting to mount this volume type. Required binary /sbin/mount.nfs is missing
This commit is contained in:
commit
0f082c6663
@ -474,6 +474,7 @@ function start-kubelet {
|
||||
flags+=" --cluster-domain=${DNS_DOMAIN}"
|
||||
flags+=" --config=/etc/kubernetes/manifests"
|
||||
flags+=" --experimental-mounter-path=${KUBE_HOME}/bin/mounter"
|
||||
flags+=" --experimental-check-node-capabilities-before-mount=true"
|
||||
|
||||
if [[ -n "${KUBELET_PORT:-}" ]]; then
|
||||
flags+=" --port=${KUBELET_PORT}"
|
||||
|
@ -254,4 +254,5 @@ func (s *KubeletServer) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&s.RemoteRuntimeEndpoint, "container-runtime-endpoint", s.RemoteRuntimeEndpoint, "[Experimental] The unix socket endpoint of remote runtime service. The endpoint is used only when CRI integration is enabled (--experimental-cri)")
|
||||
fs.StringVar(&s.RemoteImageEndpoint, "image-service-endpoint", s.RemoteImageEndpoint, "[Experimental] The unix socket endpoint of remote image service. If not specified, it will be the same with container-runtime-endpoint by default. The endpoint is used only when CRI integration is enabled (--experimental-cri)")
|
||||
|
||||
fs.BoolVar(&s.ExperimentalCheckNodeCapabilitiesBeforeMount, "experimental-check-node-capabilities-before-mount", s.ExperimentalCheckNodeCapabilitiesBeforeMount, "[Experimental] if set true, the kubelet will check the underlying node for required componenets (binaries, etc.) before performing the mount")
|
||||
}
|
||||
|
@ -195,6 +195,7 @@ experimental-mounter-path
|
||||
experimental-nvidia-gpus
|
||||
experimental-prefix
|
||||
experimental-cri
|
||||
experimental-check-node-capabilities-before-mount
|
||||
external-etcd-cafile
|
||||
external-etcd-certfile
|
||||
external-etcd-endpoints
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -466,6 +466,10 @@ type KubeletConfiguration struct {
|
||||
// TODO(#34726:1.8.0): Remove the opt-in for failing when swap is enabled.
|
||||
// Tells the Kubelet to fail to start if swap is enabled on the node.
|
||||
ExperimentalFailSwapOn bool `json:"experimentalFailSwapOn,omitempty"`
|
||||
// This flag, if set, enables a check prior to mount operations to verify that the required components
|
||||
// (binaries, etc.) to mount the volume are available on the underlying node. If the check is enabled
|
||||
// and fails the mount operation fails.
|
||||
ExperimentalCheckNodeCapabilitiesBeforeMount bool `json:"ExperimentalCheckNodeCapabilitiesBeforeMount,omitempty"`
|
||||
}
|
||||
|
||||
type KubeletAuthorizationMode string
|
||||
|
@ -505,6 +505,10 @@ type KubeletConfiguration struct {
|
||||
// TODO(#34726:1.8.0): Remove the opt-in for failing when swap is enabled.
|
||||
// Tells the Kubelet to fail to start if swap is enabled on the node.
|
||||
ExperimentalFailSwapOn bool `json:"experimentalFailSwapOn,omitempty"`
|
||||
// This flag, if set, enables a check prior to mount operations to verify that the required components
|
||||
// (binaries, etc.) to mount the volume are available on the underlying node. If the check is enabled
|
||||
// and fails the mount operation fails.
|
||||
ExperimentalCheckNodeCapabilitiesBeforeMount bool `json:"ExperimentalCheckNodeCapabilitiesBeforeMount,omitempty"`
|
||||
}
|
||||
|
||||
type KubeletAuthorizationMode string
|
||||
|
@ -407,6 +407,7 @@ func autoConvert_v1alpha1_KubeletConfiguration_To_componentconfig_KubeletConfigu
|
||||
out.FeatureGates = in.FeatureGates
|
||||
out.EnableCRI = in.EnableCRI
|
||||
out.ExperimentalFailSwapOn = in.ExperimentalFailSwapOn
|
||||
out.ExperimentalCheckNodeCapabilitiesBeforeMount = in.ExperimentalCheckNodeCapabilitiesBeforeMount
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -575,6 +576,7 @@ func autoConvert_componentconfig_KubeletConfiguration_To_v1alpha1_KubeletConfigu
|
||||
out.FeatureGates = in.FeatureGates
|
||||
out.EnableCRI = in.EnableCRI
|
||||
out.ExperimentalFailSwapOn = in.ExperimentalFailSwapOn
|
||||
out.ExperimentalCheckNodeCapabilitiesBeforeMount = in.ExperimentalCheckNodeCapabilitiesBeforeMount
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -461,6 +461,7 @@ func DeepCopy_v1alpha1_KubeletConfiguration(in interface{}, out interface{}, c *
|
||||
out.FeatureGates = in.FeatureGates
|
||||
out.EnableCRI = in.EnableCRI
|
||||
out.ExperimentalFailSwapOn = in.ExperimentalFailSwapOn
|
||||
out.ExperimentalCheckNodeCapabilitiesBeforeMount = in.ExperimentalCheckNodeCapabilitiesBeforeMount
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -392,6 +392,7 @@ func DeepCopy_componentconfig_KubeletConfiguration(in interface{}, out interface
|
||||
out.FeatureGates = in.FeatureGates
|
||||
out.EnableCRI = in.EnableCRI
|
||||
out.ExperimentalFailSwapOn = in.ExperimentalFailSwapOn
|
||||
out.ExperimentalCheckNodeCapabilitiesBeforeMount = in.ExperimentalCheckNodeCapabilitiesBeforeMount
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +120,8 @@ func NewAttachDetachController(
|
||||
operationexecutor.NewOperationExecutor(
|
||||
kubeClient,
|
||||
&adc.volumePluginMgr,
|
||||
recorder)
|
||||
recorder,
|
||||
false) // flag for experimental binary check for volume mount
|
||||
adc.nodeStatusUpdater = statusupdater.NewNodeStatusUpdater(
|
||||
kubeClient, nodeInformer, adc.actualStateOfWorld)
|
||||
adc.reconciler = reconciler.NewReconciler(
|
||||
|
@ -50,7 +50,7 @@ func Test_Run_Positive_DoNothing(t *testing.T) {
|
||||
fakeKubeClient := controllervolumetesting.CreateTestClient()
|
||||
fakeRecorder := &record.FakeRecorder{}
|
||||
ad := operationexecutor.NewOperationExecutor(
|
||||
fakeKubeClient, volumePluginMgr, fakeRecorder)
|
||||
fakeKubeClient, volumePluginMgr, fakeRecorder, false)
|
||||
nodeInformer := informers.NewNodeInformer(
|
||||
fakeKubeClient, resyncPeriod)
|
||||
nsu := statusupdater.NewNodeStatusUpdater(
|
||||
@ -81,7 +81,7 @@ func Test_Run_Positive_OneDesiredVolumeAttach(t *testing.T) {
|
||||
asw := cache.NewActualStateOfWorld(volumePluginMgr)
|
||||
fakeKubeClient := controllervolumetesting.CreateTestClient()
|
||||
fakeRecorder := &record.FakeRecorder{}
|
||||
ad := operationexecutor.NewOperationExecutor(fakeKubeClient, volumePluginMgr, fakeRecorder)
|
||||
ad := operationexecutor.NewOperationExecutor(fakeKubeClient, volumePluginMgr, fakeRecorder, false)
|
||||
nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */)
|
||||
reconciler := NewReconciler(
|
||||
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, dsw, asw, ad, nsu)
|
||||
@ -127,7 +127,7 @@ func Test_Run_Positive_OneDesiredVolumeAttachThenDetachWithUnmountedVolume(t *te
|
||||
asw := cache.NewActualStateOfWorld(volumePluginMgr)
|
||||
fakeKubeClient := controllervolumetesting.CreateTestClient()
|
||||
fakeRecorder := &record.FakeRecorder{}
|
||||
ad := operationexecutor.NewOperationExecutor(fakeKubeClient, volumePluginMgr, fakeRecorder)
|
||||
ad := operationexecutor.NewOperationExecutor(fakeKubeClient, volumePluginMgr, fakeRecorder, false)
|
||||
nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */)
|
||||
reconciler := NewReconciler(
|
||||
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, dsw, asw, ad, nsu)
|
||||
@ -194,7 +194,7 @@ func Test_Run_Positive_OneDesiredVolumeAttachThenDetachWithMountedVolume(t *test
|
||||
asw := cache.NewActualStateOfWorld(volumePluginMgr)
|
||||
fakeKubeClient := controllervolumetesting.CreateTestClient()
|
||||
fakeRecorder := &record.FakeRecorder{}
|
||||
ad := operationexecutor.NewOperationExecutor(fakeKubeClient, volumePluginMgr, fakeRecorder)
|
||||
ad := operationexecutor.NewOperationExecutor(fakeKubeClient, volumePluginMgr, fakeRecorder, false)
|
||||
nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */)
|
||||
reconciler := NewReconciler(
|
||||
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, dsw, asw, ad, nsu)
|
||||
@ -261,7 +261,7 @@ func Test_Run_Negative_OneDesiredVolumeAttachThenDetachWithUnmountedVolumeUpdate
|
||||
asw := cache.NewActualStateOfWorld(volumePluginMgr)
|
||||
fakeKubeClient := controllervolumetesting.CreateTestClient()
|
||||
fakeRecorder := &record.FakeRecorder{}
|
||||
ad := operationexecutor.NewOperationExecutor(fakeKubeClient, volumePluginMgr, fakeRecorder)
|
||||
ad := operationexecutor.NewOperationExecutor(fakeKubeClient, volumePluginMgr, fakeRecorder, false)
|
||||
nsu := statusupdater.NewFakeNodeStatusUpdater(true /* returnError */)
|
||||
reconciler := NewReconciler(
|
||||
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, dsw, asw, ad, nsu)
|
||||
|
@ -3003,8 +3003,15 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"ExperimentalCheckNodeCapabilitiesBeforeMount": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "This flag, if set, enables a check prior to mount operations to verify that the required components (binaries, etc.) to mount the volume are available on the underlying node. If the check is enabled and fails the mount operation fails.",
|
||||
Type: []string{"boolean"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"TypeMeta", "podManifestPath", "syncFrequency", "fileCheckFrequency", "httpCheckFrequency", "manifestURL", "manifestURLHeader", "enableServer", "address", "port", "readOnlyPort", "tlsCertFile", "tlsPrivateKeyFile", "certDirectory", "authentication", "authorization", "hostnameOverride", "podInfraContainerImage", "dockerEndpoint", "rootDirectory", "seccompProfileRoot", "allowPrivileged", "hostNetworkSources", "hostPIDSources", "hostIPCSources", "registryPullQPS", "registryBurst", "eventRecordQPS", "eventBurst", "enableDebuggingHandlers", "minimumGCAge", "maxPerPodContainerCount", "maxContainerCount", "cAdvisorPort", "healthzPort", "healthzBindAddress", "oomScoreAdj", "registerNode", "clusterDomain", "masterServiceNamespace", "clusterDNS", "streamingConnectionIdleTimeout", "nodeStatusUpdateFrequency", "imageMinimumGCAge", "imageGCHighThresholdPercent", "imageGCLowThresholdPercent", "lowDiskSpaceThresholdMB", "volumeStatsAggPeriod", "networkPluginName", "networkPluginMTU", "networkPluginDir", "cniConfDir", "cniBinDir", "volumePluginDir", "containerRuntime", "remoteRuntimeEndpoint", "remoteImageEndpoint", "experimentalMounterPath", "lockFilePath", "exitOnLockContention", "hairpinMode", "babysitDaemons", "maxPods", "nvidiaGPUs", "dockerExecHandlerName", "podCIDR", "resolvConf", "cpuCFSQuota", "containerized", "maxOpenFiles", "reconcileCIDR", "registerSchedulable", "contentType", "kubeAPIQPS", "kubeAPIBurst", "serializeImagePulls", "nodeLabels", "nonMasqueradeCIDR", "enableCustomMetrics", "podsPerCore", "enableControllerAttachDetach", "systemReserved", "kubeReserved", "protectKernelDefaults", "makeIPTablesUtilChains", "iptablesMasqueradeBit", "iptablesDropBit", "featureGates", "experimentalFailSwapOn"},
|
||||
Required: []string{"TypeMeta", "podManifestPath", "syncFrequency", "fileCheckFrequency", "httpCheckFrequency", "manifestURL", "manifestURLHeader", "enableServer", "address", "port", "readOnlyPort", "tlsCertFile", "tlsPrivateKeyFile", "certDirectory", "authentication", "authorization", "hostnameOverride", "podInfraContainerImage", "dockerEndpoint", "rootDirectory", "seccompProfileRoot", "allowPrivileged", "hostNetworkSources", "hostPIDSources", "hostIPCSources", "registryPullQPS", "registryBurst", "eventRecordQPS", "eventBurst", "enableDebuggingHandlers", "minimumGCAge", "maxPerPodContainerCount", "maxContainerCount", "cAdvisorPort", "healthzPort", "healthzBindAddress", "oomScoreAdj", "registerNode", "clusterDomain", "masterServiceNamespace", "clusterDNS", "streamingConnectionIdleTimeout", "nodeStatusUpdateFrequency", "imageMinimumGCAge", "imageGCHighThresholdPercent", "imageGCLowThresholdPercent", "lowDiskSpaceThresholdMB", "volumeStatsAggPeriod", "networkPluginName", "networkPluginMTU", "networkPluginDir", "cniConfDir", "cniBinDir", "volumePluginDir", "containerRuntime", "remoteRuntimeEndpoint", "remoteImageEndpoint", "experimentalMounterPath", "lockFilePath", "exitOnLockContention", "hairpinMode", "babysitDaemons", "maxPods", "nvidiaGPUs", "dockerExecHandlerName", "podCIDR", "resolvConf", "cpuCFSQuota", "containerized", "maxOpenFiles", "reconcileCIDR", "registerSchedulable", "contentType", "kubeAPIQPS", "kubeAPIBurst", "serializeImagePulls", "nodeLabels", "nonMasqueradeCIDR", "enableCustomMetrics", "podsPerCore", "enableControllerAttachDetach", "systemReserved", "kubeReserved", "protectKernelDefaults", "makeIPTablesUtilChains", "iptablesMasqueradeBit", "iptablesDropBit", "featureGates", "experimentalFailSwapOn", "ExperimentalCheckNodeCapabilitiesBeforeMount"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
@ -14803,8 +14810,15 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"ExperimentalCheckNodeCapabilitiesBeforeMount": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "This flag, if set, enables a check prior to mount operations to verify that the required components (binaries, etc.) to mount the volume are available on the underlying node. If the check is enabled and fails the mount operation fails.",
|
||||
Type: []string{"boolean"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"TypeMeta", "podManifestPath", "syncFrequency", "fileCheckFrequency", "httpCheckFrequency", "manifestURL", "manifestURLHeader", "enableServer", "address", "port", "readOnlyPort", "tlsCertFile", "tlsPrivateKeyFile", "certDirectory", "authentication", "authorization", "hostnameOverride", "podInfraContainerImage", "dockerEndpoint", "rootDirectory", "seccompProfileRoot", "allowPrivileged", "hostNetworkSources", "hostPIDSources", "hostIPCSources", "registryPullQPS", "registryBurst", "eventRecordQPS", "eventBurst", "enableDebuggingHandlers", "minimumGCAge", "maxPerPodContainerCount", "maxContainerCount", "cAdvisorPort", "healthzPort", "healthzBindAddress", "oomScoreAdj", "registerNode", "clusterDomain", "masterServiceNamespace", "clusterDNS", "streamingConnectionIdleTimeout", "nodeStatusUpdateFrequency", "imageMinimumGCAge", "imageGCHighThresholdPercent", "imageGCLowThresholdPercent", "lowDiskSpaceThresholdMB", "volumeStatsAggPeriod", "networkPluginName", "networkPluginDir", "cniConfDir", "cniBinDir", "networkPluginMTU", "volumePluginDir", "cloudProvider", "cloudConfigFile", "kubeletCgroups", "runtimeCgroups", "systemCgroups", "cgroupRoot", "containerRuntime", "remoteRuntimeEndpoint", "remoteImageEndpoint", "runtimeRequestTimeout", "rktPath", "experimentalMounterPath", "rktAPIEndpoint", "rktStage1Image", "lockFilePath", "exitOnLockContention", "hairpinMode", "babysitDaemons", "maxPods", "nvidiaGPUs", "dockerExecHandlerName", "podCIDR", "resolvConf", "cpuCFSQuota", "containerized", "maxOpenFiles", "reconcileCIDR", "registerSchedulable", "contentType", "kubeAPIQPS", "kubeAPIBurst", "serializeImagePulls", "outOfDiskTransitionFrequency", "nodeIP", "nodeLabels", "nonMasqueradeCIDR", "enableCustomMetrics", "evictionHard", "evictionSoft", "evictionSoftGracePeriod", "evictionPressureTransitionPeriod", "evictionMaxPodGracePeriod", "evictionMinimumReclaim", "podsPerCore", "enableControllerAttachDetach", "systemReserved", "kubeReserved", "protectKernelDefaults", "makeIPTablesUtilChains", "iptablesMasqueradeBit", "iptablesDropBit", "featureGates", "experimentalFailSwapOn"},
|
||||
Required: []string{"TypeMeta", "podManifestPath", "syncFrequency", "fileCheckFrequency", "httpCheckFrequency", "manifestURL", "manifestURLHeader", "enableServer", "address", "port", "readOnlyPort", "tlsCertFile", "tlsPrivateKeyFile", "certDirectory", "authentication", "authorization", "hostnameOverride", "podInfraContainerImage", "dockerEndpoint", "rootDirectory", "seccompProfileRoot", "allowPrivileged", "hostNetworkSources", "hostPIDSources", "hostIPCSources", "registryPullQPS", "registryBurst", "eventRecordQPS", "eventBurst", "enableDebuggingHandlers", "minimumGCAge", "maxPerPodContainerCount", "maxContainerCount", "cAdvisorPort", "healthzPort", "healthzBindAddress", "oomScoreAdj", "registerNode", "clusterDomain", "masterServiceNamespace", "clusterDNS", "streamingConnectionIdleTimeout", "nodeStatusUpdateFrequency", "imageMinimumGCAge", "imageGCHighThresholdPercent", "imageGCLowThresholdPercent", "lowDiskSpaceThresholdMB", "volumeStatsAggPeriod", "networkPluginName", "networkPluginDir", "cniConfDir", "cniBinDir", "networkPluginMTU", "volumePluginDir", "cloudProvider", "cloudConfigFile", "kubeletCgroups", "runtimeCgroups", "systemCgroups", "cgroupRoot", "containerRuntime", "remoteRuntimeEndpoint", "remoteImageEndpoint", "runtimeRequestTimeout", "rktPath", "experimentalMounterPath", "rktAPIEndpoint", "rktStage1Image", "lockFilePath", "exitOnLockContention", "hairpinMode", "babysitDaemons", "maxPods", "nvidiaGPUs", "dockerExecHandlerName", "podCIDR", "resolvConf", "cpuCFSQuota", "containerized", "maxOpenFiles", "reconcileCIDR", "registerSchedulable", "contentType", "kubeAPIQPS", "kubeAPIBurst", "serializeImagePulls", "outOfDiskTransitionFrequency", "nodeIP", "nodeLabels", "nonMasqueradeCIDR", "enableCustomMetrics", "evictionHard", "evictionSoft", "evictionSoftGracePeriod", "evictionPressureTransitionPeriod", "evictionMaxPodGracePeriod", "evictionMinimumReclaim", "podsPerCore", "enableControllerAttachDetach", "systemReserved", "kubeReserved", "protectKernelDefaults", "makeIPTablesUtilChains", "iptablesMasqueradeBit", "iptablesDropBit", "featureGates", "experimentalFailSwapOn", "ExperimentalCheckNodeCapabilitiesBeforeMount"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
|
@ -716,6 +716,11 @@ func NewMainKubelet(kubeCfg *componentconfig.KubeletConfiguration, kubeDeps *Kub
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the experimentalMounterPathFlag is set, we do not want to
|
||||
// check node capabilities since the mount path is not the default
|
||||
if len(kubeCfg.ExperimentalMounterPath) != 0 {
|
||||
kubeCfg.ExperimentalCheckNodeCapabilitiesBeforeMount = false
|
||||
}
|
||||
// setup volumeManager
|
||||
klet.volumeManager, err = volumemanager.NewVolumeManager(
|
||||
kubeCfg.EnableControllerAttachDetach,
|
||||
@ -726,7 +731,8 @@ func NewMainKubelet(kubeCfg *componentconfig.KubeletConfiguration, kubeDeps *Kub
|
||||
klet.containerRuntime,
|
||||
kubeDeps.Mounter,
|
||||
klet.getPodsDir(),
|
||||
kubeDeps.Recorder)
|
||||
kubeDeps.Recorder,
|
||||
kubeCfg.ExperimentalCheckNodeCapabilitiesBeforeMount)
|
||||
|
||||
runtimeCache, err := kubecontainer.NewRuntimeCache(klet.containerRuntime)
|
||||
if err != nil {
|
||||
|
@ -239,7 +239,8 @@ func newTestKubeletWithImageList(
|
||||
fakeRuntime,
|
||||
kubelet.mounter,
|
||||
kubelet.getPodsDir(),
|
||||
kubelet.recorder)
|
||||
kubelet.recorder,
|
||||
false /* experimentalCheckNodeCapabilitiesBeforeMount*/)
|
||||
require.NoError(t, err, "Failed to initialize volume manager")
|
||||
|
||||
// enable active deadline handler
|
||||
|
@ -400,6 +400,10 @@ func (f *stubVolume) GetAttributes() volume.Attributes {
|
||||
return volume.Attributes{}
|
||||
}
|
||||
|
||||
func (f *stubVolume) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *stubVolume) SetUp(fsGroup *int64) error {
|
||||
return nil
|
||||
}
|
||||
|
@ -102,7 +102,8 @@ func TestRunOnce(t *testing.T) {
|
||||
fakeRuntime,
|
||||
kb.mounter,
|
||||
kb.getPodsDir(),
|
||||
kb.recorder)
|
||||
kb.recorder,
|
||||
false /* experimentalCheckNodeCapabilitiesBeforeMount*/)
|
||||
|
||||
kb.networkPlugin, _ = network.InitNetworkPlugin([]network.NetworkPlugin{}, "", nettest.NewFakeHost(nil), componentconfig.HairpinNone, kb.nonMasqueradeCIDR, network.UseDefaultMTU)
|
||||
// TODO: Factor out "StatsProvider" from Kubelet so we don't have a cyclic dependency
|
||||
|
@ -60,7 +60,7 @@ func Test_Run_Positive_DoNothing(t *testing.T) {
|
||||
asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr)
|
||||
kubeClient := createTestClient()
|
||||
fakeRecorder := &record.FakeRecorder{}
|
||||
oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder)
|
||||
oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder, false /* checkNodeCapabilitiesBeforeMount*/)
|
||||
reconciler := NewReconciler(
|
||||
kubeClient,
|
||||
false, /* controllerAttachDetachEnabled */
|
||||
@ -97,7 +97,7 @@ func Test_Run_Positive_VolumeAttachAndMount(t *testing.T) {
|
||||
asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr)
|
||||
kubeClient := createTestClient()
|
||||
fakeRecorder := &record.FakeRecorder{}
|
||||
oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder)
|
||||
oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder, false)
|
||||
reconciler := NewReconciler(
|
||||
kubeClient,
|
||||
false, /* controllerAttachDetachEnabled */
|
||||
@ -168,7 +168,7 @@ func Test_Run_Positive_VolumeMountControllerAttachEnabled(t *testing.T) {
|
||||
asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr)
|
||||
kubeClient := createTestClient()
|
||||
fakeRecorder := &record.FakeRecorder{}
|
||||
oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder)
|
||||
oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder, false)
|
||||
reconciler := NewReconciler(
|
||||
kubeClient,
|
||||
true, /* controllerAttachDetachEnabled */
|
||||
@ -240,7 +240,7 @@ func Test_Run_Positive_VolumeAttachMountUnmountDetach(t *testing.T) {
|
||||
asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr)
|
||||
kubeClient := createTestClient()
|
||||
fakeRecorder := &record.FakeRecorder{}
|
||||
oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder)
|
||||
oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder, false)
|
||||
reconciler := NewReconciler(
|
||||
kubeClient,
|
||||
false, /* controllerAttachDetachEnabled */
|
||||
@ -323,7 +323,7 @@ func Test_Run_Positive_VolumeUnmountControllerAttachEnabled(t *testing.T) {
|
||||
asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr)
|
||||
kubeClient := createTestClient()
|
||||
fakeRecorder := &record.FakeRecorder{}
|
||||
oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder)
|
||||
oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder, false)
|
||||
reconciler := NewReconciler(
|
||||
kubeClient,
|
||||
true, /* controllerAttachDetachEnabled */
|
||||
|
@ -156,7 +156,8 @@ func NewVolumeManager(
|
||||
kubeContainerRuntime kubecontainer.Runtime,
|
||||
mounter mount.Interface,
|
||||
kubeletPodsDir string,
|
||||
recorder record.EventRecorder) (VolumeManager, error) {
|
||||
recorder record.EventRecorder,
|
||||
checkNodeCapabilitiesBeforeMount bool) (VolumeManager, error) {
|
||||
|
||||
vm := &volumeManager{
|
||||
kubeClient: kubeClient,
|
||||
@ -166,7 +167,8 @@ func NewVolumeManager(
|
||||
operationExecutor: operationexecutor.NewOperationExecutor(
|
||||
kubeClient,
|
||||
volumePluginMgr,
|
||||
recorder),
|
||||
recorder,
|
||||
checkNodeCapabilitiesBeforeMount),
|
||||
}
|
||||
|
||||
vm.reconciler = reconciler.NewReconciler(
|
||||
|
@ -195,7 +195,8 @@ func newTestVolumeManager(
|
||||
&containertest.FakeRuntime{},
|
||||
&mount.FakeMounter{},
|
||||
"",
|
||||
fakeRecorder)
|
||||
fakeRecorder,
|
||||
false /* experimentalCheckNodeCapabilitiesBeforeMount*/)
|
||||
|
||||
return vm, err
|
||||
}
|
||||
|
@ -248,6 +248,13 @@ func (b *awsElasticBlockStoreMounter) GetAttributes() volume.Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (b *awsElasticBlockStoreMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetUp attaches the disk and bind mounts to the volume path.
|
||||
func (b *awsElasticBlockStoreMounter) SetUp(fsGroup *int64) error {
|
||||
return b.SetUpAt(b.GetPath(), fsGroup)
|
||||
|
@ -217,6 +217,13 @@ func (b *azureDiskMounter) GetAttributes() volume.Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (b *azureDiskMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetUp attaches the disk and bind mounts to the volume path.
|
||||
func (b *azureDiskMounter) SetUp(fsGroup *int64) error {
|
||||
return b.SetUpAt(b.GetPath(), fsGroup)
|
||||
|
@ -168,6 +168,13 @@ func (b *azureFileMounter) GetAttributes() volume.Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (b *azureFileMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetUp attaches the disk and bind mounts to the volume path.
|
||||
func (b *azureFileMounter) SetUp(fsGroup *int64) error {
|
||||
return b.SetUpAt(b.GetPath(), fsGroup)
|
||||
|
@ -196,6 +196,13 @@ func (cephfsVolume *cephfsMounter) GetAttributes() volume.Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (caphfsMounter *cephfsMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetUp attaches the disk and bind mounts to the volume path.
|
||||
func (cephfsVolume *cephfsMounter) SetUp(fsGroup *int64) error {
|
||||
return cephfsVolume.SetUpAt(cephfsVolume.GetPath(), fsGroup)
|
||||
|
@ -281,6 +281,13 @@ func (b *cinderVolumeMounter) GetAttributes() volume.Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (b *cinderVolumeMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *cinderVolumeMounter) SetUp(fsGroup *int64) error {
|
||||
return b.SetUpAt(b.GetPath(), fsGroup)
|
||||
}
|
||||
|
@ -141,6 +141,13 @@ func wrappedVolumeSpec() volume.Spec {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (b *configMapVolumeMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *configMapVolumeMounter) SetUp(fsGroup *int64) error {
|
||||
return b.SetUpAt(b.GetPath(), fsGroup)
|
||||
}
|
||||
|
@ -147,6 +147,13 @@ func (d *downwardAPIVolume) GetAttributes() volume.Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (b *downwardAPIVolumeMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetUp puts in place the volume plugin.
|
||||
// This function is not idempotent by design. We want the data to be refreshed periodically.
|
||||
// The internal sync interval of kubelet will drive the refresh of data.
|
||||
|
@ -174,6 +174,13 @@ func (ed *emptyDir) GetAttributes() volume.Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (b *emptyDir) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetUp creates new directory.
|
||||
func (ed *emptyDir) SetUp(fsGroup *int64) error {
|
||||
return ed.SetUpAt(ed.GetPath(), fsGroup)
|
||||
|
@ -187,6 +187,14 @@ func (b *fcDiskMounter) GetAttributes() volume.Attributes {
|
||||
SupportsSELinux: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (b *fcDiskMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *fcDiskMounter) SetUp(fsGroup *int64) error {
|
||||
return b.SetUpAt(b.GetPath(), fsGroup)
|
||||
}
|
||||
|
@ -260,6 +260,13 @@ func (f flexVolumeMounter) GetAttributes() volume.Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (f *flexVolumeMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// flexVolumeManager is the abstract interface to flex volume ops.
|
||||
type flexVolumeManager interface {
|
||||
// Attaches the disk to the kubelet's host machine.
|
||||
|
@ -210,6 +210,14 @@ func (b *flockerVolumeMounter) GetAttributes() volume.Attributes {
|
||||
SupportsSELinux: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (b *flockerVolumeMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *flockerVolumeMounter) GetPath() string {
|
||||
return getPath(b.podUID, b.volName, b.plugin.host)
|
||||
}
|
||||
|
@ -238,6 +238,13 @@ func (b *gcePersistentDiskMounter) GetAttributes() volume.Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (b *gcePersistentDiskMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetUp bind mounts the disk global mount to the volume path.
|
||||
func (b *gcePersistentDiskMounter) SetUp(fsGroup *int64) error {
|
||||
return b.SetUpAt(b.GetPath(), fsGroup)
|
||||
|
@ -155,6 +155,13 @@ func (b *gitRepoVolumeMounter) GetAttributes() volume.Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (b *gitRepoVolumeMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetUp creates new directory and clones a git repo.
|
||||
func (b *gitRepoVolumeMounter) SetUp(fsGroup *int64) error {
|
||||
return b.SetUpAt(b.GetPath(), fsGroup)
|
||||
|
@ -207,6 +207,13 @@ func (b *glusterfsMounter) GetAttributes() volume.Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (b *glusterfsMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetUp attaches the disk and bind mounts to the volume path.
|
||||
func (b *glusterfsMounter) SetUp(fsGroup *int64) error {
|
||||
return b.SetUpAt(b.GetPath(), fsGroup)
|
||||
|
@ -186,6 +186,13 @@ func (b *hostPathMounter) GetAttributes() volume.Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (b *hostPathMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetUp does nothing.
|
||||
func (b *hostPathMounter) SetUp(fsGroup *int64) error {
|
||||
return nil
|
||||
|
@ -196,6 +196,13 @@ func (b *iscsiDiskMounter) GetAttributes() volume.Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (b *iscsiDiskMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *iscsiDiskMounter) SetUp(fsGroup *int64) error {
|
||||
return b.SetUpAt(b.GetPath(), fsGroup)
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/types:go_default_library",
|
||||
"//pkg/util/exec:go_default_library",
|
||||
"//pkg/util/mount:go_default_library",
|
||||
"//pkg/util/strings:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
|
@ -19,14 +19,15 @@ package nfs
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/util/exec"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
"k8s.io/kubernetes/pkg/util/strings"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// This is the primary entrypoint for volume plugins.
|
||||
@ -159,6 +160,32 @@ func (nfsVolume *nfs) GetPath() string {
|
||||
return nfsVolume.plugin.host.GetPodVolumeDir(nfsVolume.pod.UID, strings.EscapeQualifiedNameForDisk(name), nfsVolume.volName)
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (nfsMounter *nfsMounter) CanMount() error {
|
||||
exe := exec.New()
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
_, err1 := exe.Command("/bin/ls", "/sbin/mount.nfs").CombinedOutput()
|
||||
_, err2 := exe.Command("/bin/ls", "/sbin/mount.nfs4").CombinedOutput()
|
||||
|
||||
if err1 != nil {
|
||||
return fmt.Errorf("Required binary /sbin/mount.nfs is missing")
|
||||
}
|
||||
if err2 != nil {
|
||||
return fmt.Errorf("Required binary /sbin/mount.nfs4 is missing")
|
||||
}
|
||||
return nil
|
||||
case "darwin":
|
||||
_, err := exe.Command("/bin/ls", "/sbin/mount_nfs").CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Required binary /sbin/mount_nfs is missing")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type nfsMounter struct {
|
||||
*nfs
|
||||
server string
|
||||
|
@ -46,6 +46,7 @@ func TestCanSupport(t *testing.T) {
|
||||
if plug.GetPluginName() != "kubernetes.io/nfs" {
|
||||
t.Errorf("Wrong name: %s", plug.GetPluginName())
|
||||
}
|
||||
|
||||
if !plug.CanSupport(&volume.Spec{Volume: &api.Volume{VolumeSource: api.VolumeSource{NFS: &api.NFSVolumeSource{}}}}) {
|
||||
t.Errorf("Expected true")
|
||||
}
|
||||
|
@ -169,6 +169,13 @@ func (b *photonPersistentDiskMounter) GetAttributes() volume.Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (b *photonPersistentDiskMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetUp attaches the disk and bind mounts to the volume path.
|
||||
func (b *photonPersistentDiskMounter) SetUp(fsGroup *int64) error {
|
||||
return b.SetUpAt(b.GetPath(), fsGroup)
|
||||
|
@ -218,6 +218,13 @@ func (mounter *quobyteMounter) GetAttributes() volume.Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (mounter *quobyteMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetUp attaches the disk and bind mounts to the volume path.
|
||||
func (mounter *quobyteMounter) SetUp(fsGroup *int64) error {
|
||||
pluginDir := mounter.plugin.host.GetPluginDir(strings.EscapeQualifiedNameForDisk(quobytePluginName))
|
||||
|
@ -381,6 +381,13 @@ func (b *rbd) GetAttributes() volume.Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (b *rbdMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *rbdMounter) SetUp(fsGroup *int64) error {
|
||||
return b.SetUpAt(b.GetPath(), fsGroup)
|
||||
}
|
||||
|
@ -158,6 +158,14 @@ func (sv *secretVolume) GetAttributes() volume.Attributes {
|
||||
SupportsSELinux: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (b *secretVolumeMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *secretVolumeMounter) SetUp(fsGroup *int64) error {
|
||||
// Update each Slash "/" character for Windows with seperator character
|
||||
dir := b.GetPath()
|
||||
|
@ -320,6 +320,10 @@ func (_ *FakeVolume) GetAttributes() Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
func (fv *FakeVolume) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fv *FakeVolume) SetUp(fsGroup *int64) error {
|
||||
fv.Lock()
|
||||
defer fv.Unlock()
|
||||
|
@ -119,7 +119,8 @@ type OperationExecutor interface {
|
||||
func NewOperationExecutor(
|
||||
kubeClient internalclientset.Interface,
|
||||
volumePluginMgr *volume.VolumePluginMgr,
|
||||
recorder record.EventRecorder) OperationExecutor {
|
||||
recorder record.EventRecorder,
|
||||
checkNodeCapabilitiesBeforeMount bool) OperationExecutor {
|
||||
|
||||
return &operationExecutor{
|
||||
kubeClient: kubeClient,
|
||||
@ -127,6 +128,7 @@ func NewOperationExecutor(
|
||||
pendingOperations: nestedpendingoperations.NewNestedPendingOperations(
|
||||
true /* exponentialBackOffOnError */),
|
||||
recorder: recorder,
|
||||
checkNodeCapabilitiesBeforeMount: checkNodeCapabilitiesBeforeMount,
|
||||
}
|
||||
}
|
||||
|
||||
@ -371,6 +373,11 @@ type operationExecutor struct {
|
||||
|
||||
// recorder is used to record events in the API server
|
||||
recorder record.EventRecorder
|
||||
|
||||
// checkNodeCapabilitiesBeforeMount, if set, enables the CanMount check,
|
||||
// which verifies that the components (binaries, etc.) required to mount
|
||||
// the volume are available on the underlying node before attempting mount.
|
||||
checkNodeCapabilitiesBeforeMount bool
|
||||
}
|
||||
|
||||
func (oe *operationExecutor) IsOperationPending(volumeName api.UniqueVolumeName, podName volumetypes.UniquePodName) bool {
|
||||
@ -877,6 +884,15 @@ func (oe *operationExecutor) generateMountVolumeFunc(
|
||||
}
|
||||
}
|
||||
|
||||
if oe.checkNodeCapabilitiesBeforeMount {
|
||||
if canMountErr := volumeMounter.CanMount(); canMountErr != nil {
|
||||
errMsg := fmt.Sprintf("Unable to mount volume %v (spec.Name: %v) on pod %v (UID: %v). Verify that your node machine has the required components before attempting to mount this volume type. %s", volumeToMount.VolumeName, volumeToMount.VolumeSpec.Name(), volumeToMount.Pod.Name, volumeToMount.Pod.UID, canMountErr.Error())
|
||||
oe.recorder.Eventf(volumeToMount.Pod, api.EventTypeWarning, kevents.FailedMountVolume, errMsg)
|
||||
glog.Errorf(errMsg)
|
||||
return fmt.Errorf(errMsg)
|
||||
}
|
||||
}
|
||||
|
||||
// Execute mount
|
||||
mountErr := volumeMounter.SetUp(fsGroup)
|
||||
if mountErr != nil {
|
||||
|
@ -94,6 +94,18 @@ type Attributes struct {
|
||||
type Mounter interface {
|
||||
// Uses Interface to provide the path for Docker binds.
|
||||
Volume
|
||||
|
||||
// CanMount is called immediately prior to Setup to check if
|
||||
// the required components (binaries, etc.) are available on
|
||||
// the underlying node to complete the subsequent SetUp (mount)
|
||||
// operation. If CanMount returns error, the mount operation is
|
||||
// aborted and an event is generated indicating that the node
|
||||
// does not have the required binaries to complete the mount.
|
||||
// If CanMount succeeds, the mount operation continues
|
||||
// normally. The CanMount check can be enabled or disabled
|
||||
// using the experimental-check-mount-binaries binary flag
|
||||
CanMount() error
|
||||
|
||||
// SetUp prepares and mounts/unpacks the volume to a
|
||||
// self-determined directory path. The mount point and its
|
||||
// content should be owned by 'fsGroup' so that it can be
|
||||
|
@ -177,6 +177,13 @@ func (b *vsphereVolumeMounter) SetUp(fsGroup *int64) error {
|
||||
return b.SetUpAt(b.GetPath(), fsGroup)
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (b *vsphereVolumeMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetUp attaches the disk and bind mounts to the volume path.
|
||||
func (b *vsphereVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
||||
glog.V(5).Infof("vSphere volume setup %s to %s", b.volPath, dir)
|
||||
|
Loading…
Reference in New Issue
Block a user