Commit d81e216f authored by Rajat Ramesh Koujalagi's avatar Rajat Ramesh Koujalagi Committed by rkouj

Better messaging for missing volume components on host to perform mount

parent 3bd87044
...@@ -474,6 +474,7 @@ function start-kubelet { ...@@ -474,6 +474,7 @@ function start-kubelet {
flags+=" --cluster-domain=${DNS_DOMAIN}" flags+=" --cluster-domain=${DNS_DOMAIN}"
flags+=" --config=/etc/kubernetes/manifests" flags+=" --config=/etc/kubernetes/manifests"
flags+=" --experimental-mounter-path=${KUBE_HOME}/bin/mounter" flags+=" --experimental-mounter-path=${KUBE_HOME}/bin/mounter"
flags+=" --experimental-check-node-capabilities-before-mount=true"
if [[ -n "${KUBELET_PORT:-}" ]]; then if [[ -n "${KUBELET_PORT:-}" ]]; then
flags+=" --port=${KUBELET_PORT}" flags+=" --port=${KUBELET_PORT}"
......
...@@ -254,4 +254,5 @@ func (s *KubeletServer) AddFlags(fs *pflag.FlagSet) { ...@@ -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.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.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 ...@@ -195,6 +195,7 @@ experimental-mounter-path
experimental-nvidia-gpus experimental-nvidia-gpus
experimental-prefix experimental-prefix
experimental-cri experimental-cri
experimental-check-node-capabilities-before-mount
external-etcd-cafile external-etcd-cafile
external-etcd-certfile external-etcd-certfile
external-etcd-endpoints external-etcd-endpoints
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -466,6 +466,10 @@ type KubeletConfiguration struct { ...@@ -466,6 +466,10 @@ type KubeletConfiguration struct {
// TODO(#34726:1.8.0): Remove the opt-in for failing when swap is enabled. // 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. // Tells the Kubelet to fail to start if swap is enabled on the node.
ExperimentalFailSwapOn bool `json:"experimentalFailSwapOn,omitempty"` 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 type KubeletAuthorizationMode string
......
...@@ -505,6 +505,10 @@ type KubeletConfiguration struct { ...@@ -505,6 +505,10 @@ type KubeletConfiguration struct {
// TODO(#34726:1.8.0): Remove the opt-in for failing when swap is enabled. // 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. // Tells the Kubelet to fail to start if swap is enabled on the node.
ExperimentalFailSwapOn bool `json:"experimentalFailSwapOn,omitempty"` 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 type KubeletAuthorizationMode string
......
...@@ -407,6 +407,7 @@ func autoConvert_v1alpha1_KubeletConfiguration_To_componentconfig_KubeletConfigu ...@@ -407,6 +407,7 @@ func autoConvert_v1alpha1_KubeletConfiguration_To_componentconfig_KubeletConfigu
out.FeatureGates = in.FeatureGates out.FeatureGates = in.FeatureGates
out.EnableCRI = in.EnableCRI out.EnableCRI = in.EnableCRI
out.ExperimentalFailSwapOn = in.ExperimentalFailSwapOn out.ExperimentalFailSwapOn = in.ExperimentalFailSwapOn
out.ExperimentalCheckNodeCapabilitiesBeforeMount = in.ExperimentalCheckNodeCapabilitiesBeforeMount
return nil return nil
} }
...@@ -575,6 +576,7 @@ func autoConvert_componentconfig_KubeletConfiguration_To_v1alpha1_KubeletConfigu ...@@ -575,6 +576,7 @@ func autoConvert_componentconfig_KubeletConfiguration_To_v1alpha1_KubeletConfigu
out.FeatureGates = in.FeatureGates out.FeatureGates = in.FeatureGates
out.EnableCRI = in.EnableCRI out.EnableCRI = in.EnableCRI
out.ExperimentalFailSwapOn = in.ExperimentalFailSwapOn out.ExperimentalFailSwapOn = in.ExperimentalFailSwapOn
out.ExperimentalCheckNodeCapabilitiesBeforeMount = in.ExperimentalCheckNodeCapabilitiesBeforeMount
return nil return nil
} }
......
...@@ -461,6 +461,7 @@ func DeepCopy_v1alpha1_KubeletConfiguration(in interface{}, out interface{}, c * ...@@ -461,6 +461,7 @@ func DeepCopy_v1alpha1_KubeletConfiguration(in interface{}, out interface{}, c *
out.FeatureGates = in.FeatureGates out.FeatureGates = in.FeatureGates
out.EnableCRI = in.EnableCRI out.EnableCRI = in.EnableCRI
out.ExperimentalFailSwapOn = in.ExperimentalFailSwapOn out.ExperimentalFailSwapOn = in.ExperimentalFailSwapOn
out.ExperimentalCheckNodeCapabilitiesBeforeMount = in.ExperimentalCheckNodeCapabilitiesBeforeMount
return nil return nil
} }
} }
......
...@@ -392,6 +392,7 @@ func DeepCopy_componentconfig_KubeletConfiguration(in interface{}, out interface ...@@ -392,6 +392,7 @@ func DeepCopy_componentconfig_KubeletConfiguration(in interface{}, out interface
out.FeatureGates = in.FeatureGates out.FeatureGates = in.FeatureGates
out.EnableCRI = in.EnableCRI out.EnableCRI = in.EnableCRI
out.ExperimentalFailSwapOn = in.ExperimentalFailSwapOn out.ExperimentalFailSwapOn = in.ExperimentalFailSwapOn
out.ExperimentalCheckNodeCapabilitiesBeforeMount = in.ExperimentalCheckNodeCapabilitiesBeforeMount
return nil return nil
} }
} }
......
...@@ -120,7 +120,8 @@ func NewAttachDetachController( ...@@ -120,7 +120,8 @@ func NewAttachDetachController(
operationexecutor.NewOperationExecutor( operationexecutor.NewOperationExecutor(
kubeClient, kubeClient,
&adc.volumePluginMgr, &adc.volumePluginMgr,
recorder) recorder,
false) // flag for experimental binary check for volume mount
adc.nodeStatusUpdater = statusupdater.NewNodeStatusUpdater( adc.nodeStatusUpdater = statusupdater.NewNodeStatusUpdater(
kubeClient, nodeInformer, adc.actualStateOfWorld) kubeClient, nodeInformer, adc.actualStateOfWorld)
adc.reconciler = reconciler.NewReconciler( adc.reconciler = reconciler.NewReconciler(
......
...@@ -50,7 +50,7 @@ func Test_Run_Positive_DoNothing(t *testing.T) { ...@@ -50,7 +50,7 @@ func Test_Run_Positive_DoNothing(t *testing.T) {
fakeKubeClient := controllervolumetesting.CreateTestClient() fakeKubeClient := controllervolumetesting.CreateTestClient()
fakeRecorder := &record.FakeRecorder{} fakeRecorder := &record.FakeRecorder{}
ad := operationexecutor.NewOperationExecutor( ad := operationexecutor.NewOperationExecutor(
fakeKubeClient, volumePluginMgr, fakeRecorder) fakeKubeClient, volumePluginMgr, fakeRecorder, false)
nodeInformer := informers.NewNodeInformer( nodeInformer := informers.NewNodeInformer(
fakeKubeClient, resyncPeriod) fakeKubeClient, resyncPeriod)
nsu := statusupdater.NewNodeStatusUpdater( nsu := statusupdater.NewNodeStatusUpdater(
...@@ -81,7 +81,7 @@ func Test_Run_Positive_OneDesiredVolumeAttach(t *testing.T) { ...@@ -81,7 +81,7 @@ func Test_Run_Positive_OneDesiredVolumeAttach(t *testing.T) {
asw := cache.NewActualStateOfWorld(volumePluginMgr) asw := cache.NewActualStateOfWorld(volumePluginMgr)
fakeKubeClient := controllervolumetesting.CreateTestClient() fakeKubeClient := controllervolumetesting.CreateTestClient()
fakeRecorder := &record.FakeRecorder{} fakeRecorder := &record.FakeRecorder{}
ad := operationexecutor.NewOperationExecutor(fakeKubeClient, volumePluginMgr, fakeRecorder) ad := operationexecutor.NewOperationExecutor(fakeKubeClient, volumePluginMgr, fakeRecorder, false)
nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */) nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */)
reconciler := NewReconciler( reconciler := NewReconciler(
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, dsw, asw, ad, nsu) reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, dsw, asw, ad, nsu)
...@@ -127,7 +127,7 @@ func Test_Run_Positive_OneDesiredVolumeAttachThenDetachWithUnmountedVolume(t *te ...@@ -127,7 +127,7 @@ func Test_Run_Positive_OneDesiredVolumeAttachThenDetachWithUnmountedVolume(t *te
asw := cache.NewActualStateOfWorld(volumePluginMgr) asw := cache.NewActualStateOfWorld(volumePluginMgr)
fakeKubeClient := controllervolumetesting.CreateTestClient() fakeKubeClient := controllervolumetesting.CreateTestClient()
fakeRecorder := &record.FakeRecorder{} fakeRecorder := &record.FakeRecorder{}
ad := operationexecutor.NewOperationExecutor(fakeKubeClient, volumePluginMgr, fakeRecorder) ad := operationexecutor.NewOperationExecutor(fakeKubeClient, volumePluginMgr, fakeRecorder, false)
nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */) nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */)
reconciler := NewReconciler( reconciler := NewReconciler(
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, dsw, asw, ad, nsu) reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, dsw, asw, ad, nsu)
...@@ -194,7 +194,7 @@ func Test_Run_Positive_OneDesiredVolumeAttachThenDetachWithMountedVolume(t *test ...@@ -194,7 +194,7 @@ func Test_Run_Positive_OneDesiredVolumeAttachThenDetachWithMountedVolume(t *test
asw := cache.NewActualStateOfWorld(volumePluginMgr) asw := cache.NewActualStateOfWorld(volumePluginMgr)
fakeKubeClient := controllervolumetesting.CreateTestClient() fakeKubeClient := controllervolumetesting.CreateTestClient()
fakeRecorder := &record.FakeRecorder{} fakeRecorder := &record.FakeRecorder{}
ad := operationexecutor.NewOperationExecutor(fakeKubeClient, volumePluginMgr, fakeRecorder) ad := operationexecutor.NewOperationExecutor(fakeKubeClient, volumePluginMgr, fakeRecorder, false)
nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */) nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */)
reconciler := NewReconciler( reconciler := NewReconciler(
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, dsw, asw, ad, nsu) reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, dsw, asw, ad, nsu)
...@@ -261,7 +261,7 @@ func Test_Run_Negative_OneDesiredVolumeAttachThenDetachWithUnmountedVolumeUpdate ...@@ -261,7 +261,7 @@ func Test_Run_Negative_OneDesiredVolumeAttachThenDetachWithUnmountedVolumeUpdate
asw := cache.NewActualStateOfWorld(volumePluginMgr) asw := cache.NewActualStateOfWorld(volumePluginMgr)
fakeKubeClient := controllervolumetesting.CreateTestClient() fakeKubeClient := controllervolumetesting.CreateTestClient()
fakeRecorder := &record.FakeRecorder{} fakeRecorder := &record.FakeRecorder{}
ad := operationexecutor.NewOperationExecutor(fakeKubeClient, volumePluginMgr, fakeRecorder) ad := operationexecutor.NewOperationExecutor(fakeKubeClient, volumePluginMgr, fakeRecorder, false)
nsu := statusupdater.NewFakeNodeStatusUpdater(true /* returnError */) nsu := statusupdater.NewFakeNodeStatusUpdater(true /* returnError */)
reconciler := NewReconciler( reconciler := NewReconciler(
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, dsw, asw, ad, nsu) reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, dsw, asw, ad, nsu)
......
...@@ -3003,8 +3003,15 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{ ...@@ -3003,8 +3003,15 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
Format: "", 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{ Dependencies: []string{
...@@ -14803,8 +14810,15 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{ ...@@ -14803,8 +14810,15 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
Format: "", 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{ Dependencies: []string{
......
...@@ -716,6 +716,11 @@ func NewMainKubelet(kubeCfg *componentconfig.KubeletConfiguration, kubeDeps *Kub ...@@ -716,6 +716,11 @@ func NewMainKubelet(kubeCfg *componentconfig.KubeletConfiguration, kubeDeps *Kub
return nil, err 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 // setup volumeManager
klet.volumeManager, err = volumemanager.NewVolumeManager( klet.volumeManager, err = volumemanager.NewVolumeManager(
kubeCfg.EnableControllerAttachDetach, kubeCfg.EnableControllerAttachDetach,
...@@ -726,7 +731,8 @@ func NewMainKubelet(kubeCfg *componentconfig.KubeletConfiguration, kubeDeps *Kub ...@@ -726,7 +731,8 @@ func NewMainKubelet(kubeCfg *componentconfig.KubeletConfiguration, kubeDeps *Kub
klet.containerRuntime, klet.containerRuntime,
kubeDeps.Mounter, kubeDeps.Mounter,
klet.getPodsDir(), klet.getPodsDir(),
kubeDeps.Recorder) kubeDeps.Recorder,
kubeCfg.ExperimentalCheckNodeCapabilitiesBeforeMount)
runtimeCache, err := kubecontainer.NewRuntimeCache(klet.containerRuntime) runtimeCache, err := kubecontainer.NewRuntimeCache(klet.containerRuntime)
if err != nil { if err != nil {
......
...@@ -239,7 +239,8 @@ func newTestKubeletWithImageList( ...@@ -239,7 +239,8 @@ func newTestKubeletWithImageList(
fakeRuntime, fakeRuntime,
kubelet.mounter, kubelet.mounter,
kubelet.getPodsDir(), kubelet.getPodsDir(),
kubelet.recorder) kubelet.recorder,
false /* experimentalCheckNodeCapabilitiesBeforeMount*/)
require.NoError(t, err, "Failed to initialize volume manager") require.NoError(t, err, "Failed to initialize volume manager")
// enable active deadline handler // enable active deadline handler
......
...@@ -400,6 +400,10 @@ func (f *stubVolume) GetAttributes() volume.Attributes { ...@@ -400,6 +400,10 @@ func (f *stubVolume) GetAttributes() volume.Attributes {
return volume.Attributes{} return volume.Attributes{}
} }
func (f *stubVolume) CanMount() error {
return nil
}
func (f *stubVolume) SetUp(fsGroup *int64) error { func (f *stubVolume) SetUp(fsGroup *int64) error {
return nil return nil
} }
......
...@@ -102,7 +102,8 @@ func TestRunOnce(t *testing.T) { ...@@ -102,7 +102,8 @@ func TestRunOnce(t *testing.T) {
fakeRuntime, fakeRuntime,
kb.mounter, kb.mounter,
kb.getPodsDir(), kb.getPodsDir(),
kb.recorder) kb.recorder,
false /* experimentalCheckNodeCapabilitiesBeforeMount*/)
kb.networkPlugin, _ = network.InitNetworkPlugin([]network.NetworkPlugin{}, "", nettest.NewFakeHost(nil), componentconfig.HairpinNone, kb.nonMasqueradeCIDR, network.UseDefaultMTU) 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 // 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) { ...@@ -60,7 +60,7 @@ func Test_Run_Positive_DoNothing(t *testing.T) {
asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr)
kubeClient := createTestClient() kubeClient := createTestClient()
fakeRecorder := &record.FakeRecorder{} fakeRecorder := &record.FakeRecorder{}
oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder) oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder, false /* checkNodeCapabilitiesBeforeMount*/)
reconciler := NewReconciler( reconciler := NewReconciler(
kubeClient, kubeClient,
false, /* controllerAttachDetachEnabled */ false, /* controllerAttachDetachEnabled */
...@@ -97,7 +97,7 @@ func Test_Run_Positive_VolumeAttachAndMount(t *testing.T) { ...@@ -97,7 +97,7 @@ func Test_Run_Positive_VolumeAttachAndMount(t *testing.T) {
asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr)
kubeClient := createTestClient() kubeClient := createTestClient()
fakeRecorder := &record.FakeRecorder{} fakeRecorder := &record.FakeRecorder{}
oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder) oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder, false)
reconciler := NewReconciler( reconciler := NewReconciler(
kubeClient, kubeClient,
false, /* controllerAttachDetachEnabled */ false, /* controllerAttachDetachEnabled */
...@@ -168,7 +168,7 @@ func Test_Run_Positive_VolumeMountControllerAttachEnabled(t *testing.T) { ...@@ -168,7 +168,7 @@ func Test_Run_Positive_VolumeMountControllerAttachEnabled(t *testing.T) {
asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr)
kubeClient := createTestClient() kubeClient := createTestClient()
fakeRecorder := &record.FakeRecorder{} fakeRecorder := &record.FakeRecorder{}
oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder) oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder, false)
reconciler := NewReconciler( reconciler := NewReconciler(
kubeClient, kubeClient,
true, /* controllerAttachDetachEnabled */ true, /* controllerAttachDetachEnabled */
...@@ -240,7 +240,7 @@ func Test_Run_Positive_VolumeAttachMountUnmountDetach(t *testing.T) { ...@@ -240,7 +240,7 @@ func Test_Run_Positive_VolumeAttachMountUnmountDetach(t *testing.T) {
asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr)
kubeClient := createTestClient() kubeClient := createTestClient()
fakeRecorder := &record.FakeRecorder{} fakeRecorder := &record.FakeRecorder{}
oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder) oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder, false)
reconciler := NewReconciler( reconciler := NewReconciler(
kubeClient, kubeClient,
false, /* controllerAttachDetachEnabled */ false, /* controllerAttachDetachEnabled */
...@@ -323,7 +323,7 @@ func Test_Run_Positive_VolumeUnmountControllerAttachEnabled(t *testing.T) { ...@@ -323,7 +323,7 @@ func Test_Run_Positive_VolumeUnmountControllerAttachEnabled(t *testing.T) {
asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr) asw := cache.NewActualStateOfWorld(nodeName, volumePluginMgr)
kubeClient := createTestClient() kubeClient := createTestClient()
fakeRecorder := &record.FakeRecorder{} fakeRecorder := &record.FakeRecorder{}
oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder) oex := operationexecutor.NewOperationExecutor(kubeClient, volumePluginMgr, fakeRecorder, false)
reconciler := NewReconciler( reconciler := NewReconciler(
kubeClient, kubeClient,
true, /* controllerAttachDetachEnabled */ true, /* controllerAttachDetachEnabled */
......
...@@ -156,7 +156,8 @@ func NewVolumeManager( ...@@ -156,7 +156,8 @@ func NewVolumeManager(
kubeContainerRuntime kubecontainer.Runtime, kubeContainerRuntime kubecontainer.Runtime,
mounter mount.Interface, mounter mount.Interface,
kubeletPodsDir string, kubeletPodsDir string,
recorder record.EventRecorder) (VolumeManager, error) { recorder record.EventRecorder,
checkNodeCapabilitiesBeforeMount bool) (VolumeManager, error) {
vm := &volumeManager{ vm := &volumeManager{
kubeClient: kubeClient, kubeClient: kubeClient,
...@@ -166,7 +167,8 @@ func NewVolumeManager( ...@@ -166,7 +167,8 @@ func NewVolumeManager(
operationExecutor: operationexecutor.NewOperationExecutor( operationExecutor: operationexecutor.NewOperationExecutor(
kubeClient, kubeClient,
volumePluginMgr, volumePluginMgr,
recorder), recorder,
checkNodeCapabilitiesBeforeMount),
} }
vm.reconciler = reconciler.NewReconciler( vm.reconciler = reconciler.NewReconciler(
......
...@@ -195,7 +195,8 @@ func newTestVolumeManager( ...@@ -195,7 +195,8 @@ func newTestVolumeManager(
&containertest.FakeRuntime{}, &containertest.FakeRuntime{},
&mount.FakeMounter{}, &mount.FakeMounter{},
"", "",
fakeRecorder) fakeRecorder,
false /* experimentalCheckNodeCapabilitiesBeforeMount*/)
return vm, err return vm, err
} }
......
...@@ -248,6 +248,13 @@ func (b *awsElasticBlockStoreMounter) GetAttributes() volume.Attributes { ...@@ -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. // SetUp attaches the disk and bind mounts to the volume path.
func (b *awsElasticBlockStoreMounter) SetUp(fsGroup *int64) error { func (b *awsElasticBlockStoreMounter) SetUp(fsGroup *int64) error {
return b.SetUpAt(b.GetPath(), fsGroup) return b.SetUpAt(b.GetPath(), fsGroup)
......
...@@ -217,6 +217,13 @@ func (b *azureDiskMounter) GetAttributes() volume.Attributes { ...@@ -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. // SetUp attaches the disk and bind mounts to the volume path.
func (b *azureDiskMounter) SetUp(fsGroup *int64) error { func (b *azureDiskMounter) SetUp(fsGroup *int64) error {
return b.SetUpAt(b.GetPath(), fsGroup) return b.SetUpAt(b.GetPath(), fsGroup)
......
...@@ -168,6 +168,13 @@ func (b *azureFileMounter) GetAttributes() volume.Attributes { ...@@ -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. // SetUp attaches the disk and bind mounts to the volume path.
func (b *azureFileMounter) SetUp(fsGroup *int64) error { func (b *azureFileMounter) SetUp(fsGroup *int64) error {
return b.SetUpAt(b.GetPath(), fsGroup) return b.SetUpAt(b.GetPath(), fsGroup)
......
...@@ -196,6 +196,13 @@ func (cephfsVolume *cephfsMounter) GetAttributes() volume.Attributes { ...@@ -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. // SetUp attaches the disk and bind mounts to the volume path.
func (cephfsVolume *cephfsMounter) SetUp(fsGroup *int64) error { func (cephfsVolume *cephfsMounter) SetUp(fsGroup *int64) error {
return cephfsVolume.SetUpAt(cephfsVolume.GetPath(), fsGroup) return cephfsVolume.SetUpAt(cephfsVolume.GetPath(), fsGroup)
......
...@@ -281,6 +281,13 @@ func (b *cinderVolumeMounter) GetAttributes() volume.Attributes { ...@@ -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 { func (b *cinderVolumeMounter) SetUp(fsGroup *int64) error {
return b.SetUpAt(b.GetPath(), fsGroup) return b.SetUpAt(b.GetPath(), fsGroup)
} }
......
...@@ -141,6 +141,13 @@ func wrappedVolumeSpec() volume.Spec { ...@@ -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 { func (b *configMapVolumeMounter) SetUp(fsGroup *int64) error {
return b.SetUpAt(b.GetPath(), fsGroup) return b.SetUpAt(b.GetPath(), fsGroup)
} }
......
...@@ -147,6 +147,13 @@ func (d *downwardAPIVolume) GetAttributes() volume.Attributes { ...@@ -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. // SetUp puts in place the volume plugin.
// This function is not idempotent by design. We want the data to be refreshed periodically. // 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. // The internal sync interval of kubelet will drive the refresh of data.
......
...@@ -174,6 +174,13 @@ func (ed *emptyDir) GetAttributes() volume.Attributes { ...@@ -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. // SetUp creates new directory.
func (ed *emptyDir) SetUp(fsGroup *int64) error { func (ed *emptyDir) SetUp(fsGroup *int64) error {
return ed.SetUpAt(ed.GetPath(), fsGroup) return ed.SetUpAt(ed.GetPath(), fsGroup)
......
...@@ -187,6 +187,14 @@ func (b *fcDiskMounter) GetAttributes() volume.Attributes { ...@@ -187,6 +187,14 @@ func (b *fcDiskMounter) GetAttributes() volume.Attributes {
SupportsSELinux: true, 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 { func (b *fcDiskMounter) SetUp(fsGroup *int64) error {
return b.SetUpAt(b.GetPath(), fsGroup) return b.SetUpAt(b.GetPath(), fsGroup)
} }
......
...@@ -260,6 +260,13 @@ func (f flexVolumeMounter) GetAttributes() volume.Attributes { ...@@ -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. // flexVolumeManager is the abstract interface to flex volume ops.
type flexVolumeManager interface { type flexVolumeManager interface {
// Attaches the disk to the kubelet's host machine. // Attaches the disk to the kubelet's host machine.
......
...@@ -210,6 +210,14 @@ func (b *flockerVolumeMounter) GetAttributes() volume.Attributes { ...@@ -210,6 +210,14 @@ func (b *flockerVolumeMounter) GetAttributes() volume.Attributes {
SupportsSELinux: false, 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 { func (b *flockerVolumeMounter) GetPath() string {
return getPath(b.podUID, b.volName, b.plugin.host) return getPath(b.podUID, b.volName, b.plugin.host)
} }
......
...@@ -238,6 +238,13 @@ func (b *gcePersistentDiskMounter) GetAttributes() volume.Attributes { ...@@ -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. // SetUp bind mounts the disk global mount to the volume path.
func (b *gcePersistentDiskMounter) SetUp(fsGroup *int64) error { func (b *gcePersistentDiskMounter) SetUp(fsGroup *int64) error {
return b.SetUpAt(b.GetPath(), fsGroup) return b.SetUpAt(b.GetPath(), fsGroup)
......
...@@ -155,6 +155,13 @@ func (b *gitRepoVolumeMounter) GetAttributes() volume.Attributes { ...@@ -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. // SetUp creates new directory and clones a git repo.
func (b *gitRepoVolumeMounter) SetUp(fsGroup *int64) error { func (b *gitRepoVolumeMounter) SetUp(fsGroup *int64) error {
return b.SetUpAt(b.GetPath(), fsGroup) return b.SetUpAt(b.GetPath(), fsGroup)
......
...@@ -207,6 +207,13 @@ func (b *glusterfsMounter) GetAttributes() volume.Attributes { ...@@ -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. // SetUp attaches the disk and bind mounts to the volume path.
func (b *glusterfsMounter) SetUp(fsGroup *int64) error { func (b *glusterfsMounter) SetUp(fsGroup *int64) error {
return b.SetUpAt(b.GetPath(), fsGroup) return b.SetUpAt(b.GetPath(), fsGroup)
......
...@@ -186,6 +186,13 @@ func (b *hostPathMounter) GetAttributes() volume.Attributes { ...@@ -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. // SetUp does nothing.
func (b *hostPathMounter) SetUp(fsGroup *int64) error { func (b *hostPathMounter) SetUp(fsGroup *int64) error {
return nil return nil
......
...@@ -196,6 +196,13 @@ func (b *iscsiDiskMounter) GetAttributes() volume.Attributes { ...@@ -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 { func (b *iscsiDiskMounter) SetUp(fsGroup *int64) error {
return b.SetUpAt(b.GetPath(), fsGroup) return b.SetUpAt(b.GetPath(), fsGroup)
} }
......
...@@ -20,6 +20,7 @@ go_library( ...@@ -20,6 +20,7 @@ go_library(
deps = [ deps = [
"//pkg/api:go_default_library", "//pkg/api:go_default_library",
"//pkg/types:go_default_library", "//pkg/types:go_default_library",
"//pkg/util/exec:go_default_library",
"//pkg/util/mount:go_default_library", "//pkg/util/mount:go_default_library",
"//pkg/util/strings:go_default_library", "//pkg/util/strings:go_default_library",
"//pkg/volume:go_default_library", "//pkg/volume:go_default_library",
......
...@@ -19,14 +19,15 @@ package nfs ...@@ -19,14 +19,15 @@ package nfs
import ( import (
"fmt" "fmt"
"os" "os"
"runtime"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util/exec"
"k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/util/strings" "k8s.io/kubernetes/pkg/util/strings"
"k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume"
"github.com/golang/glog"
) )
// This is the primary entrypoint for volume plugins. // This is the primary entrypoint for volume plugins.
...@@ -159,6 +160,32 @@ func (nfsVolume *nfs) GetPath() string { ...@@ -159,6 +160,32 @@ func (nfsVolume *nfs) GetPath() string {
return nfsVolume.plugin.host.GetPodVolumeDir(nfsVolume.pod.UID, strings.EscapeQualifiedNameForDisk(name), nfsVolume.volName) 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 { type nfsMounter struct {
*nfs *nfs
server string server string
......
...@@ -46,6 +46,7 @@ func TestCanSupport(t *testing.T) { ...@@ -46,6 +46,7 @@ func TestCanSupport(t *testing.T) {
if plug.GetPluginName() != "kubernetes.io/nfs" { if plug.GetPluginName() != "kubernetes.io/nfs" {
t.Errorf("Wrong name: %s", plug.GetPluginName()) t.Errorf("Wrong name: %s", plug.GetPluginName())
} }
if !plug.CanSupport(&volume.Spec{Volume: &api.Volume{VolumeSource: api.VolumeSource{NFS: &api.NFSVolumeSource{}}}}) { if !plug.CanSupport(&volume.Spec{Volume: &api.Volume{VolumeSource: api.VolumeSource{NFS: &api.NFSVolumeSource{}}}}) {
t.Errorf("Expected true") t.Errorf("Expected true")
} }
......
...@@ -169,6 +169,13 @@ func (b *photonPersistentDiskMounter) GetAttributes() volume.Attributes { ...@@ -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. // SetUp attaches the disk and bind mounts to the volume path.
func (b *photonPersistentDiskMounter) SetUp(fsGroup *int64) error { func (b *photonPersistentDiskMounter) SetUp(fsGroup *int64) error {
return b.SetUpAt(b.GetPath(), fsGroup) return b.SetUpAt(b.GetPath(), fsGroup)
......
...@@ -218,6 +218,13 @@ func (mounter *quobyteMounter) GetAttributes() volume.Attributes { ...@@ -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. // SetUp attaches the disk and bind mounts to the volume path.
func (mounter *quobyteMounter) SetUp(fsGroup *int64) error { func (mounter *quobyteMounter) SetUp(fsGroup *int64) error {
pluginDir := mounter.plugin.host.GetPluginDir(strings.EscapeQualifiedNameForDisk(quobytePluginName)) pluginDir := mounter.plugin.host.GetPluginDir(strings.EscapeQualifiedNameForDisk(quobytePluginName))
......
...@@ -381,6 +381,13 @@ func (b *rbd) GetAttributes() volume.Attributes { ...@@ -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 { func (b *rbdMounter) SetUp(fsGroup *int64) error {
return b.SetUpAt(b.GetPath(), fsGroup) return b.SetUpAt(b.GetPath(), fsGroup)
} }
......
...@@ -158,6 +158,14 @@ func (sv *secretVolume) GetAttributes() volume.Attributes { ...@@ -158,6 +158,14 @@ func (sv *secretVolume) GetAttributes() volume.Attributes {
SupportsSELinux: true, 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 { func (b *secretVolumeMounter) SetUp(fsGroup *int64) error {
// Update each Slash "/" character for Windows with seperator character // Update each Slash "/" character for Windows with seperator character
dir := b.GetPath() dir := b.GetPath()
......
...@@ -320,6 +320,10 @@ func (_ *FakeVolume) GetAttributes() Attributes { ...@@ -320,6 +320,10 @@ func (_ *FakeVolume) GetAttributes() Attributes {
} }
} }
func (fv *FakeVolume) CanMount() error {
return nil
}
func (fv *FakeVolume) SetUp(fsGroup *int64) error { func (fv *FakeVolume) SetUp(fsGroup *int64) error {
fv.Lock() fv.Lock()
defer fv.Unlock() defer fv.Unlock()
......
...@@ -119,7 +119,8 @@ type OperationExecutor interface { ...@@ -119,7 +119,8 @@ type OperationExecutor interface {
func NewOperationExecutor( func NewOperationExecutor(
kubeClient internalclientset.Interface, kubeClient internalclientset.Interface,
volumePluginMgr *volume.VolumePluginMgr, volumePluginMgr *volume.VolumePluginMgr,
recorder record.EventRecorder) OperationExecutor { recorder record.EventRecorder,
checkNodeCapabilitiesBeforeMount bool) OperationExecutor {
return &operationExecutor{ return &operationExecutor{
kubeClient: kubeClient, kubeClient: kubeClient,
...@@ -127,6 +128,7 @@ func NewOperationExecutor( ...@@ -127,6 +128,7 @@ func NewOperationExecutor(
pendingOperations: nestedpendingoperations.NewNestedPendingOperations( pendingOperations: nestedpendingoperations.NewNestedPendingOperations(
true /* exponentialBackOffOnError */), true /* exponentialBackOffOnError */),
recorder: recorder, recorder: recorder,
checkNodeCapabilitiesBeforeMount: checkNodeCapabilitiesBeforeMount,
} }
} }
...@@ -371,6 +373,11 @@ type operationExecutor struct { ...@@ -371,6 +373,11 @@ type operationExecutor struct {
// recorder is used to record events in the API server // recorder is used to record events in the API server
recorder record.EventRecorder 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 { func (oe *operationExecutor) IsOperationPending(volumeName api.UniqueVolumeName, podName volumetypes.UniquePodName) bool {
...@@ -877,6 +884,15 @@ func (oe *operationExecutor) generateMountVolumeFunc( ...@@ -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 // Execute mount
mountErr := volumeMounter.SetUp(fsGroup) mountErr := volumeMounter.SetUp(fsGroup)
if mountErr != nil { if mountErr != nil {
......
...@@ -94,6 +94,18 @@ type Attributes struct { ...@@ -94,6 +94,18 @@ type Attributes struct {
type Mounter interface { type Mounter interface {
// Uses Interface to provide the path for Docker binds. // Uses Interface to provide the path for Docker binds.
Volume 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 // SetUp prepares and mounts/unpacks the volume to a
// self-determined directory path. The mount point and its // self-determined directory path. The mount point and its
// content should be owned by 'fsGroup' so that it can be // content should be owned by 'fsGroup' so that it can be
......
...@@ -177,6 +177,13 @@ func (b *vsphereVolumeMounter) SetUp(fsGroup *int64) error { ...@@ -177,6 +177,13 @@ func (b *vsphereVolumeMounter) SetUp(fsGroup *int64) error {
return b.SetUpAt(b.GetPath(), fsGroup) 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. // SetUp attaches the disk and bind mounts to the volume path.
func (b *vsphereVolumeMounter) SetUpAt(dir string, fsGroup *int64) error { func (b *vsphereVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
glog.V(5).Infof("vSphere volume setup %s to %s", b.volPath, dir) glog.V(5).Infof("vSphere volume setup %s to %s", b.volPath, dir)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment