diff --git a/api/swagger-spec/v1.json b/api/swagger-spec/v1.json
index c32d962c951..ccd134cc8d8 100644
--- a/api/swagger-spec/v1.json
+++ b/api/swagger-spec/v1.json
@@ -16761,6 +16761,13 @@
"$ref": "v1.ContainerImage"
},
"description": "List of container images on this node"
+ },
+ "volumesInUse": {
+ "type": "array",
+ "items": {
+ "$ref": "v1.UniqueDeviceName"
+ },
+ "description": "List of volumes in use (mounted) by the node."
}
}
},
@@ -16919,6 +16926,10 @@
}
}
},
+ "v1.UniqueDeviceName": {
+ "id": "v1.UniqueDeviceName",
+ "properties": {}
+ },
"v1.PersistentVolumeClaimList": {
"id": "v1.PersistentVolumeClaimList",
"description": "PersistentVolumeClaimList is a list of PersistentVolumeClaim items.",
diff --git a/cmd/kubelet/app/options/options.go b/cmd/kubelet/app/options/options.go
index c64e71a552d..eca5c76d24f 100644
--- a/cmd/kubelet/app/options/options.go
+++ b/cmd/kubelet/app/options/options.go
@@ -78,48 +78,49 @@ func NewKubeletServer() *KubeletServer {
SystemReserved: make(config.ConfigurationMap),
KubeReserved: make(config.ConfigurationMap),
KubeletConfiguration: componentconfig.KubeletConfiguration{
- Address: "0.0.0.0",
- CAdvisorPort: 4194,
- VolumeStatsAggPeriod: unversioned.Duration{Duration: time.Minute},
- CertDirectory: "/var/run/kubernetes",
- CgroupRoot: "",
- ConfigureCBR0: false,
- ContainerRuntime: "docker",
- CPUCFSQuota: true,
- DockerExecHandlerName: "native",
- EventBurst: 10,
- EventRecordQPS: 5.0,
- EnableCustomMetrics: false,
- EnableDebuggingHandlers: true,
- EnableServer: true,
- FileCheckFrequency: unversioned.Duration{Duration: 20 * time.Second},
- HealthzBindAddress: "127.0.0.1",
- HealthzPort: 10248,
- HostNetworkSources: kubetypes.AllSource,
- HostPIDSources: kubetypes.AllSource,
- HostIPCSources: kubetypes.AllSource,
- HTTPCheckFrequency: unversioned.Duration{Duration: 20 * time.Second},
- ImageMinimumGCAge: unversioned.Duration{Duration: 2 * time.Minute},
- ImageGCHighThresholdPercent: 90,
- ImageGCLowThresholdPercent: 80,
- LowDiskSpaceThresholdMB: 256,
- MasterServiceNamespace: api.NamespaceDefault,
- MaxContainerCount: 240,
- MaxPerPodContainerCount: 2,
- MaxOpenFiles: 1000000,
- MaxPods: 110,
- NvidiaGPUs: 0,
- MinimumGCAge: unversioned.Duration{Duration: 1 * time.Minute},
- NetworkPluginDir: "/usr/libexec/kubernetes/kubelet-plugins/net/exec/",
- NetworkPluginName: "",
- NonMasqueradeCIDR: "10.0.0.0/8",
- VolumePluginDir: "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/",
- NodeStatusUpdateFrequency: unversioned.Duration{Duration: 10 * time.Second},
- NodeLabels: make(map[string]string),
- OOMScoreAdj: int32(qos.KubeletOOMScoreAdj),
- LockFilePath: "",
- ExitOnLockContention: false,
- PodInfraContainerImage: GetDefaultPodInfraContainerImage(),
+ Address: "0.0.0.0",
+ CAdvisorPort: 4194,
+ VolumeStatsAggPeriod: unversioned.Duration{Duration: time.Minute},
+ CertDirectory: "/var/run/kubernetes",
+ CgroupRoot: "",
+ ConfigureCBR0: false,
+ ContainerRuntime: "docker",
+ CPUCFSQuota: true,
+ DockerExecHandlerName: "native",
+ EventBurst: 10,
+ EventRecordQPS: 5.0,
+ EnableControllerAttachDetach: true,
+ EnableCustomMetrics: false,
+ EnableDebuggingHandlers: true,
+ EnableServer: true,
+ FileCheckFrequency: unversioned.Duration{Duration: 20 * time.Second},
+ HealthzBindAddress: "127.0.0.1",
+ HealthzPort: 10248,
+ HostNetworkSources: kubetypes.AllSource,
+ HostPIDSources: kubetypes.AllSource,
+ HostIPCSources: kubetypes.AllSource,
+ HTTPCheckFrequency: unversioned.Duration{Duration: 20 * time.Second},
+ ImageMinimumGCAge: unversioned.Duration{Duration: 2 * time.Minute},
+ ImageGCHighThresholdPercent: 90,
+ ImageGCLowThresholdPercent: 80,
+ LowDiskSpaceThresholdMB: 256,
+ MasterServiceNamespace: api.NamespaceDefault,
+ MaxContainerCount: 240,
+ MaxPerPodContainerCount: 2,
+ MaxOpenFiles: 1000000,
+ MaxPods: 110,
+ NvidiaGPUs: 0,
+ MinimumGCAge: unversioned.Duration{Duration: 1 * time.Minute},
+ NetworkPluginDir: "/usr/libexec/kubernetes/kubelet-plugins/net/exec/",
+ NetworkPluginName: "",
+ NonMasqueradeCIDR: "10.0.0.0/8",
+ VolumePluginDir: "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/",
+ NodeStatusUpdateFrequency: unversioned.Duration{Duration: 10 * time.Second},
+ NodeLabels: make(map[string]string),
+ OOMScoreAdj: int32(qos.KubeletOOMScoreAdj),
+ LockFilePath: "",
+ ExitOnLockContention: false,
+ PodInfraContainerImage: GetDefaultPodInfraContainerImage(),
Port: ports.KubeletPort,
ReadOnlyPort: ports.KubeletReadOnlyPort,
RegisterNode: true, // will be ignored if no apiserver is configured
@@ -243,6 +244,8 @@ func (s *KubeletServer) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&s.PodCIDR, "pod-cidr", "", "The CIDR to use for pod IP addresses, only used in standalone mode. In cluster mode, this is obtained from the master.")
fs.StringVar(&s.ResolverConfig, "resolv-conf", s.ResolverConfig, "Resolver configuration file used as the basis for the container DNS resolution configuration.")
fs.BoolVar(&s.CPUCFSQuota, "cpu-cfs-quota", s.CPUCFSQuota, "Enable CPU CFS quota enforcement for containers that specify CPU limits")
+ fs.BoolVar(&s.EnableControllerAttachDetach, "enable-controller-attach-detach", s.EnableControllerAttachDetach, "Enables the Attach/Detach controller to manage attachment/detachment of volumes scheduled to this node, and disables kubelet from executing any attach/detach operations")
+
// Flags intended for testing, not recommended used in production environments.
fs.BoolVar(&s.ReallyCrashForTesting, "really-crash-for-testing", s.ReallyCrashForTesting, "If true, when panics occur crash. Intended for testing.")
fs.Float64Var(&s.ChaosChance, "chaos-chance", s.ChaosChance, "If > 0.0, introduce random client errors and latency. Intended for testing. [default=0.0]")
diff --git a/cmd/kubelet/app/server.go b/cmd/kubelet/app/server.go
index a3dbb4728ea..a27e7d1d4c1 100644
--- a/cmd/kubelet/app/server.go
+++ b/cmd/kubelet/app/server.go
@@ -197,57 +197,58 @@ func UnsecuredKubeletConfig(s *options.KubeletServer) (*KubeletConfig, error) {
}
return &KubeletConfig{
- Address: net.ParseIP(s.Address),
- AllowPrivileged: s.AllowPrivileged,
- Auth: nil, // default does not enforce auth[nz]
- CAdvisorInterface: nil, // launches background processes, not set here
- VolumeStatsAggPeriod: s.VolumeStatsAggPeriod.Duration,
- CgroupRoot: s.CgroupRoot,
- Cloud: nil, // cloud provider might start background processes
- ClusterDNS: net.ParseIP(s.ClusterDNS),
- ClusterDomain: s.ClusterDomain,
- ConfigFile: s.Config,
- ConfigureCBR0: s.ConfigureCBR0,
- ContainerManager: nil,
- ContainerRuntime: s.ContainerRuntime,
- CPUCFSQuota: s.CPUCFSQuota,
- DiskSpacePolicy: diskSpacePolicy,
- DockerClient: dockertools.ConnectToDockerOrDie(s.DockerEndpoint),
- RuntimeCgroups: s.RuntimeCgroups,
- DockerExecHandler: dockerExecHandler,
- EnableCustomMetrics: s.EnableCustomMetrics,
- EnableDebuggingHandlers: s.EnableDebuggingHandlers,
- EnableServer: s.EnableServer,
- EventBurst: int(s.EventBurst),
- EventRecordQPS: s.EventRecordQPS,
- FileCheckFrequency: s.FileCheckFrequency.Duration,
- HostnameOverride: s.HostnameOverride,
- HostNetworkSources: hostNetworkSources,
- HostPIDSources: hostPIDSources,
- HostIPCSources: hostIPCSources,
- HTTPCheckFrequency: s.HTTPCheckFrequency.Duration,
- ImageGCPolicy: imageGCPolicy,
- KubeClient: nil,
- ManifestURL: s.ManifestURL,
- ManifestURLHeader: manifestURLHeader,
- MasterServiceNamespace: s.MasterServiceNamespace,
- MaxContainerCount: int(s.MaxContainerCount),
- MaxOpenFiles: s.MaxOpenFiles,
- MaxPerPodContainerCount: int(s.MaxPerPodContainerCount),
- MaxPods: int(s.MaxPods),
- NvidiaGPUs: int(s.NvidiaGPUs),
- MinimumGCAge: s.MinimumGCAge.Duration,
- Mounter: mounter,
- NetworkPluginName: s.NetworkPluginName,
- NetworkPlugins: ProbeNetworkPlugins(s.NetworkPluginDir),
- NodeLabels: s.NodeLabels,
- NodeStatusUpdateFrequency: s.NodeStatusUpdateFrequency.Duration,
- NonMasqueradeCIDR: s.NonMasqueradeCIDR,
- OOMAdjuster: oom.NewOOMAdjuster(),
- OSInterface: kubecontainer.RealOS{},
- PodCIDR: s.PodCIDR,
- ReconcileCIDR: s.ReconcileCIDR,
- PodInfraContainerImage: s.PodInfraContainerImage,
+ Address: net.ParseIP(s.Address),
+ AllowPrivileged: s.AllowPrivileged,
+ Auth: nil, // default does not enforce auth[nz]
+ CAdvisorInterface: nil, // launches background processes, not set here
+ VolumeStatsAggPeriod: s.VolumeStatsAggPeriod.Duration,
+ CgroupRoot: s.CgroupRoot,
+ Cloud: nil, // cloud provider might start background processes
+ ClusterDNS: net.ParseIP(s.ClusterDNS),
+ ClusterDomain: s.ClusterDomain,
+ ConfigFile: s.Config,
+ ConfigureCBR0: s.ConfigureCBR0,
+ ContainerManager: nil,
+ ContainerRuntime: s.ContainerRuntime,
+ CPUCFSQuota: s.CPUCFSQuota,
+ DiskSpacePolicy: diskSpacePolicy,
+ DockerClient: dockertools.ConnectToDockerOrDie(s.DockerEndpoint),
+ RuntimeCgroups: s.RuntimeCgroups,
+ DockerExecHandler: dockerExecHandler,
+ EnableControllerAttachDetach: s.EnableControllerAttachDetach,
+ EnableCustomMetrics: s.EnableCustomMetrics,
+ EnableDebuggingHandlers: s.EnableDebuggingHandlers,
+ EnableServer: s.EnableServer,
+ EventBurst: int(s.EventBurst),
+ EventRecordQPS: s.EventRecordQPS,
+ FileCheckFrequency: s.FileCheckFrequency.Duration,
+ HostnameOverride: s.HostnameOverride,
+ HostNetworkSources: hostNetworkSources,
+ HostPIDSources: hostPIDSources,
+ HostIPCSources: hostIPCSources,
+ HTTPCheckFrequency: s.HTTPCheckFrequency.Duration,
+ ImageGCPolicy: imageGCPolicy,
+ KubeClient: nil,
+ ManifestURL: s.ManifestURL,
+ ManifestURLHeader: manifestURLHeader,
+ MasterServiceNamespace: s.MasterServiceNamespace,
+ MaxContainerCount: int(s.MaxContainerCount),
+ MaxOpenFiles: s.MaxOpenFiles,
+ MaxPerPodContainerCount: int(s.MaxPerPodContainerCount),
+ MaxPods: int(s.MaxPods),
+ NvidiaGPUs: int(s.NvidiaGPUs),
+ MinimumGCAge: s.MinimumGCAge.Duration,
+ Mounter: mounter,
+ NetworkPluginName: s.NetworkPluginName,
+ NetworkPlugins: ProbeNetworkPlugins(s.NetworkPluginDir),
+ NodeLabels: s.NodeLabels,
+ NodeStatusUpdateFrequency: s.NodeStatusUpdateFrequency.Duration,
+ NonMasqueradeCIDR: s.NonMasqueradeCIDR,
+ OOMAdjuster: oom.NewOOMAdjuster(),
+ OSInterface: kubecontainer.RealOS{},
+ PodCIDR: s.PodCIDR,
+ ReconcileCIDR: s.ReconcileCIDR,
+ PodInfraContainerImage: s.PodInfraContainerImage,
Port: s.Port,
ReadOnlyPort: s.ReadOnlyPort,
RegisterNode: s.RegisterNode,
@@ -550,24 +551,25 @@ func SimpleKubelet(client *clientset.Clientset,
PressureTransitionPeriod: evictionPressureTransitionPeriod,
}
kcfg := KubeletConfig{
- Address: net.ParseIP(address),
- CAdvisorInterface: cadvisorInterface,
- VolumeStatsAggPeriod: time.Minute,
- CgroupRoot: "",
- Cloud: cloud,
- ClusterDNS: clusterDNS,
- ConfigFile: configFilePath,
- ContainerManager: containerManager,
- ContainerRuntime: "docker",
- CPUCFSQuota: true,
- DiskSpacePolicy: diskSpacePolicy,
- DockerClient: dockerClient,
- RuntimeCgroups: "",
- DockerExecHandler: &dockertools.NativeExecHandler{},
- EnableCustomMetrics: false,
- EnableDebuggingHandlers: true,
- EnableServer: true,
- FileCheckFrequency: fileCheckFrequency,
+ Address: net.ParseIP(address),
+ CAdvisorInterface: cadvisorInterface,
+ VolumeStatsAggPeriod: time.Minute,
+ CgroupRoot: "",
+ Cloud: cloud,
+ ClusterDNS: clusterDNS,
+ ConfigFile: configFilePath,
+ ContainerManager: containerManager,
+ ContainerRuntime: "docker",
+ CPUCFSQuota: true,
+ DiskSpacePolicy: diskSpacePolicy,
+ DockerClient: dockerClient,
+ RuntimeCgroups: "",
+ DockerExecHandler: &dockertools.NativeExecHandler{},
+ EnableControllerAttachDetach: false,
+ EnableCustomMetrics: false,
+ EnableDebuggingHandlers: true,
+ EnableServer: true,
+ FileCheckFrequency: fileCheckFrequency,
// Since this kubelet runs with --configure-cbr0=false, it needs to use
// hairpin-veth to allow hairpin packets. Note that this deviates from
// what the "real" kubelet currently does, because there's no way to
@@ -784,6 +786,7 @@ type KubeletConfig struct {
DockerClient dockertools.DockerInterface
RuntimeCgroups string
DockerExecHandler dockertools.ExecHandler
+ EnableControllerAttachDetach bool
EnableCustomMetrics bool
EnableDebuggingHandlers bool
EnableServer bool
@@ -948,6 +951,7 @@ func CreateAndInitKubelet(kc *KubeletConfig) (k KubeletBootstrap, pc *config.Pod
kc.BabysitDaemons,
kc.EvictionConfig,
kc.Options,
+ kc.EnableControllerAttachDetach,
)
if err != nil {
diff --git a/docs/admin/kubelet.md b/docs/admin/kubelet.md
index 1d4f0e4802d..521d721402c 100644
--- a/docs/admin/kubelet.md
+++ b/docs/admin/kubelet.md
@@ -82,6 +82,7 @@ kubelet
--cpu-cfs-quota[=true]: Enable CPU CFS quota enforcement for containers that specify CPU limits
--docker-endpoint="": If non-empty, use this for the docker endpoint to communicate with
--docker-exec-handler="native": Handler to use when executing a command in a container. Valid values are 'native' and 'nsenter'. Defaults to 'native'.
+ --enable-controller-attach-detach[=true]: Enables the Attach/Detach controller to manage attachment/detachment of volumes scheduled to this node, and disables kubelet from executing any attach/detach operations
--enable-custom-metrics[=false]: Support for gathering custom metrics.
--enable-debugging-handlers[=true]: Enables server endpoints for log collection and local running of containers and commands
--enable-server[=true]: Enable the Kubelet's server
diff --git a/docs/api-reference/v1/definitions.html b/docs/api-reference/v1/definitions.html
index 31b5ed299bf..660eecdebd1 100755
--- a/docs/api-reference/v1/definitions.html
+++ b/docs/api-reference/v1/definitions.html
@@ -594,6 +594,10 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
+
+
v1.Preconditions
@@ -4751,6 +4755,13 @@ The resulting set of endpoints can be viewed as:
v1.ContainerImage array |
|
+
+volumesInUse |
+List of volumes in use (mounted) by the node. |
+false |
+v1.UniqueDeviceName array |
+ |
+
diff --git a/hack/verify-flags/known-flags.txt b/hack/verify-flags/known-flags.txt
index 796afbb6d03..505c2ff8c5a 100644
--- a/hack/verify-flags/known-flags.txt
+++ b/hack/verify-flags/known-flags.txt
@@ -111,6 +111,7 @@ dump-logs-on-failure
duration-sec
e2e-output-dir
e2e-verify-service-account
+enable-controller-attach-detach
enable-custom-metrics
enable-debugging-handlers
enable-garbage-collector
diff --git a/pkg/api/deep_copy_generated.go b/pkg/api/deep_copy_generated.go
index a535f40a28c..7ceea68b1ff 100644
--- a/pkg/api/deep_copy_generated.go
+++ b/pkg/api/deep_copy_generated.go
@@ -1601,6 +1601,15 @@ func DeepCopy_api_NodeStatus(in NodeStatus, out *NodeStatus, c *conversion.Clone
} else {
out.Images = nil
}
+ if in.VolumesInUse != nil {
+ in, out := in.VolumesInUse, &out.VolumesInUse
+ *out = make([]UniqueDeviceName, len(in))
+ for i := range in {
+ (*out)[i] = in[i]
+ }
+ } else {
+ out.VolumesInUse = nil
+ }
return nil
}
diff --git a/pkg/api/types.generated.go b/pkg/api/types.generated.go
index 6993ed34523..b7c6d05a4d8 100644
--- a/pkg/api/types.generated.go
+++ b/pkg/api/types.generated.go
@@ -36497,7 +36497,7 @@ func (x *NodeStatus) CodecEncodeSelf(e *codec1978.Encoder) {
} else {
yysep2 := !z.EncBinary()
yy2arr2 := z.EncBasicHandle().StructToArray
- var yyq2 [8]bool
+ var yyq2 [9]bool
_, _, _ = yysep2, yyq2, yy2arr2
const yyr2 bool = false
yyq2[0] = len(x.Capacity) != 0
@@ -36508,9 +36508,10 @@ func (x *NodeStatus) CodecEncodeSelf(e *codec1978.Encoder) {
yyq2[5] = true
yyq2[6] = true
yyq2[7] = len(x.Images) != 0
+ yyq2[8] = len(x.VolumesInUse) != 0
var yynn2 int
if yyr2 || yy2arr2 {
- r.EncodeArrayStart(8)
+ r.EncodeArrayStart(9)
} else {
yynn2 = 0
for _, b := range yyq2 {
@@ -36715,6 +36716,39 @@ func (x *NodeStatus) CodecEncodeSelf(e *codec1978.Encoder) {
}
}
}
+ if yyr2 || yy2arr2 {
+ z.EncSendContainerState(codecSelfer_containerArrayElem1234)
+ if yyq2[8] {
+ if x.VolumesInUse == nil {
+ r.EncodeNil()
+ } else {
+ yym32 := z.EncBinary()
+ _ = yym32
+ if false {
+ } else {
+ h.encSliceUniqueDeviceName(([]UniqueDeviceName)(x.VolumesInUse), e)
+ }
+ }
+ } else {
+ r.EncodeNil()
+ }
+ } else {
+ if yyq2[8] {
+ z.EncSendContainerState(codecSelfer_containerMapKey1234)
+ r.EncodeString(codecSelferC_UTF81234, string("volumesInUse"))
+ z.EncSendContainerState(codecSelfer_containerMapValue1234)
+ if x.VolumesInUse == nil {
+ r.EncodeNil()
+ } else {
+ yym33 := z.EncBinary()
+ _ = yym33
+ if false {
+ } else {
+ h.encSliceUniqueDeviceName(([]UniqueDeviceName)(x.VolumesInUse), e)
+ }
+ }
+ }
+ }
if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayEnd1234)
} else {
@@ -36846,6 +36880,18 @@ func (x *NodeStatus) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
h.decSliceContainerImage((*[]ContainerImage)(yyv13), d)
}
}
+ case "volumesInUse":
+ if r.TryDecodeAsNil() {
+ x.VolumesInUse = nil
+ } else {
+ yyv15 := &x.VolumesInUse
+ yym16 := z.DecBinary()
+ _ = yym16
+ if false {
+ } else {
+ h.decSliceUniqueDeviceName((*[]UniqueDeviceName)(yyv15), d)
+ }
+ }
default:
z.DecStructFieldNotFound(-1, yys3)
} // end switch yys3
@@ -36857,16 +36903,16 @@ func (x *NodeStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
var h codecSelfer1234
z, r := codec1978.GenHelperDecoder(d)
_, _, _ = h, z, r
- var yyj15 int
- var yyb15 bool
- var yyhl15 bool = l >= 0
- yyj15++
- if yyhl15 {
- yyb15 = yyj15 > l
+ var yyj17 int
+ var yyb17 bool
+ var yyhl17 bool = l >= 0
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
} else {
- yyb15 = r.CheckBreak()
+ yyb17 = r.CheckBreak()
}
- if yyb15 {
+ if yyb17 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -36874,16 +36920,16 @@ func (x *NodeStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.Capacity = nil
} else {
- yyv16 := &x.Capacity
- yyv16.CodecDecodeSelf(d)
+ yyv18 := &x.Capacity
+ yyv18.CodecDecodeSelf(d)
}
- yyj15++
- if yyhl15 {
- yyb15 = yyj15 > l
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
} else {
- yyb15 = r.CheckBreak()
+ yyb17 = r.CheckBreak()
}
- if yyb15 {
+ if yyb17 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -36891,16 +36937,16 @@ func (x *NodeStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.Allocatable = nil
} else {
- yyv17 := &x.Allocatable
- yyv17.CodecDecodeSelf(d)
+ yyv19 := &x.Allocatable
+ yyv19.CodecDecodeSelf(d)
}
- yyj15++
- if yyhl15 {
- yyb15 = yyj15 > l
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
} else {
- yyb15 = r.CheckBreak()
+ yyb17 = r.CheckBreak()
}
- if yyb15 {
+ if yyb17 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -36910,13 +36956,13 @@ func (x *NodeStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
} else {
x.Phase = NodePhase(r.DecodeString())
}
- yyj15++
- if yyhl15 {
- yyb15 = yyj15 > l
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
} else {
- yyb15 = r.CheckBreak()
+ yyb17 = r.CheckBreak()
}
- if yyb15 {
+ if yyb17 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -36924,21 +36970,21 @@ func (x *NodeStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.Conditions = nil
} else {
- yyv19 := &x.Conditions
- yym20 := z.DecBinary()
- _ = yym20
+ yyv21 := &x.Conditions
+ yym22 := z.DecBinary()
+ _ = yym22
if false {
} else {
- h.decSliceNodeCondition((*[]NodeCondition)(yyv19), d)
+ h.decSliceNodeCondition((*[]NodeCondition)(yyv21), d)
}
}
- yyj15++
- if yyhl15 {
- yyb15 = yyj15 > l
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
} else {
- yyb15 = r.CheckBreak()
+ yyb17 = r.CheckBreak()
}
- if yyb15 {
+ if yyb17 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -36946,21 +36992,21 @@ func (x *NodeStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.Addresses = nil
} else {
- yyv21 := &x.Addresses
- yym22 := z.DecBinary()
- _ = yym22
+ yyv23 := &x.Addresses
+ yym24 := z.DecBinary()
+ _ = yym24
if false {
} else {
- h.decSliceNodeAddress((*[]NodeAddress)(yyv21), d)
+ h.decSliceNodeAddress((*[]NodeAddress)(yyv23), d)
}
}
- yyj15++
- if yyhl15 {
- yyb15 = yyj15 > l
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
} else {
- yyb15 = r.CheckBreak()
+ yyb17 = r.CheckBreak()
}
- if yyb15 {
+ if yyb17 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -36968,16 +37014,16 @@ func (x *NodeStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.DaemonEndpoints = NodeDaemonEndpoints{}
} else {
- yyv23 := &x.DaemonEndpoints
- yyv23.CodecDecodeSelf(d)
+ yyv25 := &x.DaemonEndpoints
+ yyv25.CodecDecodeSelf(d)
}
- yyj15++
- if yyhl15 {
- yyb15 = yyj15 > l
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
} else {
- yyb15 = r.CheckBreak()
+ yyb17 = r.CheckBreak()
}
- if yyb15 {
+ if yyb17 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -36985,16 +37031,16 @@ func (x *NodeStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.NodeInfo = NodeSystemInfo{}
} else {
- yyv24 := &x.NodeInfo
- yyv24.CodecDecodeSelf(d)
+ yyv26 := &x.NodeInfo
+ yyv26.CodecDecodeSelf(d)
}
- yyj15++
- if yyhl15 {
- yyb15 = yyj15 > l
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
} else {
- yyb15 = r.CheckBreak()
+ yyb17 = r.CheckBreak()
}
- if yyb15 {
+ if yyb17 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -37002,30 +37048,78 @@ func (x *NodeStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.Images = nil
} else {
- yyv25 := &x.Images
- yym26 := z.DecBinary()
- _ = yym26
+ yyv27 := &x.Images
+ yym28 := z.DecBinary()
+ _ = yym28
if false {
} else {
- h.decSliceContainerImage((*[]ContainerImage)(yyv25), d)
+ h.decSliceContainerImage((*[]ContainerImage)(yyv27), d)
+ }
+ }
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
+ } else {
+ yyb17 = r.CheckBreak()
+ }
+ if yyb17 {
+ z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
+ return
+ }
+ z.DecSendContainerState(codecSelfer_containerArrayElem1234)
+ if r.TryDecodeAsNil() {
+ x.VolumesInUse = nil
+ } else {
+ yyv29 := &x.VolumesInUse
+ yym30 := z.DecBinary()
+ _ = yym30
+ if false {
+ } else {
+ h.decSliceUniqueDeviceName((*[]UniqueDeviceName)(yyv29), d)
}
}
for {
- yyj15++
- if yyhl15 {
- yyb15 = yyj15 > l
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
} else {
- yyb15 = r.CheckBreak()
+ yyb17 = r.CheckBreak()
}
- if yyb15 {
+ if yyb17 {
break
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
- z.DecStructFieldNotFound(yyj15-1, "")
+ z.DecStructFieldNotFound(yyj17-1, "")
}
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
}
+func (x UniqueDeviceName) CodecEncodeSelf(e *codec1978.Encoder) {
+ var h codecSelfer1234
+ z, r := codec1978.GenHelperEncoder(e)
+ _, _, _ = h, z, r
+ yym1 := z.EncBinary()
+ _ = yym1
+ if false {
+ } else if z.HasExtensions() && z.EncExt(x) {
+ } else {
+ r.EncodeString(codecSelferC_UTF81234, string(x))
+ }
+}
+
+func (x *UniqueDeviceName) CodecDecodeSelf(d *codec1978.Decoder) {
+ var h codecSelfer1234
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ yym1 := z.DecBinary()
+ _ = yym1
+ if false {
+ } else if z.HasExtensions() && z.DecExt(x) {
+ } else {
+ *((*string)(x)) = r.DecodeString()
+ }
+}
+
func (x *ContainerImage) CodecEncodeSelf(e *codec1978.Encoder) {
var h codecSelfer1234
z, r := codec1978.GenHelperEncoder(e)
@@ -57245,6 +57339,116 @@ func (x codecSelfer1234) decSliceContainerImage(v *[]ContainerImage, d *codec197
}
}
+func (x codecSelfer1234) encSliceUniqueDeviceName(v []UniqueDeviceName, e *codec1978.Encoder) {
+ var h codecSelfer1234
+ z, r := codec1978.GenHelperEncoder(e)
+ _, _, _ = h, z, r
+ r.EncodeArrayStart(len(v))
+ for _, yyv1 := range v {
+ z.EncSendContainerState(codecSelfer_containerArrayElem1234)
+ yyv1.CodecEncodeSelf(e)
+ }
+ z.EncSendContainerState(codecSelfer_containerArrayEnd1234)
+}
+
+func (x codecSelfer1234) decSliceUniqueDeviceName(v *[]UniqueDeviceName, d *codec1978.Decoder) {
+ var h codecSelfer1234
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+
+ yyv1 := *v
+ yyh1, yyl1 := z.DecSliceHelperStart()
+ var yyc1 bool
+ _ = yyc1
+ if yyl1 == 0 {
+ if yyv1 == nil {
+ yyv1 = []UniqueDeviceName{}
+ yyc1 = true
+ } else if len(yyv1) != 0 {
+ yyv1 = yyv1[:0]
+ yyc1 = true
+ }
+ } else if yyl1 > 0 {
+ var yyrr1, yyrl1 int
+ var yyrt1 bool
+ _, _ = yyrl1, yyrt1
+ yyrr1 = yyl1 // len(yyv1)
+ if yyl1 > cap(yyv1) {
+
+ yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 16)
+ if yyrt1 {
+ if yyrl1 <= cap(yyv1) {
+ yyv1 = yyv1[:yyrl1]
+ } else {
+ yyv1 = make([]UniqueDeviceName, yyrl1)
+ }
+ } else {
+ yyv1 = make([]UniqueDeviceName, yyrl1)
+ }
+ yyc1 = true
+ yyrr1 = len(yyv1)
+ } else if yyl1 != len(yyv1) {
+ yyv1 = yyv1[:yyl1]
+ yyc1 = true
+ }
+ yyj1 := 0
+ for ; yyj1 < yyrr1; yyj1++ {
+ yyh1.ElemContainerState(yyj1)
+ if r.TryDecodeAsNil() {
+ yyv1[yyj1] = ""
+ } else {
+ yyv1[yyj1] = UniqueDeviceName(r.DecodeString())
+ }
+
+ }
+ if yyrt1 {
+ for ; yyj1 < yyl1; yyj1++ {
+ yyv1 = append(yyv1, "")
+ yyh1.ElemContainerState(yyj1)
+ if r.TryDecodeAsNil() {
+ yyv1[yyj1] = ""
+ } else {
+ yyv1[yyj1] = UniqueDeviceName(r.DecodeString())
+ }
+
+ }
+ }
+
+ } else {
+ yyj1 := 0
+ for ; !r.CheckBreak(); yyj1++ {
+
+ if yyj1 >= len(yyv1) {
+ yyv1 = append(yyv1, "") // var yyz1 UniqueDeviceName
+ yyc1 = true
+ }
+ yyh1.ElemContainerState(yyj1)
+ if yyj1 < len(yyv1) {
+ if r.TryDecodeAsNil() {
+ yyv1[yyj1] = ""
+ } else {
+ yyv1[yyj1] = UniqueDeviceName(r.DecodeString())
+ }
+
+ } else {
+ z.DecSwallow()
+ }
+
+ }
+ if yyj1 < len(yyv1) {
+ yyv1 = yyv1[:yyj1]
+ yyc1 = true
+ } else if yyj1 == 0 && yyv1 == nil {
+ yyv1 = []UniqueDeviceName{}
+ yyc1 = true
+ }
+ }
+ yyh1.End()
+ if yyc1 {
+ *v = yyv1
+ }
+}
+
func (x codecSelfer1234) encResourceList(v ResourceList, e *codec1978.Encoder) {
var h codecSelfer1234
z, r := codec1978.GenHelperEncoder(e)
@@ -57398,7 +57602,7 @@ func (x codecSelfer1234) decSliceNode(v *[]Node, d *codec1978.Decoder) {
yyrg1 := len(yyv1) > 0
yyv21 := yyv1
- yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 568)
+ yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 592)
if yyrt1 {
if yyrl1 <= cap(yyv1) {
yyv1 = yyv1[:yyrl1]
diff --git a/pkg/api/types.go b/pkg/api/types.go
index 8a6288cc321..6a3aa5c4052 100644
--- a/pkg/api/types.go
+++ b/pkg/api/types.go
@@ -1983,8 +1983,12 @@ type NodeStatus struct {
NodeInfo NodeSystemInfo `json:"nodeInfo,omitempty"`
// List of container images on this node
Images []ContainerImage `json:"images,omitempty"`
+ // List of attachable volume devices in use (mounted) by the node.
+ VolumesInUse []UniqueDeviceName `json:"volumesInUse,omitempty"`
}
+type UniqueDeviceName string
+
// Describe a container image
type ContainerImage struct {
// Names by which this image is known.
diff --git a/pkg/api/v1/conversion_generated.go b/pkg/api/v1/conversion_generated.go
index 492d0d5d8ae..59fe6edc04f 100644
--- a/pkg/api/v1/conversion_generated.go
+++ b/pkg/api/v1/conversion_generated.go
@@ -3388,6 +3388,15 @@ func autoConvert_v1_NodeStatus_To_api_NodeStatus(in *NodeStatus, out *api.NodeSt
} else {
out.Images = nil
}
+ if in.VolumesInUse != nil {
+ in, out := &in.VolumesInUse, &out.VolumesInUse
+ *out = make([]api.UniqueDeviceName, len(*in))
+ for i := range *in {
+ (*out)[i] = api.UniqueDeviceName((*in)[i])
+ }
+ } else {
+ out.VolumesInUse = nil
+ }
return nil
}
@@ -3462,6 +3471,15 @@ func autoConvert_api_NodeStatus_To_v1_NodeStatus(in *api.NodeStatus, out *NodeSt
} else {
out.Images = nil
}
+ if in.VolumesInUse != nil {
+ in, out := &in.VolumesInUse, &out.VolumesInUse
+ *out = make([]UniqueDeviceName, len(*in))
+ for i := range *in {
+ (*out)[i] = UniqueDeviceName((*in)[i])
+ }
+ } else {
+ out.VolumesInUse = nil
+ }
return nil
}
diff --git a/pkg/api/v1/deep_copy_generated.go b/pkg/api/v1/deep_copy_generated.go
index 4018c58804b..9059bc8bd80 100644
--- a/pkg/api/v1/deep_copy_generated.go
+++ b/pkg/api/v1/deep_copy_generated.go
@@ -1548,6 +1548,15 @@ func DeepCopy_v1_NodeStatus(in NodeStatus, out *NodeStatus, c *conversion.Cloner
} else {
out.Images = nil
}
+ if in.VolumesInUse != nil {
+ in, out := in.VolumesInUse, &out.VolumesInUse
+ *out = make([]UniqueDeviceName, len(in))
+ for i := range in {
+ (*out)[i] = in[i]
+ }
+ } else {
+ out.VolumesInUse = nil
+ }
return nil
}
diff --git a/pkg/api/v1/generated.pb.go b/pkg/api/v1/generated.pb.go
index be91812aeaa..87eba24d4ed 100644
--- a/pkg/api/v1/generated.pb.go
+++ b/pkg/api/v1/generated.pb.go
@@ -4159,6 +4159,21 @@ func (m *NodeStatus) MarshalTo(data []byte) (int, error) {
i += n
}
}
+ if len(m.VolumesInUse) > 0 {
+ for _, s := range m.VolumesInUse {
+ data[i] = 0x4a
+ i++
+ l = len(s)
+ for l >= 1<<7 {
+ data[i] = uint8(uint64(l)&0x7f | 0x80)
+ l >>= 7
+ i++
+ }
+ data[i] = uint8(l)
+ i++
+ i += copy(data[i:], s)
+ }
+ }
return i, nil
}
@@ -8866,6 +8881,12 @@ func (m *NodeStatus) Size() (n int) {
n += 1 + l + sovGenerated(uint64(l))
}
}
+ if len(m.VolumesInUse) > 0 {
+ for _, s := range m.VolumesInUse {
+ l = len(s)
+ n += 1 + l + sovGenerated(uint64(l))
+ }
+ }
return n
}
@@ -21585,6 +21606,35 @@ func (m *NodeStatus) Unmarshal(data []byte) error {
return err
}
iNdEx = postIndex
+ case 9:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field VolumesInUse", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ stringLen |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.VolumesInUse = append(m.VolumesInUse, UniqueDeviceName(data[iNdEx:postIndex]))
+ iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(data[iNdEx:])
diff --git a/pkg/api/v1/generated.proto b/pkg/api/v1/generated.proto
index f65d77154bc..f049831837e 100644
--- a/pkg/api/v1/generated.proto
+++ b/pkg/api/v1/generated.proto
@@ -1303,6 +1303,9 @@ message NodeStatus {
// List of container images on this node
repeated ContainerImage images = 8;
+
+ // List of volumes in use (mounted) by the node.
+ repeated string volumesInUse = 9;
}
// NodeSystemInfo is a set of ids/uuids to uniquely identify the node.
diff --git a/pkg/api/v1/types.generated.go b/pkg/api/v1/types.generated.go
index 57ae2b3dde3..5ccb6999081 100644
--- a/pkg/api/v1/types.generated.go
+++ b/pkg/api/v1/types.generated.go
@@ -36302,7 +36302,7 @@ func (x *NodeStatus) CodecEncodeSelf(e *codec1978.Encoder) {
} else {
yysep2 := !z.EncBinary()
yy2arr2 := z.EncBasicHandle().StructToArray
- var yyq2 [8]bool
+ var yyq2 [9]bool
_, _, _ = yysep2, yyq2, yy2arr2
const yyr2 bool = false
yyq2[0] = len(x.Capacity) != 0
@@ -36313,9 +36313,10 @@ func (x *NodeStatus) CodecEncodeSelf(e *codec1978.Encoder) {
yyq2[5] = true
yyq2[6] = true
yyq2[7] = len(x.Images) != 0
+ yyq2[8] = len(x.VolumesInUse) != 0
var yynn2 int
if yyr2 || yy2arr2 {
- r.EncodeArrayStart(8)
+ r.EncodeArrayStart(9)
} else {
yynn2 = 0
for _, b := range yyq2 {
@@ -36520,6 +36521,39 @@ func (x *NodeStatus) CodecEncodeSelf(e *codec1978.Encoder) {
}
}
}
+ if yyr2 || yy2arr2 {
+ z.EncSendContainerState(codecSelfer_containerArrayElem1234)
+ if yyq2[8] {
+ if x.VolumesInUse == nil {
+ r.EncodeNil()
+ } else {
+ yym32 := z.EncBinary()
+ _ = yym32
+ if false {
+ } else {
+ h.encSliceUniqueDeviceName(([]UniqueDeviceName)(x.VolumesInUse), e)
+ }
+ }
+ } else {
+ r.EncodeNil()
+ }
+ } else {
+ if yyq2[8] {
+ z.EncSendContainerState(codecSelfer_containerMapKey1234)
+ r.EncodeString(codecSelferC_UTF81234, string("volumesInUse"))
+ z.EncSendContainerState(codecSelfer_containerMapValue1234)
+ if x.VolumesInUse == nil {
+ r.EncodeNil()
+ } else {
+ yym33 := z.EncBinary()
+ _ = yym33
+ if false {
+ } else {
+ h.encSliceUniqueDeviceName(([]UniqueDeviceName)(x.VolumesInUse), e)
+ }
+ }
+ }
+ }
if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayEnd1234)
} else {
@@ -36651,6 +36685,18 @@ func (x *NodeStatus) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
h.decSliceContainerImage((*[]ContainerImage)(yyv13), d)
}
}
+ case "volumesInUse":
+ if r.TryDecodeAsNil() {
+ x.VolumesInUse = nil
+ } else {
+ yyv15 := &x.VolumesInUse
+ yym16 := z.DecBinary()
+ _ = yym16
+ if false {
+ } else {
+ h.decSliceUniqueDeviceName((*[]UniqueDeviceName)(yyv15), d)
+ }
+ }
default:
z.DecStructFieldNotFound(-1, yys3)
} // end switch yys3
@@ -36662,16 +36708,16 @@ func (x *NodeStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
var h codecSelfer1234
z, r := codec1978.GenHelperDecoder(d)
_, _, _ = h, z, r
- var yyj15 int
- var yyb15 bool
- var yyhl15 bool = l >= 0
- yyj15++
- if yyhl15 {
- yyb15 = yyj15 > l
+ var yyj17 int
+ var yyb17 bool
+ var yyhl17 bool = l >= 0
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
} else {
- yyb15 = r.CheckBreak()
+ yyb17 = r.CheckBreak()
}
- if yyb15 {
+ if yyb17 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -36679,16 +36725,16 @@ func (x *NodeStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.Capacity = nil
} else {
- yyv16 := &x.Capacity
- yyv16.CodecDecodeSelf(d)
+ yyv18 := &x.Capacity
+ yyv18.CodecDecodeSelf(d)
}
- yyj15++
- if yyhl15 {
- yyb15 = yyj15 > l
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
} else {
- yyb15 = r.CheckBreak()
+ yyb17 = r.CheckBreak()
}
- if yyb15 {
+ if yyb17 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -36696,16 +36742,16 @@ func (x *NodeStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.Allocatable = nil
} else {
- yyv17 := &x.Allocatable
- yyv17.CodecDecodeSelf(d)
+ yyv19 := &x.Allocatable
+ yyv19.CodecDecodeSelf(d)
}
- yyj15++
- if yyhl15 {
- yyb15 = yyj15 > l
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
} else {
- yyb15 = r.CheckBreak()
+ yyb17 = r.CheckBreak()
}
- if yyb15 {
+ if yyb17 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -36715,13 +36761,13 @@ func (x *NodeStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
} else {
x.Phase = NodePhase(r.DecodeString())
}
- yyj15++
- if yyhl15 {
- yyb15 = yyj15 > l
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
} else {
- yyb15 = r.CheckBreak()
+ yyb17 = r.CheckBreak()
}
- if yyb15 {
+ if yyb17 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -36729,21 +36775,21 @@ func (x *NodeStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.Conditions = nil
} else {
- yyv19 := &x.Conditions
- yym20 := z.DecBinary()
- _ = yym20
+ yyv21 := &x.Conditions
+ yym22 := z.DecBinary()
+ _ = yym22
if false {
} else {
- h.decSliceNodeCondition((*[]NodeCondition)(yyv19), d)
+ h.decSliceNodeCondition((*[]NodeCondition)(yyv21), d)
}
}
- yyj15++
- if yyhl15 {
- yyb15 = yyj15 > l
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
} else {
- yyb15 = r.CheckBreak()
+ yyb17 = r.CheckBreak()
}
- if yyb15 {
+ if yyb17 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -36751,21 +36797,21 @@ func (x *NodeStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.Addresses = nil
} else {
- yyv21 := &x.Addresses
- yym22 := z.DecBinary()
- _ = yym22
+ yyv23 := &x.Addresses
+ yym24 := z.DecBinary()
+ _ = yym24
if false {
} else {
- h.decSliceNodeAddress((*[]NodeAddress)(yyv21), d)
+ h.decSliceNodeAddress((*[]NodeAddress)(yyv23), d)
}
}
- yyj15++
- if yyhl15 {
- yyb15 = yyj15 > l
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
} else {
- yyb15 = r.CheckBreak()
+ yyb17 = r.CheckBreak()
}
- if yyb15 {
+ if yyb17 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -36773,16 +36819,16 @@ func (x *NodeStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.DaemonEndpoints = NodeDaemonEndpoints{}
} else {
- yyv23 := &x.DaemonEndpoints
- yyv23.CodecDecodeSelf(d)
+ yyv25 := &x.DaemonEndpoints
+ yyv25.CodecDecodeSelf(d)
}
- yyj15++
- if yyhl15 {
- yyb15 = yyj15 > l
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
} else {
- yyb15 = r.CheckBreak()
+ yyb17 = r.CheckBreak()
}
- if yyb15 {
+ if yyb17 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -36790,16 +36836,16 @@ func (x *NodeStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.NodeInfo = NodeSystemInfo{}
} else {
- yyv24 := &x.NodeInfo
- yyv24.CodecDecodeSelf(d)
+ yyv26 := &x.NodeInfo
+ yyv26.CodecDecodeSelf(d)
}
- yyj15++
- if yyhl15 {
- yyb15 = yyj15 > l
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
} else {
- yyb15 = r.CheckBreak()
+ yyb17 = r.CheckBreak()
}
- if yyb15 {
+ if yyb17 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -36807,30 +36853,78 @@ func (x *NodeStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.Images = nil
} else {
- yyv25 := &x.Images
- yym26 := z.DecBinary()
- _ = yym26
+ yyv27 := &x.Images
+ yym28 := z.DecBinary()
+ _ = yym28
if false {
} else {
- h.decSliceContainerImage((*[]ContainerImage)(yyv25), d)
+ h.decSliceContainerImage((*[]ContainerImage)(yyv27), d)
+ }
+ }
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
+ } else {
+ yyb17 = r.CheckBreak()
+ }
+ if yyb17 {
+ z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
+ return
+ }
+ z.DecSendContainerState(codecSelfer_containerArrayElem1234)
+ if r.TryDecodeAsNil() {
+ x.VolumesInUse = nil
+ } else {
+ yyv29 := &x.VolumesInUse
+ yym30 := z.DecBinary()
+ _ = yym30
+ if false {
+ } else {
+ h.decSliceUniqueDeviceName((*[]UniqueDeviceName)(yyv29), d)
}
}
for {
- yyj15++
- if yyhl15 {
- yyb15 = yyj15 > l
+ yyj17++
+ if yyhl17 {
+ yyb17 = yyj17 > l
} else {
- yyb15 = r.CheckBreak()
+ yyb17 = r.CheckBreak()
}
- if yyb15 {
+ if yyb17 {
break
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
- z.DecStructFieldNotFound(yyj15-1, "")
+ z.DecStructFieldNotFound(yyj17-1, "")
}
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
}
+func (x UniqueDeviceName) CodecEncodeSelf(e *codec1978.Encoder) {
+ var h codecSelfer1234
+ z, r := codec1978.GenHelperEncoder(e)
+ _, _, _ = h, z, r
+ yym1 := z.EncBinary()
+ _ = yym1
+ if false {
+ } else if z.HasExtensions() && z.EncExt(x) {
+ } else {
+ r.EncodeString(codecSelferC_UTF81234, string(x))
+ }
+}
+
+func (x *UniqueDeviceName) CodecDecodeSelf(d *codec1978.Decoder) {
+ var h codecSelfer1234
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ yym1 := z.DecBinary()
+ _ = yym1
+ if false {
+ } else if z.HasExtensions() && z.DecExt(x) {
+ } else {
+ *((*string)(x)) = r.DecodeString()
+ }
+}
+
func (x *ContainerImage) CodecEncodeSelf(e *codec1978.Encoder) {
var h codecSelfer1234
z, r := codec1978.GenHelperEncoder(e)
@@ -57298,6 +57392,116 @@ func (x codecSelfer1234) decSliceContainerImage(v *[]ContainerImage, d *codec197
}
}
+func (x codecSelfer1234) encSliceUniqueDeviceName(v []UniqueDeviceName, e *codec1978.Encoder) {
+ var h codecSelfer1234
+ z, r := codec1978.GenHelperEncoder(e)
+ _, _, _ = h, z, r
+ r.EncodeArrayStart(len(v))
+ for _, yyv1 := range v {
+ z.EncSendContainerState(codecSelfer_containerArrayElem1234)
+ yyv1.CodecEncodeSelf(e)
+ }
+ z.EncSendContainerState(codecSelfer_containerArrayEnd1234)
+}
+
+func (x codecSelfer1234) decSliceUniqueDeviceName(v *[]UniqueDeviceName, d *codec1978.Decoder) {
+ var h codecSelfer1234
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+
+ yyv1 := *v
+ yyh1, yyl1 := z.DecSliceHelperStart()
+ var yyc1 bool
+ _ = yyc1
+ if yyl1 == 0 {
+ if yyv1 == nil {
+ yyv1 = []UniqueDeviceName{}
+ yyc1 = true
+ } else if len(yyv1) != 0 {
+ yyv1 = yyv1[:0]
+ yyc1 = true
+ }
+ } else if yyl1 > 0 {
+ var yyrr1, yyrl1 int
+ var yyrt1 bool
+ _, _ = yyrl1, yyrt1
+ yyrr1 = yyl1 // len(yyv1)
+ if yyl1 > cap(yyv1) {
+
+ yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 16)
+ if yyrt1 {
+ if yyrl1 <= cap(yyv1) {
+ yyv1 = yyv1[:yyrl1]
+ } else {
+ yyv1 = make([]UniqueDeviceName, yyrl1)
+ }
+ } else {
+ yyv1 = make([]UniqueDeviceName, yyrl1)
+ }
+ yyc1 = true
+ yyrr1 = len(yyv1)
+ } else if yyl1 != len(yyv1) {
+ yyv1 = yyv1[:yyl1]
+ yyc1 = true
+ }
+ yyj1 := 0
+ for ; yyj1 < yyrr1; yyj1++ {
+ yyh1.ElemContainerState(yyj1)
+ if r.TryDecodeAsNil() {
+ yyv1[yyj1] = ""
+ } else {
+ yyv1[yyj1] = UniqueDeviceName(r.DecodeString())
+ }
+
+ }
+ if yyrt1 {
+ for ; yyj1 < yyl1; yyj1++ {
+ yyv1 = append(yyv1, "")
+ yyh1.ElemContainerState(yyj1)
+ if r.TryDecodeAsNil() {
+ yyv1[yyj1] = ""
+ } else {
+ yyv1[yyj1] = UniqueDeviceName(r.DecodeString())
+ }
+
+ }
+ }
+
+ } else {
+ yyj1 := 0
+ for ; !r.CheckBreak(); yyj1++ {
+
+ if yyj1 >= len(yyv1) {
+ yyv1 = append(yyv1, "") // var yyz1 UniqueDeviceName
+ yyc1 = true
+ }
+ yyh1.ElemContainerState(yyj1)
+ if yyj1 < len(yyv1) {
+ if r.TryDecodeAsNil() {
+ yyv1[yyj1] = ""
+ } else {
+ yyv1[yyj1] = UniqueDeviceName(r.DecodeString())
+ }
+
+ } else {
+ z.DecSwallow()
+ }
+
+ }
+ if yyj1 < len(yyv1) {
+ yyv1 = yyv1[:yyj1]
+ yyc1 = true
+ } else if yyj1 == 0 && yyv1 == nil {
+ yyv1 = []UniqueDeviceName{}
+ yyc1 = true
+ }
+ }
+ yyh1.End()
+ if yyc1 {
+ *v = yyv1
+ }
+}
+
func (x codecSelfer1234) encResourceList(v ResourceList, e *codec1978.Encoder) {
var h codecSelfer1234
z, r := codec1978.GenHelperEncoder(e)
@@ -57451,7 +57655,7 @@ func (x codecSelfer1234) decSliceNode(v *[]Node, d *codec1978.Decoder) {
yyrg1 := len(yyv1) > 0
yyv21 := yyv1
- yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 568)
+ yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 592)
if yyrt1 {
if yyrl1 <= cap(yyv1) {
yyv1 = yyv1[:yyrl1]
diff --git a/pkg/api/v1/types.go b/pkg/api/v1/types.go
index 75be2f93c33..9aecfd56d6b 100644
--- a/pkg/api/v1/types.go
+++ b/pkg/api/v1/types.go
@@ -2386,8 +2386,12 @@ type NodeStatus struct {
NodeInfo NodeSystemInfo `json:"nodeInfo,omitempty" protobuf:"bytes,7,opt,name=nodeInfo"`
// List of container images on this node
Images []ContainerImage `json:"images,omitempty" protobuf:"bytes,8,rep,name=images"`
+ // List of volumes in use (mounted) by the node.
+ VolumesInUse []UniqueDeviceName `json:"volumesInUse,omitempty" protobuf:"bytes,9,rep,name=volumesInUse"`
}
+type UniqueDeviceName string
+
// Describe a container image
type ContainerImage struct {
// Names by which this image is known.
diff --git a/pkg/api/v1/types_swagger_doc_generated.go b/pkg/api/v1/types_swagger_doc_generated.go
index 4d28d9291ec..cf40307487d 100644
--- a/pkg/api/v1/types_swagger_doc_generated.go
+++ b/pkg/api/v1/types_swagger_doc_generated.go
@@ -880,6 +880,7 @@ var map_NodeStatus = map[string]string{
"daemonEndpoints": "Endpoints of daemons running on the Node.",
"nodeInfo": "Set of ids/uuids to uniquely identify the node. More info: http://releases.k8s.io/HEAD/docs/admin/node.md#node-info",
"images": "List of container images on this node",
+ "volumesInUse": "List of volumes in use (mounted) by the node.",
}
func (NodeStatus) SwaggerDoc() map[string]string {
diff --git a/pkg/apis/componentconfig/deep_copy_generated.go b/pkg/apis/componentconfig/deep_copy_generated.go
index ee6514fcf05..3a40ca43a02 100644
--- a/pkg/apis/componentconfig/deep_copy_generated.go
+++ b/pkg/apis/componentconfig/deep_copy_generated.go
@@ -319,6 +319,7 @@ func DeepCopy_componentconfig_KubeletConfiguration(in KubeletConfiguration, out
}
out.EvictionMaxPodGracePeriod = in.EvictionMaxPodGracePeriod
out.PodsPerCore = in.PodsPerCore
+ out.EnableControllerAttachDetach = in.EnableControllerAttachDetach
return nil
}
diff --git a/pkg/apis/componentconfig/types.generated.go b/pkg/apis/componentconfig/types.generated.go
index 53a6c1d36af..68d7813b629 100644
--- a/pkg/apis/componentconfig/types.generated.go
+++ b/pkg/apis/componentconfig/types.generated.go
@@ -1175,7 +1175,7 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
} else {
yysep2 := !z.EncBinary()
yy2arr2 := z.EncBasicHandle().StructToArray
- var yyq2 [89]bool
+ var yyq2 [90]bool
_, _, _ = yysep2, yyq2, yy2arr2
const yyr2 bool = false
yyq2[48] = x.CloudProvider != ""
@@ -1196,9 +1196,9 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
yyq2[87] = x.EvictionMaxPodGracePeriod != 0
var yynn2 int
if yyr2 || yy2arr2 {
- r.EncodeArrayStart(89)
+ r.EncodeArrayStart(90)
} else {
- yynn2 = 73
+ yynn2 = 74
for _, b := range yyq2 {
if b {
yynn2++
@@ -3082,6 +3082,25 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
r.EncodeInt(int64(x.PodsPerCore))
}
}
+ if yyr2 || yy2arr2 {
+ z.EncSendContainerState(codecSelfer_containerArrayElem1234)
+ yym291 := z.EncBinary()
+ _ = yym291
+ if false {
+ } else {
+ r.EncodeBool(bool(x.EnableControllerAttachDetach))
+ }
+ } else {
+ z.EncSendContainerState(codecSelfer_containerMapKey1234)
+ r.EncodeString(codecSelferC_UTF81234, string("enableControllerAttachDetach"))
+ z.EncSendContainerState(codecSelfer_containerMapValue1234)
+ yym292 := z.EncBinary()
+ _ = yym292
+ if false {
+ } else {
+ r.EncodeBool(bool(x.EnableControllerAttachDetach))
+ }
+ }
if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayEnd1234)
} else {
@@ -3773,6 +3792,12 @@ func (x *KubeletConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.Decode
} else {
x.PodsPerCore = int32(r.DecodeInt(32))
}
+ case "enableControllerAttachDetach":
+ if r.TryDecodeAsNil() {
+ x.EnableControllerAttachDetach = false
+ } else {
+ x.EnableControllerAttachDetach = bool(r.DecodeBool())
+ }
default:
z.DecStructFieldNotFound(-1, yys3)
} // end switch yys3
@@ -3784,16 +3809,16 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
var h codecSelfer1234
z, r := codec1978.GenHelperDecoder(d)
_, _, _ = h, z, r
- var yyj104 int
- var yyb104 bool
- var yyhl104 bool = l >= 0
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ var yyj105 int
+ var yyb105 bool
+ var yyhl105 bool = l >= 0
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -3803,13 +3828,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.Config = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -3817,24 +3842,24 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
if r.TryDecodeAsNil() {
x.SyncFrequency = pkg1_unversioned.Duration{}
} else {
- yyv106 := &x.SyncFrequency
- yym107 := z.DecBinary()
- _ = yym107
+ yyv107 := &x.SyncFrequency
+ yym108 := z.DecBinary()
+ _ = yym108
if false {
- } else if z.HasExtensions() && z.DecExt(yyv106) {
- } else if !yym107 && z.IsJSONHandle() {
- z.DecJSONUnmarshal(yyv106)
+ } else if z.HasExtensions() && z.DecExt(yyv107) {
+ } else if !yym108 && z.IsJSONHandle() {
+ z.DecJSONUnmarshal(yyv107)
} else {
- z.DecFallback(yyv106, false)
+ z.DecFallback(yyv107, false)
}
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -3842,24 +3867,24 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
if r.TryDecodeAsNil() {
x.FileCheckFrequency = pkg1_unversioned.Duration{}
} else {
- yyv108 := &x.FileCheckFrequency
- yym109 := z.DecBinary()
- _ = yym109
+ yyv109 := &x.FileCheckFrequency
+ yym110 := z.DecBinary()
+ _ = yym110
if false {
- } else if z.HasExtensions() && z.DecExt(yyv108) {
- } else if !yym109 && z.IsJSONHandle() {
- z.DecJSONUnmarshal(yyv108)
+ } else if z.HasExtensions() && z.DecExt(yyv109) {
+ } else if !yym110 && z.IsJSONHandle() {
+ z.DecJSONUnmarshal(yyv109)
} else {
- z.DecFallback(yyv108, false)
+ z.DecFallback(yyv109, false)
}
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -3867,24 +3892,24 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
if r.TryDecodeAsNil() {
x.HTTPCheckFrequency = pkg1_unversioned.Duration{}
} else {
- yyv110 := &x.HTTPCheckFrequency
- yym111 := z.DecBinary()
- _ = yym111
+ yyv111 := &x.HTTPCheckFrequency
+ yym112 := z.DecBinary()
+ _ = yym112
if false {
- } else if z.HasExtensions() && z.DecExt(yyv110) {
- } else if !yym111 && z.IsJSONHandle() {
- z.DecJSONUnmarshal(yyv110)
+ } else if z.HasExtensions() && z.DecExt(yyv111) {
+ } else if !yym112 && z.IsJSONHandle() {
+ z.DecJSONUnmarshal(yyv111)
} else {
- z.DecFallback(yyv110, false)
+ z.DecFallback(yyv111, false)
}
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -3894,13 +3919,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.ManifestURL = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -3910,13 +3935,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.ManifestURLHeader = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -3926,13 +3951,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.EnableServer = bool(r.DecodeBool())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -3942,13 +3967,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.Address = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -3958,13 +3983,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.Port = uint(r.DecodeUint(codecSelferBitsize1234))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -3974,13 +3999,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.ReadOnlyPort = uint(r.DecodeUint(codecSelferBitsize1234))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -3990,13 +4015,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.TLSCertFile = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4006,13 +4031,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.TLSPrivateKeyFile = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4022,13 +4047,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.CertDirectory = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4038,13 +4063,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.HostnameOverride = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4054,13 +4079,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.PodInfraContainerImage = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4070,13 +4095,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.DockerEndpoint = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4086,13 +4111,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.RootDirectory = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4102,13 +4127,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.SeccompProfileRoot = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4118,13 +4143,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.AllowPrivileged = bool(r.DecodeBool())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4134,13 +4159,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.HostNetworkSources = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4150,13 +4175,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.HostPIDSources = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4166,13 +4191,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.HostIPCSources = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4182,13 +4207,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.RegistryPullQPS = float64(r.DecodeFloat(false))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4198,13 +4223,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.RegistryBurst = int32(r.DecodeInt(32))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4214,13 +4239,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.EventRecordQPS = float32(r.DecodeFloat(true))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4230,13 +4255,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.EventBurst = int32(r.DecodeInt(32))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4246,13 +4271,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.EnableDebuggingHandlers = bool(r.DecodeBool())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4260,24 +4285,24 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
if r.TryDecodeAsNil() {
x.MinimumGCAge = pkg1_unversioned.Duration{}
} else {
- yyv135 := &x.MinimumGCAge
- yym136 := z.DecBinary()
- _ = yym136
+ yyv136 := &x.MinimumGCAge
+ yym137 := z.DecBinary()
+ _ = yym137
if false {
- } else if z.HasExtensions() && z.DecExt(yyv135) {
- } else if !yym136 && z.IsJSONHandle() {
- z.DecJSONUnmarshal(yyv135)
+ } else if z.HasExtensions() && z.DecExt(yyv136) {
+ } else if !yym137 && z.IsJSONHandle() {
+ z.DecJSONUnmarshal(yyv136)
} else {
- z.DecFallback(yyv135, false)
+ z.DecFallback(yyv136, false)
}
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4287,13 +4312,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.MaxPerPodContainerCount = int32(r.DecodeInt(32))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4303,13 +4328,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.MaxContainerCount = int32(r.DecodeInt(32))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4319,13 +4344,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.CAdvisorPort = uint(r.DecodeUint(codecSelferBitsize1234))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4335,13 +4360,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.HealthzPort = int32(r.DecodeInt(32))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4351,13 +4376,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.HealthzBindAddress = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4367,13 +4392,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.OOMScoreAdj = int32(r.DecodeInt(32))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4383,13 +4408,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.RegisterNode = bool(r.DecodeBool())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4399,13 +4424,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.ClusterDomain = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4415,13 +4440,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.MasterServiceNamespace = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4431,13 +4456,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.ClusterDNS = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4445,24 +4470,24 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
if r.TryDecodeAsNil() {
x.StreamingConnectionIdleTimeout = pkg1_unversioned.Duration{}
} else {
- yyv147 := &x.StreamingConnectionIdleTimeout
- yym148 := z.DecBinary()
- _ = yym148
+ yyv148 := &x.StreamingConnectionIdleTimeout
+ yym149 := z.DecBinary()
+ _ = yym149
if false {
- } else if z.HasExtensions() && z.DecExt(yyv147) {
- } else if !yym148 && z.IsJSONHandle() {
- z.DecJSONUnmarshal(yyv147)
+ } else if z.HasExtensions() && z.DecExt(yyv148) {
+ } else if !yym149 && z.IsJSONHandle() {
+ z.DecJSONUnmarshal(yyv148)
} else {
- z.DecFallback(yyv147, false)
+ z.DecFallback(yyv148, false)
}
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4470,24 +4495,24 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
if r.TryDecodeAsNil() {
x.NodeStatusUpdateFrequency = pkg1_unversioned.Duration{}
} else {
- yyv149 := &x.NodeStatusUpdateFrequency
- yym150 := z.DecBinary()
- _ = yym150
+ yyv150 := &x.NodeStatusUpdateFrequency
+ yym151 := z.DecBinary()
+ _ = yym151
if false {
- } else if z.HasExtensions() && z.DecExt(yyv149) {
- } else if !yym150 && z.IsJSONHandle() {
- z.DecJSONUnmarshal(yyv149)
+ } else if z.HasExtensions() && z.DecExt(yyv150) {
+ } else if !yym151 && z.IsJSONHandle() {
+ z.DecJSONUnmarshal(yyv150)
} else {
- z.DecFallback(yyv149, false)
+ z.DecFallback(yyv150, false)
}
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4495,24 +4520,24 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
if r.TryDecodeAsNil() {
x.ImageMinimumGCAge = pkg1_unversioned.Duration{}
} else {
- yyv151 := &x.ImageMinimumGCAge
- yym152 := z.DecBinary()
- _ = yym152
+ yyv152 := &x.ImageMinimumGCAge
+ yym153 := z.DecBinary()
+ _ = yym153
if false {
- } else if z.HasExtensions() && z.DecExt(yyv151) {
- } else if !yym152 && z.IsJSONHandle() {
- z.DecJSONUnmarshal(yyv151)
+ } else if z.HasExtensions() && z.DecExt(yyv152) {
+ } else if !yym153 && z.IsJSONHandle() {
+ z.DecJSONUnmarshal(yyv152)
} else {
- z.DecFallback(yyv151, false)
+ z.DecFallback(yyv152, false)
}
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4522,13 +4547,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.ImageGCHighThresholdPercent = int32(r.DecodeInt(32))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4538,13 +4563,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.ImageGCLowThresholdPercent = int32(r.DecodeInt(32))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4554,13 +4579,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.LowDiskSpaceThresholdMB = int32(r.DecodeInt(32))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4568,24 +4593,24 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
if r.TryDecodeAsNil() {
x.VolumeStatsAggPeriod = pkg1_unversioned.Duration{}
} else {
- yyv156 := &x.VolumeStatsAggPeriod
- yym157 := z.DecBinary()
- _ = yym157
+ yyv157 := &x.VolumeStatsAggPeriod
+ yym158 := z.DecBinary()
+ _ = yym158
if false {
- } else if z.HasExtensions() && z.DecExt(yyv156) {
- } else if !yym157 && z.IsJSONHandle() {
- z.DecJSONUnmarshal(yyv156)
+ } else if z.HasExtensions() && z.DecExt(yyv157) {
+ } else if !yym158 && z.IsJSONHandle() {
+ z.DecJSONUnmarshal(yyv157)
} else {
- z.DecFallback(yyv156, false)
+ z.DecFallback(yyv157, false)
}
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4595,13 +4620,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.NetworkPluginName = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4611,13 +4636,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.NetworkPluginDir = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4627,13 +4652,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.VolumePluginDir = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4643,13 +4668,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.CloudProvider = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4659,13 +4684,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.CloudConfigFile = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4675,13 +4700,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.KubeletCgroups = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4691,13 +4716,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.RuntimeCgroups = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4707,13 +4732,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.SystemCgroups = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4723,13 +4748,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.CgroupRoot = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4739,13 +4764,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.ContainerRuntime = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4755,13 +4780,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.RktPath = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4771,13 +4796,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.RktAPIEndpoint = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4787,13 +4812,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.RktStage1Image = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4803,13 +4828,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.LockFilePath = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4819,13 +4844,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.ExitOnLockContention = bool(r.DecodeBool())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4835,13 +4860,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.ConfigureCBR0 = bool(r.DecodeBool())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4851,13 +4876,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.HairpinMode = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4867,13 +4892,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.BabysitDaemons = bool(r.DecodeBool())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4883,13 +4908,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.MaxPods = int32(r.DecodeInt(32))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4899,13 +4924,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.NvidiaGPUs = int32(r.DecodeInt(32))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4915,13 +4940,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.DockerExecHandlerName = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4931,13 +4956,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.PodCIDR = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4947,13 +4972,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.ResolverConfig = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4963,13 +4988,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.CPUCFSQuota = bool(r.DecodeBool())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4979,13 +5004,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.Containerized = bool(r.DecodeBool())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -4995,13 +5020,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.MaxOpenFiles = uint64(r.DecodeUint(64))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -5011,13 +5036,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.ReconcileCIDR = bool(r.DecodeBool())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -5027,13 +5052,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.RegisterSchedulable = bool(r.DecodeBool())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -5043,13 +5068,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.ContentType = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -5059,13 +5084,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.KubeAPIQPS = float32(r.DecodeFloat(true))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -5075,13 +5100,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.KubeAPIBurst = int32(r.DecodeInt(32))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -5091,13 +5116,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.SerializeImagePulls = bool(r.DecodeBool())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -5107,13 +5132,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.ExperimentalFlannelOverlay = bool(r.DecodeBool())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -5121,24 +5146,24 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
if r.TryDecodeAsNil() {
x.OutOfDiskTransitionFrequency = pkg1_unversioned.Duration{}
} else {
- yyv191 := &x.OutOfDiskTransitionFrequency
- yym192 := z.DecBinary()
- _ = yym192
+ yyv192 := &x.OutOfDiskTransitionFrequency
+ yym193 := z.DecBinary()
+ _ = yym193
if false {
- } else if z.HasExtensions() && z.DecExt(yyv191) {
- } else if !yym192 && z.IsJSONHandle() {
- z.DecJSONUnmarshal(yyv191)
+ } else if z.HasExtensions() && z.DecExt(yyv192) {
+ } else if !yym193 && z.IsJSONHandle() {
+ z.DecJSONUnmarshal(yyv192)
} else {
- z.DecFallback(yyv191, false)
+ z.DecFallback(yyv192, false)
}
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -5148,13 +5173,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.NodeIP = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -5162,21 +5187,21 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
if r.TryDecodeAsNil() {
x.NodeLabels = nil
} else {
- yyv194 := &x.NodeLabels
- yym195 := z.DecBinary()
- _ = yym195
+ yyv195 := &x.NodeLabels
+ yym196 := z.DecBinary()
+ _ = yym196
if false {
} else {
- z.F.DecMapStringStringX(yyv194, false, d)
+ z.F.DecMapStringStringX(yyv195, false, d)
}
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -5186,13 +5211,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.NonMasqueradeCIDR = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -5202,13 +5227,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.EnableCustomMetrics = bool(r.DecodeBool())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -5218,13 +5243,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.EvictionHard = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -5234,13 +5259,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.EvictionSoft = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -5250,13 +5275,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.EvictionSoftGracePeriod = string(r.DecodeString())
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -5264,24 +5289,24 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
if r.TryDecodeAsNil() {
x.EvictionPressureTransitionPeriod = pkg1_unversioned.Duration{}
} else {
- yyv201 := &x.EvictionPressureTransitionPeriod
- yym202 := z.DecBinary()
- _ = yym202
+ yyv202 := &x.EvictionPressureTransitionPeriod
+ yym203 := z.DecBinary()
+ _ = yym203
if false {
- } else if z.HasExtensions() && z.DecExt(yyv201) {
- } else if !yym202 && z.IsJSONHandle() {
- z.DecJSONUnmarshal(yyv201)
+ } else if z.HasExtensions() && z.DecExt(yyv202) {
+ } else if !yym203 && z.IsJSONHandle() {
+ z.DecJSONUnmarshal(yyv202)
} else {
- z.DecFallback(yyv201, false)
+ z.DecFallback(yyv202, false)
}
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -5291,13 +5316,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.EvictionMaxPodGracePeriod = int32(r.DecodeInt(32))
}
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@@ -5307,18 +5332,34 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco
} else {
x.PodsPerCore = int32(r.DecodeInt(32))
}
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
+ } else {
+ yyb105 = r.CheckBreak()
+ }
+ if yyb105 {
+ z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
+ return
+ }
+ z.DecSendContainerState(codecSelfer_containerArrayElem1234)
+ if r.TryDecodeAsNil() {
+ x.EnableControllerAttachDetach = false
+ } else {
+ x.EnableControllerAttachDetach = bool(r.DecodeBool())
+ }
for {
- yyj104++
- if yyhl104 {
- yyb104 = yyj104 > l
+ yyj105++
+ if yyhl105 {
+ yyb105 = yyj105 > l
} else {
- yyb104 = r.CheckBreak()
+ yyb105 = r.CheckBreak()
}
- if yyb104 {
+ if yyb105 {
break
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
- z.DecStructFieldNotFound(yyj104-1, "")
+ z.DecStructFieldNotFound(yyj105-1, "")
}
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
}
diff --git a/pkg/apis/componentconfig/types.go b/pkg/apis/componentconfig/types.go
index 3d2b7c769cb..79619687512 100644
--- a/pkg/apis/componentconfig/types.go
+++ b/pkg/apis/componentconfig/types.go
@@ -362,6 +362,10 @@ type KubeletConfiguration struct {
EvictionMaxPodGracePeriod int32 `json:"evictionMaxPodGracePeriod,omitempty"`
// Maximum number of pods per core. Cannot exceed MaxPods
PodsPerCore int32 `json:"podsPerCore"`
+ // enableControllerAttachDetach enables the Attach/Detach controller to
+ // manage attachment/detachment of volumes scheduled to this node, and
+ // disables kubelet from executing any attach/detach operations
+ EnableControllerAttachDetach bool `json:"enableControllerAttachDetach"`
}
type KubeSchedulerConfiguration struct {
diff --git a/pkg/controller/volume/attach_detach_controller.go b/pkg/controller/volume/attach_detach_controller.go
index 234ac8044c9..ab4fd0a1eb4 100644
--- a/pkg/controller/volume/attach_detach_controller.go
+++ b/pkg/controller/volume/attach_detach_controller.go
@@ -20,7 +20,6 @@ package volume
import (
"fmt"
- "strings"
"time"
"github.com/golang/glog"
@@ -36,28 +35,19 @@ import (
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/util/runtime"
"k8s.io/kubernetes/pkg/volume"
+ "k8s.io/kubernetes/pkg/volume/util/attachdetach"
)
const (
- // ControllerManagedAnnotation is the key of the annotation on Node objects
- // that indicates attach/detach operations for the node should be managed
- // by the attach/detach controller
- ControllerManagedAnnotation string = "volumes.kubernetes.io/controller-managed-attach"
-
- // SafeToDetachAnnotation is the annotation added to the Node object by
- // kubelet in the format "volumes.kubernetes.io/safetodetach/{volumename}"
- // to indicate the volume has been unmounted and is safe to detach.
- SafeToDetachAnnotation string = "volumes.kubernetes.io/safetodetach-"
-
// loopPeriod is the ammount of time the reconciler loop waits between
// successive executions
reconcilerLoopPeriod time.Duration = 100 * time.Millisecond
- // reconcilerMaxSafeToDetachDuration is the maximum amount of time the
- // attach detach controller will wait for a volume to be safely detached
+ // reconcilerMaxWaitForUnmountDuration is the maximum amount of time the
+ // attach detach controller will wait for a volume to be safely unmounted
// from its node. Once this time has expired, the controller will assume the
// node or kubelet are unresponsive and will detach the volume anyway.
- reconcilerMaxSafeToDetachDuration time.Duration = 10 * time.Minute
+ reconcilerMaxWaitForUnmountDuration time.Duration = 3 * time.Minute
)
// AttachDetachController defines the operations supported by this controller.
@@ -116,7 +106,7 @@ func NewAttachDetachController(
adc.attacherDetacher = attacherdetacher.NewAttacherDetacher(&adc.volumePluginMgr)
adc.reconciler = reconciler.NewReconciler(
reconcilerLoopPeriod,
- reconcilerMaxSafeToDetachDuration,
+ reconcilerMaxWaitForUnmountDuration,
adc.desiredStateOfWorld,
adc.actualStateOfWorld,
adc.attacherDetacher)
@@ -215,13 +205,13 @@ func (adc *attachDetachController) nodeAdd(obj interface{}) {
}
nodeName := node.Name
- if _, exists := node.Annotations[ControllerManagedAnnotation]; exists {
+ if _, exists := node.Annotations[attachdetach.ControllerManagedAnnotation]; exists {
// Node specifies annotation indicating it should be managed by attach
// detach controller. Add it to desired state of world.
adc.desiredStateOfWorld.AddNode(nodeName)
}
- adc.processSafeToDetachAnnotations(nodeName, node.Annotations)
+ adc.processVolumesInUse(nodeName, node.Status.VolumesInUse)
}
func (adc *attachDetachController) nodeUpdate(oldObj, newObj interface{}) {
@@ -240,7 +230,7 @@ func (adc *attachDetachController) nodeDelete(obj interface{}) {
glog.V(10).Infof("%v", err)
}
- adc.processSafeToDetachAnnotations(nodeName, node.Annotations)
+ adc.processVolumesInUse(nodeName, node.Status.VolumesInUse)
}
// processPodVolumes processes the volumes in the given pod and adds them to the
@@ -309,10 +299,11 @@ func (adc *attachDetachController) processPodVolumes(
} else {
// Remove volume from desired state of world
- uniqueVolumeName, err := attachableVolumePlugin.GetUniqueVolumeName(volumeSpec)
+ uniqueVolumeName, err := attachdetach.GetUniqueDeviceNameFromSpec(
+ attachableVolumePlugin, volumeSpec)
if err != nil {
glog.V(10).Infof(
- "Failed to delete volume %q for pod %q/%q from desiredStateOfWorld. GetUniqueVolumeName failed with %v",
+ "Failed to delete volume %q for pod %q/%q from desiredStateOfWorld. GenerateUniqueDeviceName failed with %v",
podVolume.Name,
pod.Namespace,
pod.Name,
@@ -332,20 +323,48 @@ func (adc *attachDetachController) processPodVolumes(
func (adc *attachDetachController) createVolumeSpec(
podVolume api.Volume, podNamespace string) (*volume.Spec, error) {
if pvcSource := podVolume.VolumeSource.PersistentVolumeClaim; pvcSource != nil {
+ glog.V(10).Infof(
+ "Found PVC, ClaimName: %q/%q",
+ podNamespace,
+ pvcSource.ClaimName)
+
// If podVolume is a PVC, fetch the real PV behind the claim
pvName, pvcUID, err := adc.getPVCFromCacheExtractPV(
podNamespace, pvcSource.ClaimName)
if err != nil {
- return nil, fmt.Errorf("error processing PVC %q: %v", pvcSource.ClaimName, err)
+ return nil, fmt.Errorf(
+ "error processing PVC %q/%q: %v",
+ podNamespace,
+ pvcSource.ClaimName,
+ err)
}
+ glog.V(10).Infof(
+ "Found bound PV for PVC (ClaimName %q/%q pvcUID %v): pvName=%q",
+ podNamespace,
+ pvcSource.ClaimName,
+ pvcUID,
+ pvName)
+
// Fetch actual PV object
volumeSpec, err := adc.getPVSpecFromCache(
pvName, pvcSource.ReadOnly, pvcUID)
if err != nil {
- return nil, fmt.Errorf("error processing PVC %q: %v", pvcSource.ClaimName, err)
+ return nil, fmt.Errorf(
+ "error processing PVC %q/%q: %v",
+ podNamespace,
+ pvcSource.ClaimName,
+ err)
}
+ glog.V(10).Infof(
+ "Extracted volumeSpec (%v) from bound PV (pvName %q) and PVC (ClaimName %q/%q pvcUID %v)",
+ volumeSpec.Name,
+ pvName,
+ podNamespace,
+ pvcSource.ClaimName,
+ pvcUID)
+
return volumeSpec, nil
}
@@ -353,7 +372,8 @@ func (adc *attachDetachController) createVolumeSpec(
// informer it may be mutated by another consumer.
clonedPodVolumeObj, err := api.Scheme.DeepCopy(podVolume)
if err != nil || clonedPodVolumeObj == nil {
- return nil, fmt.Errorf("failed to deep copy %q volume object", podVolume.Name)
+ return nil, fmt.Errorf(
+ "failed to deep copy %q volume object. err=%v", podVolume.Name, err)
}
clonedPodVolume, ok := clonedPodVolumeObj.(api.Volume)
@@ -377,7 +397,7 @@ func (adc *attachDetachController) getPVCFromCacheExtractPV(
key = namespace + "/" + name
}
- pvcObj, exists, err := adc.pvcInformer.GetStore().Get(key)
+ pvcObj, exists, err := adc.pvcInformer.GetStore().GetByKey(key)
if pvcObj == nil || !exists || err != nil {
return "", "", fmt.Errorf(
"failed to find PVC %q in PVCInformer cache. %v",
@@ -386,7 +406,7 @@ func (adc *attachDetachController) getPVCFromCacheExtractPV(
}
pvc, ok := pvcObj.(*api.PersistentVolumeClaim)
- if ok || pvc == nil {
+ if !ok || pvc == nil {
return "", "", fmt.Errorf(
"failed to cast %q object %#v to PersistentVolumeClaim",
key,
@@ -414,14 +434,14 @@ func (adc *attachDetachController) getPVSpecFromCache(
name string,
pvcReadOnly bool,
expectedClaimUID types.UID) (*volume.Spec, error) {
- pvObj, exists, err := adc.pvInformer.GetStore().Get(name)
+ pvObj, exists, err := adc.pvInformer.GetStore().GetByKey(name)
if pvObj == nil || !exists || err != nil {
return nil, fmt.Errorf(
"failed to find PV %q in PVInformer cache. %v", name, err)
}
pv, ok := pvObj.(*api.PersistentVolume)
- if ok || pv == nil {
+ if !ok || pv == nil {
return nil, fmt.Errorf(
"failed to cast %q object %#v to PersistentVolume", name, pvObj)
}
@@ -442,9 +462,10 @@ func (adc *attachDetachController) getPVSpecFromCache(
// Do not return the object from the informer, since the store is shared it
// may be mutated by another consumer.
- clonedPVObj, err := api.Scheme.DeepCopy(pv)
+ clonedPVObj, err := api.Scheme.DeepCopy(*pv)
if err != nil || clonedPVObj == nil {
- return nil, fmt.Errorf("failed to deep copy %q PV object", name)
+ return nil, fmt.Errorf(
+ "failed to deep copy %q PV object. err=%v", name, err)
}
clonedPV, ok := clonedPVObj.(api.PersistentVolume)
@@ -456,28 +477,28 @@ func (adc *attachDetachController) getPVSpecFromCache(
return volume.NewSpecFromPersistentVolume(&clonedPV, pvcReadOnly), nil
}
-// processSafeToDetachAnnotations processes the "safe to detach" annotations for
-// the given node. It makes calls to delete any annotations referring to volumes
-// it is not aware of. For volumes it is aware of, it marks them safe to detach
-// in the "actual state of world" data structure.
-func (adc *attachDetachController) processSafeToDetachAnnotations(
- nodeName string, annotations map[string]string) {
- var annotationsToRemove []string
- for annotation := range annotations {
- // Check annotations for "safe to detach" volumes
- annotation = strings.ToLower(annotation)
- if strings.HasPrefix(annotation, SafeToDetachAnnotation) {
- // If volume exists in "actual state of world" mark it as safe to detach
- safeToAttachVolume := strings.TrimPrefix(annotation, SafeToDetachAnnotation)
- if err := adc.actualStateOfWorld.MarkVolumeNodeSafeToDetach(safeToAttachVolume, nodeName); err != nil {
- // If volume doesn't exist in "actual state of world" remove
- // the "safe to detach" annotation from the node
- annotationsToRemove = append(annotationsToRemove, annotation)
+// processVolumesInUse processes the list of volumes marked as "in-use"
+// according to the specified Node's Status.VolumesInUse and updates the
+// corresponding volume in the actual state of the world to indicate that it is
+// mounted.
+func (adc *attachDetachController) processVolumesInUse(
+ nodeName string, volumesInUse []api.UniqueDeviceName) {
+ for _, attachedVolume := range adc.actualStateOfWorld.GetAttachedVolumesForNode(nodeName) {
+ mounted := false
+ for _, volumeInUse := range volumesInUse {
+ if attachedVolume.VolumeName == volumeInUse {
+ mounted = true
+ break
}
}
+ err := adc.actualStateOfWorld.SetVolumeMountedByNode(
+ attachedVolume.VolumeName, nodeName, mounted)
+ if err != nil {
+ glog.Warningf(
+ "SetVolumeMountedByNode(%q, %q, %q) returned an error: %v",
+ attachedVolume.VolumeName, nodeName, mounted, err)
+ }
}
-
- // TODO: Call out to API server to delete annotationsToRemove from Node
}
// getUniquePodName returns a unique name to reference pod by in memory caches
diff --git a/pkg/controller/volume/attacherdetacher/attacher_detacher.go b/pkg/controller/volume/attacherdetacher/attacher_detacher.go
index fb7dd54f464..b6b19cf0a67 100644
--- a/pkg/controller/volume/attacherdetacher/attacher_detacher.go
+++ b/pkg/controller/volume/attacherdetacher/attacher_detacher.go
@@ -40,7 +40,7 @@ type AttacherDetacher interface {
// responsible for implmenting this behavior).
// All other errors are logged and the goroutine terminates without updating
// actualStateOfWorld (caller is responsible for retrying as needed).
- AttachVolume(volumeToAttach *cache.VolumeToAttach, actualStateOfWorld cache.ActualStateOfWorld) error
+ AttachVolume(volumeToAttach cache.VolumeToAttach, actualStateOfWorld cache.ActualStateOfWorld) error
// Spawns a new goroutine to execute volume-specific logic to detach the
// volume from the node specified in volumeToDetach.
@@ -51,7 +51,7 @@ type AttacherDetacher interface {
// responsible for implmenting this behavior).
// All other errors are logged and the goroutine terminates without updating
// actualStateOfWorld (caller is responsible for retrying as needed).
- DetachVolume(volumeToDetach *cache.AttachedVolume, actualStateOfWorld cache.ActualStateOfWorld) error
+ DetachVolume(volumeToDetach cache.AttachedVolume, actualStateOfWorld cache.ActualStateOfWorld) error
}
// NewAttacherDetacher returns a new instance of AttacherDetacher.
@@ -72,29 +72,29 @@ type attacherDetacher struct {
}
func (ad *attacherDetacher) AttachVolume(
- volumeToAttach *cache.VolumeToAttach,
+ volumeToAttach cache.VolumeToAttach,
actualStateOfWorld cache.ActualStateOfWorld) error {
attachFunc, err := ad.generateAttachVolumeFunc(volumeToAttach, actualStateOfWorld)
if err != nil {
return err
}
- return ad.pendingOperations.Run(volumeToAttach.VolumeName, attachFunc)
+ return ad.pendingOperations.Run(string(volumeToAttach.VolumeName), attachFunc)
}
func (ad *attacherDetacher) DetachVolume(
- volumeToDetach *cache.AttachedVolume,
+ volumeToDetach cache.AttachedVolume,
actualStateOfWorld cache.ActualStateOfWorld) error {
detachFunc, err := ad.generateDetachVolumeFunc(volumeToDetach, actualStateOfWorld)
if err != nil {
return err
}
- return ad.pendingOperations.Run(volumeToDetach.VolumeName, detachFunc)
+ return ad.pendingOperations.Run(string(volumeToDetach.VolumeName), detachFunc)
}
func (ad *attacherDetacher) generateAttachVolumeFunc(
- volumeToAttach *cache.VolumeToAttach,
+ volumeToAttach cache.VolumeToAttach,
actualStateOfWorld cache.ActualStateOfWorld) (func() error, error) {
// Get attacher plugin
attachableVolumePlugin, err := ad.volumePluginMgr.FindAttachablePluginBySpec(volumeToAttach.VolumeSpec)
@@ -119,15 +119,23 @@ func (ad *attacherDetacher) generateAttachVolumeFunc(
if attachErr != nil {
// On failure, just log and exit. The controller will retry
- glog.Errorf("Attach operation for %q failed with: %v", volumeToAttach.VolumeName, attachErr)
+ glog.Errorf(
+ "Attach operation for device %q to node %q failed with: %v",
+ volumeToAttach.VolumeName, volumeToAttach.NodeName, attachErr)
return attachErr
}
+ glog.Infof(
+ "Successfully attached device %q to node %q. Will update actual state of world.",
+ volumeToAttach.VolumeName, volumeToAttach.NodeName)
+
// Update actual state of world
_, addVolumeNodeErr := actualStateOfWorld.AddVolumeNode(volumeToAttach.VolumeSpec, volumeToAttach.NodeName)
if addVolumeNodeErr != nil {
// On failure, just log and exit. The controller will retry
- glog.Errorf("Attach operation for %q succeeded but updating actualStateOfWorld failed with: %v", volumeToAttach.VolumeName, addVolumeNodeErr)
+ glog.Errorf(
+ "Attach operation for device %q to node %q succeeded, but updating actualStateOfWorld failed with: %v",
+ volumeToAttach.VolumeName, volumeToAttach.NodeName, addVolumeNodeErr)
return addVolumeNodeErr
}
@@ -136,7 +144,7 @@ func (ad *attacherDetacher) generateAttachVolumeFunc(
}
func (ad *attacherDetacher) generateDetachVolumeFunc(
- volumeToDetach *cache.AttachedVolume,
+ volumeToDetach cache.AttachedVolume,
actualStateOfWorld cache.ActualStateOfWorld) (func() error, error) {
// Get attacher plugin
attachableVolumePlugin, err := ad.volumePluginMgr.FindAttachablePluginBySpec(volumeToDetach.VolumeSpec)
@@ -150,7 +158,7 @@ func (ad *attacherDetacher) generateDetachVolumeFunc(
deviceName, err := attachableVolumePlugin.GetDeviceName(volumeToDetach.VolumeSpec)
if err != nil {
return nil, fmt.Errorf(
- "failed to GetUniqueVolumeName from AttachablePlugin for volumeSpec %q err=%v",
+ "failed to GetDeviceName from AttachablePlugin for volumeSpec %q err=%v",
volumeToDetach.VolumeSpec.Name(),
err)
}
@@ -169,11 +177,15 @@ func (ad *attacherDetacher) generateDetachVolumeFunc(
if detachErr != nil {
// On failure, just log and exit. The controller will retry
- glog.Errorf("Detach operation for %q failed with: %v", volumeToDetach.VolumeName, detachErr)
+ glog.Errorf(
+ "Detach operation for device %q from node %q failed with: %v",
+ volumeToDetach.VolumeName, volumeToDetach.NodeName, detachErr)
return detachErr
}
- // TODO: Reset "safe to detach" annotation on Node
+ glog.Infof(
+ "Successfully detached device %q from node %q. Will update actual state of world.",
+ volumeToDetach.VolumeName, volumeToDetach.NodeName)
// Update actual state of world
actualStateOfWorld.DeleteVolumeNode(volumeToDetach.VolumeName, volumeToDetach.NodeName)
diff --git a/pkg/controller/volume/cache/actual_state_of_world.go b/pkg/controller/volume/cache/actual_state_of_world.go
index d6d979671ed..1eff7ae5b71 100644
--- a/pkg/controller/volume/cache/actual_state_of_world.go
+++ b/pkg/controller/volume/cache/actual_state_of_world.go
@@ -26,7 +26,9 @@ import (
"sync"
"time"
+ "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/volume"
+ "k8s.io/kubernetes/pkg/volume/util/attachdetach"
)
// ActualStateOfWorld defines a set of thread-safe operations supported on
@@ -38,21 +40,23 @@ type ActualStateOfWorld interface {
// indicating the specified volume is attached to the specified node.
// A unique volumeName is generated from the volumeSpec and returned on
// success.
- // If the volume/node combo already exists, this is a no-op.
+ // If the volume/node combo already exists, the detachRequestedTime is reset
+ // to zero.
// If volumeSpec is not an attachable volume plugin, an error is returned.
// If no volume with the name volumeName exists in the store, the volume is
// added.
// If no node with the name nodeName exists in list of attached nodes for
// the specified volume, the node is added.
- AddVolumeNode(volumeSpec *volume.Spec, nodeName string) (string, error)
+ AddVolumeNode(volumeSpec *volume.Spec, nodeName string) (api.UniqueDeviceName, error)
- // MarkVolumeNodeSafeToDetach marks the given volume as safe to detach from
- // the given node.
+ // SetVolumeMountedByNode sets the MountedByNode value for the given volume
+ // and node. When set to true this value indicates the volume is mounted by
+ // the given node, indicating it may not be safe to detach.
// If no volume with the name volumeName exists in the store, an error is
// returned.
// If no node with the name nodeName exists in list of attached nodes for
// the specified volume, an error is returned.
- MarkVolumeNodeSafeToDetach(volumeName, nodeName string) error
+ SetVolumeMountedByNode(volumeName api.UniqueDeviceName, nodeName string, mounted bool) error
// MarkDesireToDetach returns the difference between the current time and
// the DetachRequestedTime for the given volume/node combo. If the
@@ -61,7 +65,7 @@ type ActualStateOfWorld interface {
// returned.
// If no node with the name nodeName exists in list of attached nodes for
// the specified volume, an error is returned.
- MarkDesireToDetach(volumeName, nodeName string) (time.Duration, error)
+ MarkDesireToDetach(volumeName api.UniqueDeviceName, nodeName string) (time.Duration, error)
// DeleteVolumeNode removes the given volume and node from the underlying
// store indicating the specified volume is no longer attached to the
@@ -69,23 +73,28 @@ type ActualStateOfWorld interface {
// If the volume/node combo does not exist, this is a no-op.
// If after deleting the node, the specified volume contains no other child
// nodes, the volume is also deleted.
- DeleteVolumeNode(volumeName, nodeName string)
+ DeleteVolumeNode(volumeName api.UniqueDeviceName, nodeName string)
// VolumeNodeExists returns true if the specified volume/node combo exists
// in the underlying store indicating the specified volume is attached to
// the specified node.
- VolumeNodeExists(volumeName, nodeName string) bool
+ VolumeNodeExists(volumeName api.UniqueDeviceName, nodeName string) bool
// GetAttachedVolumes generates and returns a list of volumes/node pairs
// reflecting which volumes are attached to which nodes based on the
// current actual state of the world.
GetAttachedVolumes() []AttachedVolume
+
+ // GetAttachedVolumes generates and returns a list of volumes attached to
+ // the specified node reflecting which volumes are attached to that node
+ // based on the current actual state of the world.
+ GetAttachedVolumesForNode(nodeName string) []AttachedVolume
}
// AttachedVolume represents a volume that is attached to a node.
type AttachedVolume struct {
// VolumeName is the unique identifier for the volume that is attached.
- VolumeName string
+ VolumeName api.UniqueDeviceName
// VolumeSpec is the volume spec containing the specification for the
// volume that is attached.
@@ -94,11 +103,10 @@ type AttachedVolume struct {
// NodeName is the identifier for the node that the volume is attached to.
NodeName string
- // SafeToDetach indicates that this volume has been been unmounted from the
- // node and is safe to detach.
- // The value is set by MarkVolumeNodeSafeToDetach(...) and reset on
- // AddVolumeNode(...) calls.
- SafeToDetach bool
+ // MountedByNode indicates that this volume has been been mounted by the
+ // node and is unsafe to detach.
+ // The value is set and unset by SetVolumeMountedByNode(...).
+ MountedByNode bool
// DetachRequestedTime is used to capture the desire to detach this volume.
// When the volume is newly created this value is set to time zero.
@@ -111,7 +119,7 @@ type AttachedVolume struct {
// NewActualStateOfWorld returns a new instance of ActualStateOfWorld.
func NewActualStateOfWorld(volumePluginMgr *volume.VolumePluginMgr) ActualStateOfWorld {
return &actualStateOfWorld{
- attachedVolumes: make(map[string]attachedVolume),
+ attachedVolumes: make(map[api.UniqueDeviceName]attachedVolume),
volumePluginMgr: volumePluginMgr,
}
}
@@ -121,7 +129,7 @@ type actualStateOfWorld struct {
// controller believes to be successfully attached to the nodes it is
// managing. The key in this map is the name of the volume and the value is
// an object containing more information about the attached volume.
- attachedVolumes map[string]attachedVolume
+ attachedVolumes map[api.UniqueDeviceName]attachedVolume
// volumePluginMgr is the volume plugin manager used to create volume
// plugin objects.
volumePluginMgr *volume.VolumePluginMgr
@@ -132,7 +140,7 @@ type actualStateOfWorld struct {
// believes to be succesfully attached to a node it is managing.
type attachedVolume struct {
// volumeName contains the unique identifier for this volume.
- volumeName string
+ volumeName api.UniqueDeviceName
// spec is the volume spec containing the specification for this volume.
// Used to generate the volume plugin object, and passed to attach/detach
@@ -151,15 +159,22 @@ type nodeAttachedTo struct {
// nodeName contains the name of this node.
nodeName string
- // safeToDetach indicates that this node/volume combo has been unmounted
- // by the node and is safe to detach
- safeToDetach bool
+ // mountedByNode indicates that this node/volume combo is mounted by the
+ // node and is unsafe to detach
+ mountedByNode bool
+
+ // number of times SetVolumeMountedByNode has been called to set the value
+ // of mountedByNode to true. This is used to prevent mountedByNode from
+ // being reset during the period between attach and mount when volumesInUse
+ // status for the node may not be set.
+ mountedByNodeSetCount uint
// detachRequestedTime used to capture the desire to detach this volume
detachRequestedTime time.Time
}
-func (asw *actualStateOfWorld) AddVolumeNode(volumeSpec *volume.Spec, nodeName string) (string, error) {
+func (asw *actualStateOfWorld) AddVolumeNode(
+ volumeSpec *volume.Spec, nodeName string) (api.UniqueDeviceName, error) {
asw.Lock()
defer asw.Unlock()
@@ -171,10 +186,11 @@ func (asw *actualStateOfWorld) AddVolumeNode(volumeSpec *volume.Spec, nodeName s
err)
}
- volumeName, err := attachableVolumePlugin.GetUniqueVolumeName(volumeSpec)
+ volumeName, err := attachdetach.GetUniqueDeviceNameFromSpec(
+ attachableVolumePlugin, volumeSpec)
if err != nil {
return "", fmt.Errorf(
- "failed to GetUniqueVolumeName from AttachablePlugin for volumeSpec %q err=%v",
+ "failed to GetUniqueDeviceNameFromSpec for volumeSpec %q err=%v",
volumeSpec.Name(),
err)
}
@@ -190,55 +206,70 @@ func (asw *actualStateOfWorld) AddVolumeNode(volumeSpec *volume.Spec, nodeName s
}
nodeObj, nodeExists := volumeObj.nodesAttachedTo[nodeName]
- if !nodeExists || nodeObj.safeToDetach || !nodeObj.detachRequestedTime.IsZero() {
+ if !nodeExists {
// Create object if it doesn't exist.
- // Reset safeToDeatch and detachRequestedTime values if it does.
volumeObj.nodesAttachedTo[nodeName] = nodeAttachedTo{
- nodeName: nodeName,
- safeToDetach: false,
- detachRequestedTime: time.Time{},
+ nodeName: nodeName,
+ mountedByNode: true, // Assume mounted, until proven otherwise
+ mountedByNodeSetCount: 0,
+ detachRequestedTime: time.Time{},
}
+ } else if !nodeObj.detachRequestedTime.IsZero() {
+ // Reset detachRequestedTime values if object exists and time is non-zero
+ nodeObj.detachRequestedTime = time.Time{}
+ volumeObj.nodesAttachedTo[nodeName] = nodeObj
}
return volumeName, nil
}
-func (asw *actualStateOfWorld) MarkVolumeNodeSafeToDetach(
- volumeName, nodeName string) error {
+func (asw *actualStateOfWorld) SetVolumeMountedByNode(
+ volumeName api.UniqueDeviceName, nodeName string, mounted bool) error {
asw.Lock()
defer asw.Unlock()
volumeObj, volumeExists := asw.attachedVolumes[volumeName]
if !volumeExists {
return fmt.Errorf(
- "failed to MarkVolumeNodeSafeToDetach(volumeName=%q, nodeName=%q) volumeName does not exist",
+ "failed to SetVolumeMountedByNode(volumeName=%v, nodeName=%q, mounted=%v) volumeName does not exist",
volumeName,
- nodeName)
+ nodeName,
+ mounted)
}
nodeObj, nodeExists := volumeObj.nodesAttachedTo[nodeName]
if !nodeExists {
return fmt.Errorf(
- "failed to MarkVolumeNodeSafeToDetach(volumeName=%q, nodeName=%q) nodeName does not exist",
+ "failed to SetVolumeMountedByNode(volumeName=%v, nodeName=%q, mounted=%v) nodeName does not exist",
volumeName,
- nodeName)
+ nodeName,
+ mounted)
}
- // Reset safe to detach
- nodeObj.safeToDetach = true
+ if mounted {
+ // Increment set count
+ nodeObj.mountedByNodeSetCount = nodeObj.mountedByNodeSetCount + 1
+ } else {
+ // Do not allow value to be reset unless it has been set at least once
+ if nodeObj.mountedByNodeSetCount == 0 {
+ return nil
+ }
+ }
+
+ nodeObj.mountedByNode = mounted
volumeObj.nodesAttachedTo[nodeName] = nodeObj
return nil
}
func (asw *actualStateOfWorld) MarkDesireToDetach(
- volumeName, nodeName string) (time.Duration, error) {
+ volumeName api.UniqueDeviceName, nodeName string) (time.Duration, error) {
asw.Lock()
defer asw.Unlock()
volumeObj, volumeExists := asw.attachedVolumes[volumeName]
if !volumeExists {
return time.Millisecond * 0, fmt.Errorf(
- "failed to MarkVolumeNodeSafeToDetach(volumeName=%q, nodeName=%q) volumeName does not exist",
+ "failed to MarkDesireToDetach(volumeName=%v, nodeName=%q) volumeName does not exist",
volumeName,
nodeName)
}
@@ -246,7 +277,7 @@ func (asw *actualStateOfWorld) MarkDesireToDetach(
nodeObj, nodeExists := volumeObj.nodesAttachedTo[nodeName]
if !nodeExists {
return time.Millisecond * 0, fmt.Errorf(
- "failed to MarkVolumeNodeSafeToDetach(volumeName=%q, nodeName=%q) nodeName does not exist",
+ "failed to MarkDesireToDetach(volumeName=%v, nodeName=%q) nodeName does not exist",
volumeName,
nodeName)
}
@@ -259,7 +290,8 @@ func (asw *actualStateOfWorld) MarkDesireToDetach(
return time.Since(volumeObj.nodesAttachedTo[nodeName].detachRequestedTime), nil
}
-func (asw *actualStateOfWorld) DeleteVolumeNode(volumeName, nodeName string) {
+func (asw *actualStateOfWorld) DeleteVolumeNode(
+ volumeName api.UniqueDeviceName, nodeName string) {
asw.Lock()
defer asw.Unlock()
@@ -278,7 +310,8 @@ func (asw *actualStateOfWorld) DeleteVolumeNode(volumeName, nodeName string) {
}
}
-func (asw *actualStateOfWorld) VolumeNodeExists(volumeName, nodeName string) bool {
+func (asw *actualStateOfWorld) VolumeNodeExists(
+ volumeName api.UniqueDeviceName, nodeName string) bool {
asw.RLock()
defer asw.RUnlock()
@@ -305,10 +338,35 @@ func (asw *actualStateOfWorld) GetAttachedVolumes() []AttachedVolume {
NodeName: nodeName,
VolumeName: volumeName,
VolumeSpec: volumeObj.spec,
- SafeToDetach: nodeObj.safeToDetach,
+ MountedByNode: nodeObj.mountedByNode,
DetachRequestedTime: nodeObj.detachRequestedTime})
}
}
return attachedVolumes
}
+
+func (asw *actualStateOfWorld) GetAttachedVolumesForNode(
+ nodeName string) []AttachedVolume {
+ asw.RLock()
+ defer asw.RUnlock()
+
+ attachedVolumes := make(
+ []AttachedVolume, 0 /* len */, len(asw.attachedVolumes) /* cap */)
+ for volumeName, volumeObj := range asw.attachedVolumes {
+ for actualNodeName, nodeObj := range volumeObj.nodesAttachedTo {
+ if actualNodeName == nodeName {
+ attachedVolumes = append(
+ attachedVolumes,
+ AttachedVolume{
+ NodeName: nodeName,
+ VolumeName: volumeName,
+ VolumeSpec: volumeObj.spec,
+ MountedByNode: nodeObj.mountedByNode,
+ DetachRequestedTime: nodeObj.detachRequestedTime})
+ }
+ }
+ }
+
+ return attachedVolumes
+}
diff --git a/pkg/controller/volume/cache/actual_state_of_world_test.go b/pkg/controller/volume/cache/actual_state_of_world_test.go
index a9ccddc4ef4..95f7a135060 100644
--- a/pkg/controller/volume/cache/actual_state_of_world_test.go
+++ b/pkg/controller/volume/cache/actual_state_of_world_test.go
@@ -19,15 +19,18 @@ package cache
import (
"testing"
+ "k8s.io/kubernetes/pkg/api"
controllervolumetesting "k8s.io/kubernetes/pkg/controller/volume/testing"
)
+// Calls AddVolumeNode() once.
+// Verifies a single volume/node entry exists.
func Test_AddVolumeNode_Positive_NewVolumeNewNode(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
@@ -49,15 +52,17 @@ func Test_AddVolumeNode_Positive_NewVolumeNewNode(t *testing.T) {
t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes))
}
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, volumeName, nodeName, false /* expectedSafeToDetach */, false /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), nodeName, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
}
+// Calls AddVolumeNode() twice. Second time use a different node name.
+// Verifies two volume/node entries exist with the same volumeSpec.
func Test_AddVolumeNode_Positive_ExistingVolumeNewNode(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
node1Name := "node1-name"
node2Name := "node2-name"
@@ -95,16 +100,18 @@ func Test_AddVolumeNode_Positive_ExistingVolumeNewNode(t *testing.T) {
t.Fatalf("len(attachedVolumes) Expected: <2> Actual: <%v>", len(attachedVolumes))
}
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, volumeName, node1Name, false /* expectedSafeToDetach */, false /* expectNonZeroDetachRequestedTime */)
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, volumeName, node2Name, false /* expectedSafeToDetach */, false /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, string(volumeName), node1Name, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, string(volumeName), node2Name, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
}
+// Calls AddVolumeNode() twice. Uses the same volume and node both times.
+// Verifies a single volume/node entry exists.
func Test_AddVolumeNode_Positive_ExistingVolumeExistingNode(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
// Act
@@ -136,15 +143,18 @@ func Test_AddVolumeNode_Positive_ExistingVolumeExistingNode(t *testing.T) {
t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes))
}
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, volumeName, nodeName, false /* expectedSafeToDetach */, false /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, string(volumeName), nodeName, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
}
+// Populates data struct with one volume/node entry.
+// Calls DeleteVolumeNode() to delete volume/node.
+// Verifies no volume/node entries exists.
func Test_DeleteVolumeNode_Positive_VolumeExistsNodeExists(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName)
if addErr != nil {
@@ -166,11 +176,13 @@ func Test_DeleteVolumeNode_Positive_VolumeExistsNodeExists(t *testing.T) {
}
}
+// Calls DeleteVolumeNode() to delete volume/node on empty data stcut
+// Verifies no volume/node entries exists.
func Test_DeleteVolumeNode_Positive_VolumeDoesntExistNodeDoesntExist(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volumeName := "volume-name"
+ volumeName := api.UniqueDeviceName("volume-name")
nodeName := "node-name"
// Act
@@ -188,12 +200,16 @@ func Test_DeleteVolumeNode_Positive_VolumeDoesntExistNodeDoesntExist(t *testing.
}
}
+// Populates data struct with two volume/node entries the second one using a
+// different node.
+// Calls DeleteVolumeNode() to delete first volume/node.
+// Verifies only second volume/node entry exists.
func Test_DeleteVolumeNode_Positive_TwoNodesOneDeleted(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
node1Name := "node1-name"
node2Name := "node2-name"
generatedVolumeName1, add1Err := asw.AddVolumeNode(volumeSpec, node1Name)
@@ -230,15 +246,18 @@ func Test_DeleteVolumeNode_Positive_TwoNodesOneDeleted(t *testing.T) {
t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes))
}
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, volumeName, node2Name, false /* expectedSafeToDetach */, false /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, string(volumeName), node2Name, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
}
+// Populates data struct with one volume/node entry.
+// Calls VolumeNodeExists() to verify entry.
+// Verifies the populated volume/node entry exists.
func Test_VolumeNodeExists_Positive_VolumeExistsNodeExists(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName)
if addErr != nil {
@@ -258,15 +277,18 @@ func Test_VolumeNodeExists_Positive_VolumeExistsNodeExists(t *testing.T) {
t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes))
}
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, volumeName, nodeName, false /* expectedSafeToDetach */, false /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), nodeName, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
}
+// Populates data struct with one volume1/node1 entry.
+// Calls VolumeNodeExists() with volume1/node2.
+// Verifies requested entry does not exist, but populated entry does.
func Test_VolumeNodeExists_Positive_VolumeExistsNodeDoesntExist(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
node1Name := "node1-name"
node2Name := "node2-name"
generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, node1Name)
@@ -287,14 +309,16 @@ func Test_VolumeNodeExists_Positive_VolumeExistsNodeDoesntExist(t *testing.T) {
t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes))
}
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, volumeName, node1Name, false /* expectedSafeToDetach */, false /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), node1Name, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
}
+// Calls VolumeNodeExists() on empty data struct.
+// Verifies requested entry does not exist.
func Test_VolumeNodeExists_Positive_VolumeAndNodeDontExist(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volumeName := "volume-name"
+ volumeName := api.UniqueDeviceName("volume-name")
nodeName := "node-name"
// Act
@@ -311,6 +335,8 @@ func Test_VolumeNodeExists_Positive_VolumeAndNodeDontExist(t *testing.T) {
}
}
+// Calls GetAttachedVolumes() on empty data struct.
+// Verifies no volume/node entries are returned.
func Test_GetAttachedVolumes_Positive_NoVolumesOrNodes(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
@@ -325,12 +351,15 @@ func Test_GetAttachedVolumes_Positive_NoVolumesOrNodes(t *testing.T) {
}
}
+// Populates data struct with one volume/node entry.
+// Calls GetAttachedVolumes() to get list of entries.
+// Verifies one volume/node entry is returned.
func Test_GetAttachedVolumes_Positive_OneVolumeOneNode(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName)
if addErr != nil {
@@ -345,22 +374,25 @@ func Test_GetAttachedVolumes_Positive_OneVolumeOneNode(t *testing.T) {
t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes))
}
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, volumeName, nodeName, false /* expectedSafeToDetach */, false /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), nodeName, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
}
+// Populates data struct with two volume/node entries (different node and volume).
+// Calls GetAttachedVolumes() to get list of entries.
+// Verifies both volume/node entries are returned.
func Test_GetAttachedVolumes_Positive_TwoVolumeTwoNodes(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volume1Name := "volume1-name"
- volume1Spec := controllervolumetesting.GetTestVolumeSpec(volume1Name, volume1Name)
+ volume1Name := api.UniqueDeviceName("volume1-name")
+ volume1Spec := controllervolumetesting.GetTestVolumeSpec(string(volume1Name), volume1Name)
node1Name := "node1-name"
generatedVolumeName1, add1Err := asw.AddVolumeNode(volume1Spec, node1Name)
if add1Err != nil {
t.Fatalf("AddVolumeNode failed. Expected:
Actual: <%v>", add1Err)
}
- volume2Name := "volume2-name"
- volume2Spec := controllervolumetesting.GetTestVolumeSpec(volume2Name, volume2Name)
+ volume2Name := api.UniqueDeviceName("volume2-name")
+ volume2Spec := controllervolumetesting.GetTestVolumeSpec(string(volume2Name), volume2Name)
node2Name := "node2-name"
generatedVolumeName2, add2Err := asw.AddVolumeNode(volume2Spec, node2Name)
if add2Err != nil {
@@ -375,16 +407,19 @@ func Test_GetAttachedVolumes_Positive_TwoVolumeTwoNodes(t *testing.T) {
t.Fatalf("len(attachedVolumes) Expected: <2> Actual: <%v>", len(attachedVolumes))
}
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, volume1Name, node1Name, false /* expectedSafeToDetach */, false /* expectNonZeroDetachRequestedTime */)
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName2, volume2Name, node2Name, false /* expectedSafeToDetach */, false /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, string(volume1Name), node1Name, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName2, string(volume2Name), node2Name, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
}
+// Populates data struct with two volume/node entries (same volume different node).
+// Calls GetAttachedVolumes() to get list of entries.
+// Verifies both volume/node entries are returned.
func Test_GetAttachedVolumes_Positive_OneVolumeTwoNodes(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
node1Name := "node1-name"
generatedVolumeName1, add1Err := asw.AddVolumeNode(volumeSpec, node1Name)
if add1Err != nil {
@@ -411,16 +446,18 @@ func Test_GetAttachedVolumes_Positive_OneVolumeTwoNodes(t *testing.T) {
t.Fatalf("len(attachedVolumes) Expected: <2> Actual: <%v>", len(attachedVolumes))
}
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, volumeName, node1Name, false /* expectedSafeToDetach */, false /* expectNonZeroDetachRequestedTime */)
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, volumeName, node2Name, false /* expectedSafeToDetach */, false /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, string(volumeName), node1Name, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, string(volumeName), node2Name, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
}
-func Test_MarkVolumeNodeSafeToDetach_Positive_NotMarked(t *testing.T) {
+// Populates data struct with one volume/node entry.
+// Verifies mountedByNode is true and DetachRequestedTime is zero.
+func Test_SetVolumeMountedByNode_Positive_Set(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName)
if addErr != nil {
@@ -435,15 +472,18 @@ func Test_MarkVolumeNodeSafeToDetach_Positive_NotMarked(t *testing.T) {
t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes))
}
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, volumeName, nodeName, false /* expectedSafeToDetach */, false /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), nodeName, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
}
-func Test_MarkVolumeNodeSafeToDetach_Positive_Marked(t *testing.T) {
+// Populates data struct with one volume/node entry.
+// Calls SetVolumeMountedByNode twice, first setting mounted to true then false.
+// Verifies mountedByNode is false.
+func Test_SetVolumeMountedByNode_Positive_UnsetWithInitialSet(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName)
if addErr != nil {
@@ -451,11 +491,15 @@ func Test_MarkVolumeNodeSafeToDetach_Positive_Marked(t *testing.T) {
}
// Act
- markSafeToDetachErr := asw.MarkVolumeNodeSafeToDetach(generatedVolumeName, nodeName)
+ setVolumeMountedErr1 := asw.SetVolumeMountedByNode(generatedVolumeName, nodeName, true /* mounted */)
+ setVolumeMountedErr2 := asw.SetVolumeMountedByNode(generatedVolumeName, nodeName, false /* mounted */)
// Assert
- if markSafeToDetachErr != nil {
- t.Fatalf("MarkVolumeNodeSafeToDetach failed. Expected Actual: <%v>", markSafeToDetachErr)
+ if setVolumeMountedErr1 != nil {
+ t.Fatalf("SetVolumeMountedByNode1 failed. Expected Actual: <%v>", setVolumeMountedErr1)
+ }
+ if setVolumeMountedErr2 != nil {
+ t.Fatalf("SetVolumeMountedByNode2 failed. Expected Actual: <%v>", setVolumeMountedErr2)
}
attachedVolumes := asw.GetAttachedVolumes()
@@ -463,15 +507,18 @@ func Test_MarkVolumeNodeSafeToDetach_Positive_Marked(t *testing.T) {
t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes))
}
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, volumeName, nodeName, true /* expectedSafeToDetach */, false /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), nodeName, false /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
}
-func Test_MarkVolumeNodeSafeToDetach_Positive_MarkedAddVolumeNodeReset(t *testing.T) {
+// Populates data struct with one volume/node entry.
+// Calls SetVolumeMountedByNode once, setting mounted to false.
+// Verifies mountedByNode is still true (since there was no SetVolumeMountedByNode to true call first)
+func Test_SetVolumeMountedByNode_Positive_UnsetWithoutInitialSet(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName)
if addErr != nil {
@@ -479,12 +526,48 @@ func Test_MarkVolumeNodeSafeToDetach_Positive_MarkedAddVolumeNodeReset(t *testin
}
// Act
- markSafeToDetachErr := asw.MarkVolumeNodeSafeToDetach(generatedVolumeName, nodeName)
+ setVolumeMountedErr := asw.SetVolumeMountedByNode(generatedVolumeName, nodeName, false /* mounted */)
+
+ // Assert
+ if setVolumeMountedErr != nil {
+ t.Fatalf("SetVolumeMountedByNode failed. Expected Actual: <%v>", setVolumeMountedErr)
+ }
+
+ attachedVolumes := asw.GetAttachedVolumes()
+ if len(attachedVolumes) != 1 {
+ t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes))
+ }
+
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), nodeName, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
+}
+
+// Populates data struct with one volume/node entry.
+// Calls SetVolumeMountedByNode twice, first setting mounted to true then false.
+// Calls AddVolumeNode to readd the same volume/node.
+// Verifies mountedByNode is false and detachRequestedTime is zero.
+func Test_SetVolumeMountedByNode_Positive_UnsetWithInitialSetAddVolumeNodeNotReset(t *testing.T) {
+ // Arrange
+ volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
+ asw := NewActualStateOfWorld(volumePluginMgr)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
+ nodeName := "node-name"
+ generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName)
+ if addErr != nil {
+ t.Fatalf("AddVolumeNode failed. Expected: Actual: <%v>", addErr)
+ }
+
+ // Act
+ setVolumeMountedErr1 := asw.SetVolumeMountedByNode(generatedVolumeName, nodeName, true /* mounted */)
+ setVolumeMountedErr2 := asw.SetVolumeMountedByNode(generatedVolumeName, nodeName, false /* mounted */)
generatedVolumeName, addErr = asw.AddVolumeNode(volumeSpec, nodeName)
// Assert
- if markSafeToDetachErr != nil {
- t.Fatalf("MarkVolumeNodeSafeToDetach failed. Expected Actual: <%v>", markSafeToDetachErr)
+ if setVolumeMountedErr1 != nil {
+ t.Fatalf("SetVolumeMountedByNode1 failed. Expected Actual: <%v>", setVolumeMountedErr1)
+ }
+ if setVolumeMountedErr2 != nil {
+ t.Fatalf("SetVolumeMountedByNode2 failed. Expected Actual: <%v>", setVolumeMountedErr2)
}
if addErr != nil {
t.Fatalf("AddVolumeNode failed. Expected: Actual: <%v>", addErr)
@@ -495,15 +578,19 @@ func Test_MarkVolumeNodeSafeToDetach_Positive_MarkedAddVolumeNodeReset(t *testin
t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes))
}
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, volumeName, nodeName, false /* expectedSafeToDetach */, false /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), nodeName, false /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
}
-func Test_MarkVolumeNodeSafeToDetach_Positive_MarkedVerifyDetachRequestedTimePerserved(t *testing.T) {
+// Populates data struct with one volume/node entry.
+// Calls MarkDesireToDetach() once on volume/node entry.
+// Calls SetVolumeMountedByNode() twice, first setting mounted to true then false.
+// Verifies mountedByNode is false and detachRequestedTime is NOT zero.
+func Test_SetVolumeMountedByNode_Positive_UnsetWithInitialSetVerifyDetachRequestedTimePerserved(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName)
if addErr != nil {
@@ -516,11 +603,15 @@ func Test_MarkVolumeNodeSafeToDetach_Positive_MarkedVerifyDetachRequestedTimePer
expectedDetachRequestedTime := asw.GetAttachedVolumes()[0].DetachRequestedTime
// Act
- markSafeToDetachErr := asw.MarkVolumeNodeSafeToDetach(generatedVolumeName, nodeName)
+ setVolumeMountedErr1 := asw.SetVolumeMountedByNode(generatedVolumeName, nodeName, true /* mounted */)
+ setVolumeMountedErr2 := asw.SetVolumeMountedByNode(generatedVolumeName, nodeName, false /* mounted */)
// Assert
- if markSafeToDetachErr != nil {
- t.Fatalf("MarkVolumeNodeSafeToDetach failed. Expected Actual: <%v>", markSafeToDetachErr)
+ if setVolumeMountedErr1 != nil {
+ t.Fatalf("SetVolumeMountedByNode1 failed. Expected Actual: <%v>", setVolumeMountedErr1)
+ }
+ if setVolumeMountedErr2 != nil {
+ t.Fatalf("SetVolumeMountedByNode2 failed. Expected Actual: <%v>", setVolumeMountedErr2)
}
attachedVolumes := asw.GetAttachedVolumes()
@@ -528,18 +619,20 @@ func Test_MarkVolumeNodeSafeToDetach_Positive_MarkedVerifyDetachRequestedTimePer
t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes))
}
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, volumeName, nodeName, true /* expectedSafeToDetach */, true /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), nodeName, false /* expectedMountedByNode */, true /* expectNonZeroDetachRequestedTime */)
if !expectedDetachRequestedTime.Equal(attachedVolumes[0].DetachRequestedTime) {
t.Fatalf("DetachRequestedTime changed. Expected: <%v> Actual: <%v>", expectedDetachRequestedTime, attachedVolumes[0].DetachRequestedTime)
}
}
-func Test_MarkDesireToDetach_Positive_NotMarked(t *testing.T) {
+// Populates data struct with one volume/node entry.
+// Verifies mountedByNode is true and detachRequestedTime is zero (default values).
+func Test_MarkDesireToDetach_Positive_Set(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName)
if addErr != nil {
@@ -554,15 +647,18 @@ func Test_MarkDesireToDetach_Positive_NotMarked(t *testing.T) {
t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes))
}
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, volumeName, nodeName, false /* expectedSafeToDetach */, false /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), nodeName, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
}
+// Populates data struct with one volume/node entry.
+// Calls MarkDesireToDetach() once on volume/node entry.
+// Verifies mountedByNode is true and detachRequestedTime is NOT zero.
func Test_MarkDesireToDetach_Positive_Marked(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName)
if addErr != nil {
@@ -583,15 +679,19 @@ func Test_MarkDesireToDetach_Positive_Marked(t *testing.T) {
t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes))
}
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, volumeName, nodeName, false /* expectedSafeToDetach */, true /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), nodeName, true /* expectedMountedByNode */, true /* expectNonZeroDetachRequestedTime */)
}
+// Populates data struct with one volume/node entry.
+// Calls MarkDesireToDetach() once on volume/node entry.
+// Calls AddVolumeNode() to re-add the same volume/node entry.
+// Verifies mountedByNode is true and detachRequestedTime is reset to zero.
func Test_MarkDesireToDetach_Positive_MarkedAddVolumeNodeReset(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName)
if addErr != nil {
@@ -616,23 +716,31 @@ func Test_MarkDesireToDetach_Positive_MarkedAddVolumeNodeReset(t *testing.T) {
t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes))
}
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, volumeName, nodeName, false /* expectedSafeToDetach */, false /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), nodeName, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
}
-func Test_MarkDesireToDetach_Positive_MarkedVerifySafeToDetachPreserved(t *testing.T) {
+// Populates data struct with one volume/node entry.
+// Calls SetVolumeMountedByNode() twice, first setting mounted to true then false.
+// Calls MarkDesireToDetach() once on volume/node entry.
+// Verifies mountedByNode is false and detachRequestedTime is NOT zero.
+func Test_MarkDesireToDetach_Positive_UnsetWithInitialSetVolumeMountedByNodePreserved(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
asw := NewActualStateOfWorld(volumePluginMgr)
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName)
if addErr != nil {
t.Fatalf("AddVolumeNode failed. Expected: Actual: <%v>", addErr)
}
- markSafeToDetachErr := asw.MarkVolumeNodeSafeToDetach(generatedVolumeName, nodeName)
- if markSafeToDetachErr != nil {
- t.Fatalf("MarkVolumeNodeSafeToDetach failed. Expected Actual: <%v>", markSafeToDetachErr)
+ setVolumeMountedErr1 := asw.SetVolumeMountedByNode(generatedVolumeName, nodeName, true /* mounted */)
+ setVolumeMountedErr2 := asw.SetVolumeMountedByNode(generatedVolumeName, nodeName, false /* mounted */)
+ if setVolumeMountedErr1 != nil {
+ t.Fatalf("SetVolumeMountedByNode1 failed. Expected Actual: <%v>", setVolumeMountedErr1)
+ }
+ if setVolumeMountedErr2 != nil {
+ t.Fatalf("SetVolumeMountedByNode2 failed. Expected Actual: <%v>", setVolumeMountedErr2)
}
// Act
@@ -649,34 +757,135 @@ func Test_MarkDesireToDetach_Positive_MarkedVerifySafeToDetachPreserved(t *testi
t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes))
}
- verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, volumeName, nodeName, true /* expectedSafeToDetach */, true /* expectNonZeroDetachRequestedTime */)
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), nodeName, false /* expectedMountedByNode */, true /* expectNonZeroDetachRequestedTime */)
}
func verifyAttachedVolume(
t *testing.T,
attachedVolumes []AttachedVolume,
- expectedVolumeName,
- expectedVolumeSpecName,
+ expectedVolumeName api.UniqueDeviceName,
+ expectedVolumeSpecName string,
expectedNodeName string,
- expectedSafeToDetach,
+ expectedMountedByNode,
expectNonZeroDetachRequestedTime bool) {
for _, attachedVolume := range attachedVolumes {
if attachedVolume.VolumeName == expectedVolumeName &&
attachedVolume.VolumeSpec.Name() == expectedVolumeSpecName &&
attachedVolume.NodeName == expectedNodeName &&
- attachedVolume.SafeToDetach == expectedSafeToDetach &&
+ attachedVolume.MountedByNode == expectedMountedByNode &&
attachedVolume.DetachRequestedTime.IsZero() == !expectNonZeroDetachRequestedTime {
return
}
}
t.Fatalf(
- "attachedVolumes (%v) should contain the volume/node combo %q/%q with SafeToDetach=%v and NonZeroDetachRequestedTime=%v. It does not.",
+ "attachedVolumes (%v) should contain the volume/node combo %q/%q with MountedByNode=%v and NonZeroDetachRequestedTime=%v. It does not.",
attachedVolumes,
expectedVolumeName,
expectedNodeName,
- expectedSafeToDetach,
+ expectedMountedByNode,
expectNonZeroDetachRequestedTime)
}
-// t.Logf("attachedVolumes: %v", asw.GetAttachedVolumes()) // TEMP
+func Test_GetAttachedVolumesForNode_Positive_NoVolumesOrNodes(t *testing.T) {
+ // Arrange
+ volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
+ asw := NewActualStateOfWorld(volumePluginMgr)
+ node := "random"
+
+ // Act
+ attachedVolumes := asw.GetAttachedVolumesForNode(node)
+
+ // Assert
+ if len(attachedVolumes) != 0 {
+ t.Fatalf("len(attachedVolumes) Expected: <0> Actual: <%v>", len(attachedVolumes))
+ }
+}
+
+func Test_GetAttachedVolumesForNode_Positive_OneVolumeOneNode(t *testing.T) {
+ // Arrange
+ volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
+ asw := NewActualStateOfWorld(volumePluginMgr)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
+ nodeName := "node-name"
+ generatedVolumeName, addErr := asw.AddVolumeNode(volumeSpec, nodeName)
+ if addErr != nil {
+ t.Fatalf("AddVolumeNode failed. Expected: Actual: <%v>", addErr)
+ }
+
+ // Act
+ attachedVolumes := asw.GetAttachedVolumesForNode(nodeName)
+
+ // Assert
+ if len(attachedVolumes) != 1 {
+ t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes))
+ }
+
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName, string(volumeName), nodeName, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
+}
+
+func Test_GetAttachedVolumesForNode_Positive_TwoVolumeTwoNodes(t *testing.T) {
+ // Arrange
+ volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
+ asw := NewActualStateOfWorld(volumePluginMgr)
+ volume1Name := api.UniqueDeviceName("volume1-name")
+ volume1Spec := controllervolumetesting.GetTestVolumeSpec(string(volume1Name), volume1Name)
+ node1Name := "node1-name"
+ _, add1Err := asw.AddVolumeNode(volume1Spec, node1Name)
+ if add1Err != nil {
+ t.Fatalf("AddVolumeNode failed. Expected: Actual: <%v>", add1Err)
+ }
+ volume2Name := api.UniqueDeviceName("volume2-name")
+ volume2Spec := controllervolumetesting.GetTestVolumeSpec(string(volume2Name), volume2Name)
+ node2Name := "node2-name"
+ generatedVolumeName2, add2Err := asw.AddVolumeNode(volume2Spec, node2Name)
+ if add2Err != nil {
+ t.Fatalf("AddVolumeNode failed. Expected: Actual: <%v>", add2Err)
+ }
+
+ // Act
+ attachedVolumes := asw.GetAttachedVolumesForNode(node2Name)
+
+ // Assert
+ if len(attachedVolumes) != 1 {
+ t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes))
+ }
+
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName2, string(volume2Name), node2Name, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
+}
+
+func Test_GetAttachedVolumesForNode_Positive_OneVolumeTwoNodes(t *testing.T) {
+ // Arrange
+ volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
+ asw := NewActualStateOfWorld(volumePluginMgr)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
+ node1Name := "node1-name"
+ generatedVolumeName1, add1Err := asw.AddVolumeNode(volumeSpec, node1Name)
+ if add1Err != nil {
+ t.Fatalf("AddVolumeNode failed. Expected: Actual: <%v>", add1Err)
+ }
+ node2Name := "node2-name"
+ generatedVolumeName2, add2Err := asw.AddVolumeNode(volumeSpec, node2Name)
+ if add2Err != nil {
+ t.Fatalf("AddVolumeNode failed. Expected: Actual: <%v>", add2Err)
+ }
+
+ if generatedVolumeName1 != generatedVolumeName2 {
+ t.Fatalf(
+ "Generated volume names for the same volume should be the same but they are not: %q and %q",
+ generatedVolumeName1,
+ generatedVolumeName2)
+ }
+
+ // Act
+ attachedVolumes := asw.GetAttachedVolumesForNode(node1Name)
+
+ // Assert
+ if len(attachedVolumes) != 1 {
+ t.Fatalf("len(attachedVolumes) Expected: <1> Actual: <%v>", len(attachedVolumes))
+ }
+
+ verifyAttachedVolume(t, attachedVolumes, generatedVolumeName1, string(volumeName), node1Name, true /* expectedMountedByNode */, false /* expectNonZeroDetachRequestedTime */)
+}
diff --git a/pkg/controller/volume/cache/desired_state_of_world.go b/pkg/controller/volume/cache/desired_state_of_world.go
index f2095150df2..c9ec2a8e93e 100644
--- a/pkg/controller/volume/cache/desired_state_of_world.go
+++ b/pkg/controller/volume/cache/desired_state_of_world.go
@@ -25,7 +25,9 @@ import (
"fmt"
"sync"
+ "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/volume"
+ "k8s.io/kubernetes/pkg/volume/util/attachdetach"
)
// DesiredStateOfWorld defines a set of thread-safe operations supported on
@@ -50,7 +52,7 @@ type DesiredStateOfWorld interface {
// should be attached to the specified node, the volume is implicitly added.
// If no node with the name nodeName exists in list of nodes managed by the
// attach/detach attached controller, an error is returned.
- AddPod(podName string, volumeSpec *volume.Spec, nodeName string) (string, error)
+ AddPod(podName string, volumeSpec *volume.Spec, nodeName string) (api.UniqueDeviceName, error)
// DeleteNode removes the given node from the list of nodes managed by the
// attach/detach controller.
@@ -68,7 +70,7 @@ type DesiredStateOfWorld interface {
// volumes under the specified node, this is a no-op.
// If after deleting the pod, the specified volume contains no other child
// pods, the volume is also deleted.
- DeletePod(podName, volumeName, nodeName string)
+ DeletePod(podName string, volumeName api.UniqueDeviceName, nodeName string)
// NodeExists returns true if the node with the specified name exists in
// the list of nodes managed by the attach/detach controller.
@@ -77,7 +79,7 @@ type DesiredStateOfWorld interface {
// VolumeExists returns true if the volume with the specified name exists
// in the list of volumes that should be attached to the specified node by
// the attach detach controller.
- VolumeExists(volumeName, nodeName string) bool
+ VolumeExists(volumeName api.UniqueDeviceName, nodeName string) bool
// GetVolumesToAttach generates and returns a list of volumes to attach
// and the nodes they should be attached to based on the current desired
@@ -89,7 +91,7 @@ type DesiredStateOfWorld interface {
type VolumeToAttach struct {
// VolumeName is the unique identifier for the volume that should be
// attached.
- VolumeName string
+ VolumeName api.UniqueDeviceName
// VolumeSpec is a volume spec containing the specification for the volume
// that should be attached.
@@ -128,13 +130,13 @@ type nodeManaged struct {
// volumesToAttach is a map containing the set of volumes that should be
// attached to this node. The key in the map is the name of the volume and
// the value is a pod object containing more information about the volume.
- volumesToAttach map[string]volumeToAttach
+ volumesToAttach map[api.UniqueDeviceName]volumeToAttach
}
// The volume object represents a volume that should be attached to a node.
type volumeToAttach struct {
// volumeName contains the unique identifier for this volume.
- volumeName string
+ volumeName api.UniqueDeviceName
// spec is the volume spec containing the specification for this volume.
// Used to generate the volume plugin object, and passed to attach/detach
@@ -162,12 +164,15 @@ func (dsw *desiredStateOfWorld) AddNode(nodeName string) {
if _, nodeExists := dsw.nodesManaged[nodeName]; !nodeExists {
dsw.nodesManaged[nodeName] = nodeManaged{
nodeName: nodeName,
- volumesToAttach: make(map[string]volumeToAttach),
+ volumesToAttach: make(map[api.UniqueDeviceName]volumeToAttach),
}
}
}
-func (dsw *desiredStateOfWorld) AddPod(podName string, volumeSpec *volume.Spec, nodeName string) (string, error) {
+func (dsw *desiredStateOfWorld) AddPod(
+ podName string,
+ volumeSpec *volume.Spec,
+ nodeName string) (api.UniqueDeviceName, error) {
dsw.Lock()
defer dsw.Unlock()
@@ -186,10 +191,11 @@ func (dsw *desiredStateOfWorld) AddPod(podName string, volumeSpec *volume.Spec,
err)
}
- volumeName, err := attachableVolumePlugin.GetUniqueVolumeName(volumeSpec)
+ volumeName, err := attachdetach.GetUniqueDeviceNameFromSpec(
+ attachableVolumePlugin, volumeSpec)
if err != nil {
return "", fmt.Errorf(
- "failed to GetUniqueVolumeName from AttachablePlugin for volumeSpec %q err=%v",
+ "failed to GenerateUniqueDeviceName for volumeSpec %q err=%v",
volumeSpec.Name(),
err)
}
@@ -236,7 +242,10 @@ func (dsw *desiredStateOfWorld) DeleteNode(nodeName string) error {
return nil
}
-func (dsw *desiredStateOfWorld) DeletePod(podName, volumeName, nodeName string) {
+func (dsw *desiredStateOfWorld) DeletePod(
+ podName string,
+ volumeName api.UniqueDeviceName,
+ nodeName string) {
dsw.Lock()
defer dsw.Unlock()
@@ -272,7 +281,8 @@ func (dsw *desiredStateOfWorld) NodeExists(nodeName string) bool {
return nodeExists
}
-func (dsw *desiredStateOfWorld) VolumeExists(volumeName, nodeName string) bool {
+func (dsw *desiredStateOfWorld) VolumeExists(
+ volumeName api.UniqueDeviceName, nodeName string) bool {
dsw.RLock()
defer dsw.RUnlock()
diff --git a/pkg/controller/volume/cache/desired_state_of_world_test.go b/pkg/controller/volume/cache/desired_state_of_world_test.go
index 18bd2b83819..791aea5f439 100644
--- a/pkg/controller/volume/cache/desired_state_of_world_test.go
+++ b/pkg/controller/volume/cache/desired_state_of_world_test.go
@@ -19,9 +19,12 @@ package cache
import (
"testing"
+ "k8s.io/kubernetes/pkg/api"
controllervolumetesting "k8s.io/kubernetes/pkg/controller/volume/testing"
)
+// Calls AddNode() once.
+// Verifies node exists, and zero volumes to attach.
func Test_AddNode_Positive_NewNode(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
@@ -43,27 +46,10 @@ func Test_AddNode_Positive_NewNode(t *testing.T) {
}
}
-func Test_AddNode_Positive_ExistingVolume(t *testing.T) {
- // Arrange
- volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
- dsw := NewDesiredStateOfWorld(volumePluginMgr)
- nodeName := "node-name"
- dsw.AddNode(nodeName)
-
- // Act
- dsw.AddNode(nodeName)
-
- // Assert
- nodeExists := dsw.NodeExists(nodeName)
- if !nodeExists {
- t.Fatalf("Added node %q does not exist, it should.", nodeName)
- }
-
- volumesToAttach := dsw.GetVolumesToAttach()
- if len(volumesToAttach) != 0 {
- t.Fatalf("len(volumesToAttach) Expected: <0> Actual: <%v>", len(volumesToAttach))
- }
-}
+// Calls AddNode() once.
+// Verifies node exists.
+// Calls AddNode() again with the same node.
+// Verifies node exists, and zero volumes to attach.
func Test_AddNode_Positive_ExistingNode(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
@@ -94,13 +80,16 @@ func Test_AddNode_Positive_ExistingNode(t *testing.T) {
}
}
+// Populates data struct with a single node no volume.
+// Calls AddPod() with the same node and new pod/volume.
+// Verifies node/volume exists, and 1 volumes to attach.
func Test_AddPod_Positive_NewPodNodeExistsVolumeDoesntExist(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
dsw := NewDesiredStateOfWorld(volumePluginMgr)
podName := "pod-name"
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
dsw.AddNode(nodeName)
volumeExists := dsw.VolumeExists(volumeName, nodeName)
@@ -133,17 +122,22 @@ func Test_AddPod_Positive_NewPodNodeExistsVolumeDoesntExist(t *testing.T) {
t.Fatalf("len(volumesToAttach) Expected: <1> Actual: <%v>", len(volumesToAttach))
}
- verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolumeName, volumeName)
+ verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolumeName, string(volumeName))
}
+// Populates data struct with a single node no volume.
+// Calls AddPod() with the same node and new pod/volume.
+// Verifies node/volume exists.
+// Calls AddPod() with the same node and volume different pod.
+// Verifies the same node/volume exists, and 1 volumes to attach.
func Test_AddPod_Positive_NewPodNodeExistsVolumeExists(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
dsw := NewDesiredStateOfWorld(volumePluginMgr)
pod1Name := "pod1-name"
pod2Name := "pod2-name"
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
dsw.AddNode(nodeName)
volumeExists := dsw.VolumeExists(volumeName, nodeName)
@@ -198,16 +192,21 @@ func Test_AddPod_Positive_NewPodNodeExistsVolumeExists(t *testing.T) {
t.Fatalf("len(volumesToAttach) Expected: <1> Actual: <%v>", len(volumesToAttach))
}
- verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolumeName, volumeName)
+ verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolumeName, string(volumeName))
}
+// Populates data struct with a single node no volume.
+// Calls AddPod() with the same node and new pod/volume.
+// Verifies node/volume exists.
+// Calls AddPod() with the same node, volume, and pod.
+// Verifies the same node/volume exists, and 1 volumes to attach.
func Test_AddPod_Positive_PodExistsNodeExistsVolumeExists(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
dsw := NewDesiredStateOfWorld(volumePluginMgr)
podName := "pod-name"
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
dsw.AddNode(nodeName)
volumeExists := dsw.VolumeExists(volumeName, nodeName)
@@ -262,16 +261,18 @@ func Test_AddPod_Positive_PodExistsNodeExistsVolumeExists(t *testing.T) {
t.Fatalf("len(volumesToAttach) Expected: <1> Actual: <%v>", len(volumesToAttach))
}
- verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolumeName, volumeName)
+ verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolumeName, string(volumeName))
}
+// Calls AddPod() with new pod/volume/node on empty data struct.
+// Verifies call fails because node does not exist.
func Test_AddPod_Negative_NewPodNodeDoesntExistVolumeDoesntExist(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
dsw := NewDesiredStateOfWorld(volumePluginMgr)
podName := "pod-name"
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
volumeExists := dsw.VolumeExists(volumeName, nodeName)
if volumeExists {
@@ -303,6 +304,9 @@ func Test_AddPod_Negative_NewPodNodeDoesntExistVolumeDoesntExist(t *testing.T) {
}
}
+// Populates data struct with a single node.
+// Calls DeleteNode() to delete the node.
+// Verifies node no longer exists, and zero volumes to attach.
func Test_DeleteNode_Positive_NodeExists(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
@@ -329,6 +333,8 @@ func Test_DeleteNode_Positive_NodeExists(t *testing.T) {
}
}
+// Calls DeleteNode() to delete node on empty data struct.
+// Verifies no error is returned, and zero volumes to attach.
func Test_DeleteNode_Positive_NodeDoesntExist(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
@@ -354,6 +360,9 @@ func Test_DeleteNode_Positive_NodeDoesntExist(t *testing.T) {
}
}
+// Populates data struct with new pod/volume/node.
+// Calls DeleteNode() to delete the node.
+// Verifies call fails because node still contains child volumes.
func Test_DeleteNode_Negative_NodeExistsHasChildVolumes(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
@@ -361,8 +370,8 @@ func Test_DeleteNode_Negative_NodeExistsHasChildVolumes(t *testing.T) {
nodeName := "node-name"
dsw.AddNode(nodeName)
podName := "pod-name"
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
generatedVolumeName, podAddErr := dsw.AddPod(podName, volumeSpec, nodeName)
if podAddErr != nil {
t.Fatalf(
@@ -376,7 +385,7 @@ func Test_DeleteNode_Negative_NodeExistsHasChildVolumes(t *testing.T) {
// Assert
if err == nil {
- t.Fatalf("DeleteNode did not fail. Expected: <\"\"> Actual: ")
+ t.Fatalf("DeleteNode did not fail. Expected: <\"failed to delete node...the node still contains volumes in its list of volumes to attach\"> Actual: ")
}
nodeExists := dsw.NodeExists(nodeName)
@@ -389,16 +398,19 @@ func Test_DeleteNode_Negative_NodeExistsHasChildVolumes(t *testing.T) {
t.Fatalf("len(volumesToAttach) Expected: <1> Actual: <%v>", len(volumesToAttach))
}
- verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolumeName, volumeName)
+ verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolumeName, string(volumeName))
}
+// Populates data struct with new pod/volume/node.
+// Calls DeleteNode() to delete the pod/volume/node.
+// Verifies volume no longer exists, and zero volumes to attach.
func Test_DeletePod_Positive_PodExistsNodeExistsVolumeExists(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
dsw := NewDesiredStateOfWorld(volumePluginMgr)
podName := "pod-name"
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
dsw.AddNode(nodeName)
generatedVolumeName, podAddErr := dsw.AddPod(podName, volumeSpec, nodeName)
@@ -436,14 +448,17 @@ func Test_DeletePod_Positive_PodExistsNodeExistsVolumeExists(t *testing.T) {
}
}
+// Populates data struct with pod1/volume/node and pod2/volume/node.
+// Calls DeleteNode() to delete the pod1/volume/node.
+// Verifies volume still exists, and one volumes to attach.
func Test_DeletePod_Positive_2PodsExistNodeExistsVolumesExist(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
dsw := NewDesiredStateOfWorld(volumePluginMgr)
pod1Name := "pod1-name"
pod2Name := "pod2-name"
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
dsw.AddNode(nodeName)
generatedVolumeName1, pod1AddErr := dsw.AddPod(pod1Name, volumeSpec, nodeName)
@@ -491,17 +506,20 @@ func Test_DeletePod_Positive_2PodsExistNodeExistsVolumesExist(t *testing.T) {
t.Fatalf("len(volumesToAttach) Expected: <1> Actual: <%v>", len(volumesToAttach))
}
- verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolumeName1, volumeName)
+ verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolumeName1, string(volumeName))
}
+// Populates data struct with pod1/volume/node.
+// Calls DeleteNode() to delete the pod2/volume/node.
+// Verifies volume still exists, and one volumes to attach.
func Test_DeletePod_Positive_PodDoesNotExist(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
dsw := NewDesiredStateOfWorld(volumePluginMgr)
pod1Name := "pod1-name"
pod2Name := "pod2-name"
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
dsw.AddNode(nodeName)
generatedVolumeName, pod1AddErr := dsw.AddPod(pod1Name, volumeSpec, nodeName)
@@ -537,16 +555,19 @@ func Test_DeletePod_Positive_PodDoesNotExist(t *testing.T) {
t.Fatalf("len(volumesToAttach) Expected: <1> Actual: <%v>", len(volumesToAttach))
}
- verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolumeName, volumeName)
+ verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolumeName, string(volumeName))
}
+// Populates data struct with pod/volume/node1.
+// Calls DeleteNode() to delete the pod/volume/node2.
+// Verifies volume still exists, and one volumes to attach.
func Test_DeletePod_Positive_NodeDoesNotExist(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
dsw := NewDesiredStateOfWorld(volumePluginMgr)
podName := "pod-name"
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
node1Name := "node1-name"
dsw.AddNode(node1Name)
generatedVolumeName, podAddErr := dsw.AddPod(podName, volumeSpec, node1Name)
@@ -589,16 +610,19 @@ func Test_DeletePod_Positive_NodeDoesNotExist(t *testing.T) {
t.Fatalf("len(volumesToAttach) Expected: <1> Actual: <%v>", len(volumesToAttach))
}
- verifyVolumeToAttach(t, volumesToAttach, node1Name, generatedVolumeName, volumeName)
+ verifyVolumeToAttach(t, volumesToAttach, node1Name, generatedVolumeName, string(volumeName))
}
+// Populates data struct with pod/volume1/node.
+// Calls DeleteNode() to delete the pod/volume2/node.
+// Verifies volume still exists, and one volumes to attach.
func Test_DeletePod_Positive_VolumeDoesNotExist(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
dsw := NewDesiredStateOfWorld(volumePluginMgr)
podName := "pod-name"
- volume1Name := "volume1-name"
- volume1Spec := controllervolumetesting.GetTestVolumeSpec(volume1Name, volume1Name)
+ volume1Name := api.UniqueDeviceName("volume1-name")
+ volume1Spec := controllervolumetesting.GetTestVolumeSpec(string(volume1Name), volume1Name)
nodeName := "node-name"
dsw.AddNode(nodeName)
generatedVolume1Name, podAddErr := dsw.AddPod(podName, volume1Spec, nodeName)
@@ -616,7 +640,7 @@ func Test_DeletePod_Positive_VolumeDoesNotExist(t *testing.T) {
generatedVolume1Name,
nodeName)
}
- volume2Name := "volume2-name"
+ volume2Name := api.UniqueDeviceName("volume2-name")
// Act
dsw.DeletePod(podName, volume2Name, nodeName)
@@ -641,9 +665,11 @@ func Test_DeletePod_Positive_VolumeDoesNotExist(t *testing.T) {
t.Fatalf("len(volumesToAttach) Expected: <1> Actual: <%v>", len(volumesToAttach))
}
- verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolume1Name, volume1Name)
+ verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolume1Name, string(volume1Name))
}
+// Calls NodeExists() on random node.
+// Verifies node does not exist, and no volumes to attach.
func Test_NodeExists_Positive_NodeExists(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
@@ -664,6 +690,9 @@ func Test_NodeExists_Positive_NodeExists(t *testing.T) {
}
}
+// Populates data struct with a single node.
+// Calls NodeExists() on that node.
+// Verifies node exists, and no volumes to attach.
func Test_NodeExists_Positive_NodeDoesntExist(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
@@ -685,6 +714,9 @@ func Test_NodeExists_Positive_NodeDoesntExist(t *testing.T) {
}
}
+// Populates data struct with new pod/volume/node.
+// Calls VolumeExists() on that volume/node.
+// Verifies volume/node exists, and one volume to attach.
func Test_VolumeExists_Positive_VolumeExistsNodeExists(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
@@ -692,8 +724,8 @@ func Test_VolumeExists_Positive_VolumeExistsNodeExists(t *testing.T) {
nodeName := "node-name"
dsw.AddNode(nodeName)
podName := "pod-name"
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
generatedVolumeName, _ := dsw.AddPod(podName, volumeSpec, nodeName)
// Act
@@ -709,9 +741,12 @@ func Test_VolumeExists_Positive_VolumeExistsNodeExists(t *testing.T) {
t.Fatalf("len(volumesToAttach) Expected: <1> Actual: <%v>", len(volumesToAttach))
}
- verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolumeName, volumeName)
+ verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolumeName, string(volumeName))
}
+// Populates data struct with new pod/volume1/node.
+// Calls VolumeExists() on that volume2/node.
+// Verifies volume2/node does not exist, and one volume to attach.
func Test_VolumeExists_Positive_VolumeDoesntExistNodeExists(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
@@ -719,8 +754,8 @@ func Test_VolumeExists_Positive_VolumeDoesntExistNodeExists(t *testing.T) {
nodeName := "node-name"
dsw.AddNode(nodeName)
podName := "pod-name"
- volume1Name := "volume1-name"
- volume1Spec := controllervolumetesting.GetTestVolumeSpec(volume1Name, volume1Name)
+ volume1Name := api.UniqueDeviceName("volume1-name")
+ volume1Spec := controllervolumetesting.GetTestVolumeSpec(string(volume1Name), volume1Name)
generatedVolume1Name, podAddErr := dsw.AddPod(podName, volume1Spec, nodeName)
if podAddErr != nil {
t.Fatalf(
@@ -728,7 +763,7 @@ func Test_VolumeExists_Positive_VolumeDoesntExistNodeExists(t *testing.T) {
podName,
podAddErr)
}
- volume2Name := "volume2-name"
+ volume2Name := api.UniqueDeviceName("volume2-name")
// Act
volumeExists := dsw.VolumeExists(volume2Name, nodeName)
@@ -743,15 +778,17 @@ func Test_VolumeExists_Positive_VolumeDoesntExistNodeExists(t *testing.T) {
t.Fatalf("len(volumesToAttach) Expected: <1> Actual: <%v>", len(volumesToAttach))
}
- verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolume1Name, volume1Name)
+ verifyVolumeToAttach(t, volumesToAttach, nodeName, generatedVolume1Name, string(volume1Name))
}
+// Calls VolumeExists() on some volume/node.
+// Verifies volume/node do not exist, and zero volumes to attach.
func Test_VolumeExists_Positive_VolumeDoesntExistNodeDoesntExists(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
dsw := NewDesiredStateOfWorld(volumePluginMgr)
nodeName := "node-name"
- volumeName := "volume-name"
+ volumeName := api.UniqueDeviceName("volume-name")
// Act
volumeExists := dsw.VolumeExists(volumeName, nodeName)
@@ -767,6 +804,8 @@ func Test_VolumeExists_Positive_VolumeDoesntExistNodeDoesntExists(t *testing.T)
}
}
+// Calls GetVolumesToAttach()
+// Verifies zero volumes to attach.
func Test_GetVolumesToAttach_Positive_NoNodes(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
@@ -781,6 +820,9 @@ func Test_GetVolumesToAttach_Positive_NoNodes(t *testing.T) {
}
}
+// Populates data struct with two nodes.
+// Calls GetVolumesToAttach()
+// Verifies zero volumes to attach.
func Test_GetVolumesToAttach_Positive_TwoNodes(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
@@ -799,14 +841,17 @@ func Test_GetVolumesToAttach_Positive_TwoNodes(t *testing.T) {
}
}
+// Populates data struct with two nodes with one volume/pod each.
+// Calls GetVolumesToAttach()
+// Verifies two volumes to attach.
func Test_GetVolumesToAttach_Positive_TwoNodesOneVolumeEach(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
dsw := NewDesiredStateOfWorld(volumePluginMgr)
node1Name := "node1-name"
pod1Name := "pod1-name"
- volume1Name := "volume1-name"
- volume1Spec := controllervolumetesting.GetTestVolumeSpec(volume1Name, volume1Name)
+ volume1Name := api.UniqueDeviceName("volume1-name")
+ volume1Spec := controllervolumetesting.GetTestVolumeSpec(string(volume1Name), volume1Name)
dsw.AddNode(node1Name)
generatedVolume1Name, podAddErr := dsw.AddPod(pod1Name, volume1Spec, node1Name)
if podAddErr != nil {
@@ -817,8 +862,8 @@ func Test_GetVolumesToAttach_Positive_TwoNodesOneVolumeEach(t *testing.T) {
}
node2Name := "node2-name"
pod2Name := "pod2-name"
- volume2Name := "volume2-name"
- volume2Spec := controllervolumetesting.GetTestVolumeSpec(volume2Name, volume2Name)
+ volume2Name := api.UniqueDeviceName("volume2-name")
+ volume2Spec := controllervolumetesting.GetTestVolumeSpec(string(volume2Name), volume2Name)
dsw.AddNode(node2Name)
generatedVolume2Name, podAddErr := dsw.AddPod(pod2Name, volume2Spec, node2Name)
if podAddErr != nil {
@@ -836,18 +881,22 @@ func Test_GetVolumesToAttach_Positive_TwoNodesOneVolumeEach(t *testing.T) {
t.Fatalf("len(volumesToAttach) Expected: <2> Actual: <%v>", len(volumesToAttach))
}
- verifyVolumeToAttach(t, volumesToAttach, node1Name, generatedVolume1Name, volume1Name)
- verifyVolumeToAttach(t, volumesToAttach, node2Name, generatedVolume2Name, volume2Name)
+ verifyVolumeToAttach(t, volumesToAttach, node1Name, generatedVolume1Name, string(volume1Name))
+ verifyVolumeToAttach(t, volumesToAttach, node2Name, generatedVolume2Name, string(volume2Name))
}
+// Populates data struct with two nodes with one volume/pod each and an extra
+// pod for the second node/volume pair.
+// Calls GetVolumesToAttach()
+// Verifies two volumes to attach.
func Test_GetVolumesToAttach_Positive_TwoNodesOneVolumeEachExtraPod(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
dsw := NewDesiredStateOfWorld(volumePluginMgr)
node1Name := "node1-name"
pod1Name := "pod1-name"
- volume1Name := "volume1-name"
- volume1Spec := controllervolumetesting.GetTestVolumeSpec(volume1Name, volume1Name)
+ volume1Name := api.UniqueDeviceName("volume1-name")
+ volume1Spec := controllervolumetesting.GetTestVolumeSpec(string(volume1Name), volume1Name)
dsw.AddNode(node1Name)
generatedVolume1Name, podAddErr := dsw.AddPod(pod1Name, volume1Spec, node1Name)
if podAddErr != nil {
@@ -858,8 +907,8 @@ func Test_GetVolumesToAttach_Positive_TwoNodesOneVolumeEachExtraPod(t *testing.T
}
node2Name := "node2-name"
pod2Name := "pod2-name"
- volume2Name := "volume2-name"
- volume2Spec := controllervolumetesting.GetTestVolumeSpec(volume2Name, volume2Name)
+ volume2Name := api.UniqueDeviceName("volume2-name")
+ volume2Spec := controllervolumetesting.GetTestVolumeSpec(string(volume2Name), volume2Name)
dsw.AddNode(node2Name)
generatedVolume2Name, podAddErr := dsw.AddPod(pod2Name, volume2Spec, node2Name)
if podAddErr != nil {
@@ -886,18 +935,22 @@ func Test_GetVolumesToAttach_Positive_TwoNodesOneVolumeEachExtraPod(t *testing.T
t.Fatalf("len(volumesToAttach) Expected: <2> Actual: <%v>", len(volumesToAttach))
}
- verifyVolumeToAttach(t, volumesToAttach, node1Name, generatedVolume1Name, volume1Name)
- verifyVolumeToAttach(t, volumesToAttach, node2Name, generatedVolume2Name, volume2Name)
+ verifyVolumeToAttach(t, volumesToAttach, node1Name, generatedVolume1Name, string(volume1Name))
+ verifyVolumeToAttach(t, volumesToAttach, node2Name, generatedVolume2Name, string(volume2Name))
}
+// Populates data struct with two nodes with one volume/pod on one node and two
+// volume/pod pairs on the other node.
+// Calls GetVolumesToAttach()
+// Verifies three volumes to attach.
func Test_GetVolumesToAttach_Positive_TwoNodesThreeVolumes(t *testing.T) {
// Arrange
volumePluginMgr, _ := controllervolumetesting.GetTestVolumePluginMgr((t))
dsw := NewDesiredStateOfWorld(volumePluginMgr)
node1Name := "node1-name"
pod1Name := "pod1-name"
- volume1Name := "volume1-name"
- volume1Spec := controllervolumetesting.GetTestVolumeSpec(volume1Name, volume1Name)
+ volume1Name := api.UniqueDeviceName("volume1-name")
+ volume1Spec := controllervolumetesting.GetTestVolumeSpec(string(volume1Name), volume1Name)
dsw.AddNode(node1Name)
generatedVolume1Name, podAddErr := dsw.AddPod(pod1Name, volume1Spec, node1Name)
if podAddErr != nil {
@@ -908,8 +961,8 @@ func Test_GetVolumesToAttach_Positive_TwoNodesThreeVolumes(t *testing.T) {
}
node2Name := "node2-name"
pod2aName := "pod2a-name"
- volume2Name := "volume2-name"
- volume2Spec := controllervolumetesting.GetTestVolumeSpec(volume2Name, volume2Name)
+ volume2Name := api.UniqueDeviceName("volume2-name")
+ volume2Spec := controllervolumetesting.GetTestVolumeSpec(string(volume2Name), volume2Name)
dsw.AddNode(node2Name)
generatedVolume2Name1, podAddErr := dsw.AddPod(pod2aName, volume2Spec, node2Name)
if podAddErr != nil {
@@ -933,8 +986,8 @@ func Test_GetVolumesToAttach_Positive_TwoNodesThreeVolumes(t *testing.T) {
generatedVolume2Name2)
}
pod3Name := "pod3-name"
- volume3Name := "volume3-name"
- volume3Spec := controllervolumetesting.GetTestVolumeSpec(volume3Name, volume3Name)
+ volume3Name := api.UniqueDeviceName("volume3-name")
+ volume3Spec := controllervolumetesting.GetTestVolumeSpec(string(volume3Name), volume3Name)
generatedVolume3Name, podAddErr := dsw.AddPod(pod3Name, volume3Spec, node1Name)
if podAddErr != nil {
t.Fatalf(
@@ -951,16 +1004,16 @@ func Test_GetVolumesToAttach_Positive_TwoNodesThreeVolumes(t *testing.T) {
t.Fatalf("len(volumesToAttach) Expected: <3> Actual: <%v>", len(volumesToAttach))
}
- verifyVolumeToAttach(t, volumesToAttach, node1Name, generatedVolume1Name, volume1Name)
- verifyVolumeToAttach(t, volumesToAttach, node2Name, generatedVolume2Name1, volume2Name)
- verifyVolumeToAttach(t, volumesToAttach, node1Name, generatedVolume3Name, volume3Name)
+ verifyVolumeToAttach(t, volumesToAttach, node1Name, generatedVolume1Name, string(volume1Name))
+ verifyVolumeToAttach(t, volumesToAttach, node2Name, generatedVolume2Name1, string(volume2Name))
+ verifyVolumeToAttach(t, volumesToAttach, node1Name, generatedVolume3Name, string(volume3Name))
}
func verifyVolumeToAttach(
t *testing.T,
volumesToAttach []VolumeToAttach,
- expectedNodeName,
- expectedVolumeName,
+ expectedNodeName string,
+ expectedVolumeName api.UniqueDeviceName,
expectedVolumeSpecName string) {
for _, volumeToAttach := range volumesToAttach {
if volumeToAttach.NodeName == expectedNodeName &&
diff --git a/pkg/controller/volume/reconciler/reconciler.go b/pkg/controller/volume/reconciler/reconciler.go
index 09812757e43..7af8da6fe6d 100644
--- a/pkg/controller/volume/reconciler/reconciler.go
+++ b/pkg/controller/volume/reconciler/reconciler.go
@@ -31,7 +31,7 @@ import (
// Reconciler runs a periodic loop to reconcile the desired state of the with
// the actual state of the world by triggering attach detach operations.
type Reconciler interface {
- // Starts running the reconcilation loop which executes periodically, checks
+ // Starts running the reconciliation loop which executes periodically, checks
// if volumes that should be attached are attached and volumes that should
// be detached are detached. If not, it will trigger attach/detach
// operations to rectify.
@@ -40,33 +40,34 @@ type Reconciler interface {
// NewReconciler returns a new instance of Reconciler that waits loopPeriod
// between successive executions.
-// loopPeriod is the ammount of time the reconciler loop waits between
+// loopPeriod is the amount of time the reconciler loop waits between
// successive executions.
-// maxSafeToDetachDuration is the max ammount of time the reconciler will wait
-// for the volume to deatch, after this it will detach the volume anyway
-// assuming the node is unavilable. If during this time the volume becomes used
-// by a new pod, the detach request will be aborted and the timer cleared.
+// maxWaitForUnmountDuration is the max amount of time the reconciler will wait
+// for the volume to be safely unmounted, after this it will detach the volume
+// anyway (to handle crashed/unavailable nodes). If during this time the volume
+// becomes used by a new pod, the detach request will be aborted and the timer
+// cleared.
func NewReconciler(
loopPeriod time.Duration,
- maxSafeToDetachDuration time.Duration,
+ maxWaitForUnmountDuration time.Duration,
desiredStateOfWorld cache.DesiredStateOfWorld,
actualStateOfWorld cache.ActualStateOfWorld,
attacherDetacher attacherdetacher.AttacherDetacher) Reconciler {
return &reconciler{
- loopPeriod: loopPeriod,
- maxSafeToDetachDuration: maxSafeToDetachDuration,
- desiredStateOfWorld: desiredStateOfWorld,
- actualStateOfWorld: actualStateOfWorld,
- attacherDetacher: attacherDetacher,
+ loopPeriod: loopPeriod,
+ maxWaitForUnmountDuration: maxWaitForUnmountDuration,
+ desiredStateOfWorld: desiredStateOfWorld,
+ actualStateOfWorld: actualStateOfWorld,
+ attacherDetacher: attacherDetacher,
}
}
type reconciler struct {
- loopPeriod time.Duration
- maxSafeToDetachDuration time.Duration
- desiredStateOfWorld cache.DesiredStateOfWorld
- actualStateOfWorld cache.ActualStateOfWorld
- attacherDetacher attacherdetacher.AttacherDetacher
+ loopPeriod time.Duration
+ maxWaitForUnmountDuration time.Duration
+ desiredStateOfWorld cache.DesiredStateOfWorld
+ actualStateOfWorld cache.ActualStateOfWorld
+ attacherDetacher attacherdetacher.AttacherDetacher
}
func (rc *reconciler) Run(stopCh <-chan struct{}) {
@@ -75,11 +76,42 @@ func (rc *reconciler) Run(stopCh <-chan struct{}) {
func (rc *reconciler) reconciliationLoopFunc() func() {
return func() {
+ // Detaches are triggered before attaches so that volumes referenced by
+ // pods that are rescheduled to a different node are detached first.
+
+ // Ensure volumes that should be detached are detached.
+ for _, attachedVolume := range rc.actualStateOfWorld.GetAttachedVolumes() {
+ if !rc.desiredStateOfWorld.VolumeExists(
+ attachedVolume.VolumeName, attachedVolume.NodeName) {
+ // Volume exists in actual state of world but not desired
+ if !attachedVolume.MountedByNode {
+ glog.V(5).Infof("Attempting to start DetachVolume for volume %q to node %q", attachedVolume.VolumeName, attachedVolume.NodeName)
+ err := rc.attacherDetacher.DetachVolume(attachedVolume, rc.actualStateOfWorld)
+ if err == nil {
+ glog.Infof("Started DetachVolume for volume %q to node %q", attachedVolume.VolumeName, attachedVolume.NodeName)
+ }
+ } else {
+ // If volume is not safe to detach (is mounted) wait a max amount of time before detaching any way.
+ timeElapsed, err := rc.actualStateOfWorld.MarkDesireToDetach(attachedVolume.VolumeName, attachedVolume.NodeName)
+ if err != nil {
+ glog.Errorf("Unexpected error actualStateOfWorld.MarkDesireToDetach(): %v", err)
+ }
+ if timeElapsed > rc.maxWaitForUnmountDuration {
+ glog.V(5).Infof("Attempting to start DetachVolume for volume %q to node %q. Volume is not safe to detach, but maxWaitForUnmountDuration expired.", attachedVolume.VolumeName, attachedVolume.NodeName)
+ err := rc.attacherDetacher.DetachVolume(attachedVolume, rc.actualStateOfWorld)
+ if err == nil {
+ glog.Infof("Started DetachVolume for volume %q to node %q due to maxWaitForUnmountDuration expiry.", attachedVolume.VolumeName, attachedVolume.NodeName)
+ }
+ }
+ }
+ }
+ }
+
// Ensure volumes that should be attached are attached.
for _, volumeToAttach := range rc.desiredStateOfWorld.GetVolumesToAttach() {
if rc.actualStateOfWorld.VolumeNodeExists(
volumeToAttach.VolumeName, volumeToAttach.NodeName) {
- // Volume/Node exists, touch it to reset "safe to detach"
+ // Volume/Node exists, touch it to reset detachRequestedTime
glog.V(12).Infof("Volume %q/Node %q is attached--touching.", volumeToAttach.VolumeName, volumeToAttach.NodeName)
_, err := rc.actualStateOfWorld.AddVolumeNode(
volumeToAttach.VolumeSpec, volumeToAttach.NodeName)
@@ -88,29 +120,10 @@ func (rc *reconciler) reconciliationLoopFunc() func() {
}
} else {
// Volume/Node doesn't exist, spawn a goroutine to attach it
- glog.V(5).Infof("Triggering AttachVolume for volume %q to node %q", volumeToAttach.VolumeName, volumeToAttach.NodeName)
- rc.attacherDetacher.AttachVolume(&volumeToAttach, rc.actualStateOfWorld)
- }
- }
-
- // Ensure volumes that should be detached are detached.
- for _, attachedVolume := range rc.actualStateOfWorld.GetAttachedVolumes() {
- if !rc.desiredStateOfWorld.VolumeExists(
- attachedVolume.VolumeName, attachedVolume.NodeName) {
- // Volume exists in actual state of world but not desired
- if attachedVolume.SafeToDetach {
- glog.V(5).Infof("Triggering DetachVolume for volume %q to node %q", attachedVolume.VolumeName, attachedVolume.NodeName)
- rc.attacherDetacher.DetachVolume(&attachedVolume, rc.actualStateOfWorld)
- } else {
- // If volume is not safe to detach wait a max amount of time before detaching any way.
- timeElapsed, err := rc.actualStateOfWorld.MarkDesireToDetach(attachedVolume.VolumeName, attachedVolume.NodeName)
- if err != nil {
- glog.Errorf("Unexpected error actualStateOfWorld.MarkDesireToDetach(): %v", err)
- }
- if timeElapsed > rc.maxSafeToDetachDuration {
- glog.V(5).Infof("Triggering DetachVolume for volume %q to node %q. Volume is not safe to detach, but max wait time expired.", attachedVolume.VolumeName, attachedVolume.NodeName)
- rc.attacherDetacher.DetachVolume(&attachedVolume, rc.actualStateOfWorld)
- }
+ glog.V(5).Infof("Attempting to start AttachVolume for volume %q to node %q", volumeToAttach.VolumeName, volumeToAttach.NodeName)
+ err := rc.attacherDetacher.AttachVolume(volumeToAttach, rc.actualStateOfWorld)
+ if err == nil {
+ glog.Infof("Started AttachVolume for volume %q to node %q", volumeToAttach.VolumeName, volumeToAttach.NodeName)
}
}
}
diff --git a/pkg/controller/volume/reconciler/reconciler_test.go b/pkg/controller/volume/reconciler/reconciler_test.go
index c0c513681c2..942a708ccc0 100644
--- a/pkg/controller/volume/reconciler/reconciler_test.go
+++ b/pkg/controller/volume/reconciler/reconciler_test.go
@@ -20,6 +20,7 @@ import (
"testing"
"time"
+ "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/controller/volume/attacherdetacher"
"k8s.io/kubernetes/pkg/controller/volume/cache"
controllervolumetesting "k8s.io/kubernetes/pkg/controller/volume/testing"
@@ -28,10 +29,12 @@ import (
)
const (
- reconcilerLoopPeriod time.Duration = 0 * time.Millisecond
- maxSafeToDetachDuration time.Duration = 50 * time.Millisecond
+ reconcilerLoopPeriod time.Duration = 0 * time.Millisecond
+ maxWaitForUnmountDuration time.Duration = 50 * time.Millisecond
)
+// Calls Run()
+// Verifies there are no calls to attach or detach.
func Test_Run_Positive_DoNothing(t *testing.T) {
// Arrange
volumePluginMgr, fakePlugin := controllervolumetesting.GetTestVolumePluginMgr((t))
@@ -39,7 +42,7 @@ func Test_Run_Positive_DoNothing(t *testing.T) {
asw := cache.NewActualStateOfWorld(volumePluginMgr)
ad := attacherdetacher.NewAttacherDetacher(volumePluginMgr)
reconciler := NewReconciler(
- reconcilerLoopPeriod, maxSafeToDetachDuration, dsw, asw, ad)
+ reconcilerLoopPeriod, maxWaitForUnmountDuration, dsw, asw, ad)
// Act
go reconciler.Run(wait.NeverStop)
@@ -52,6 +55,9 @@ func Test_Run_Positive_DoNothing(t *testing.T) {
waitForDetachCallCount(t, 0 /* expectedDetachCallCount */, fakePlugin)
}
+// Populates desiredStateOfWorld cache with one node/volume/pod tuple.
+// Calls Run()
+// Verifies there is one attach call and no detach calls.
func Test_Run_Positive_OneDesiredVolumeAttach(t *testing.T) {
// Arrange
volumePluginMgr, fakePlugin := controllervolumetesting.GetTestVolumePluginMgr((t))
@@ -59,10 +65,10 @@ func Test_Run_Positive_OneDesiredVolumeAttach(t *testing.T) {
asw := cache.NewActualStateOfWorld(volumePluginMgr)
ad := attacherdetacher.NewAttacherDetacher(volumePluginMgr)
reconciler := NewReconciler(
- reconcilerLoopPeriod, maxSafeToDetachDuration, dsw, asw, ad)
+ reconcilerLoopPeriod, maxWaitForUnmountDuration, dsw, asw, ad)
podName := "pod-name"
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
dsw.AddNode(nodeName)
volumeExists := dsw.VolumeExists(volumeName, nodeName)
@@ -87,17 +93,23 @@ func Test_Run_Positive_OneDesiredVolumeAttach(t *testing.T) {
verifyNewDetacherCallCount(t, true /* expectZeroNewDetacherCallCount */, fakePlugin)
}
-func Test_Run_Positive_OneDesiredVolumeAttachThenDetachWithMarkVolume(t *testing.T) {
+// Populates desiredStateOfWorld cache with one node/volume/pod tuple.
+// Calls Run()
+// Verifies there is one attach call and no detach calls.
+// Marks the node/volume as unmounted.
+// Deletes the node/volume/pod tuple from desiredStateOfWorld cache.
+// Verifies there is one detach call and no (new) attach calls.
+func Test_Run_Positive_OneDesiredVolumeAttachThenDetachWithUnmountedVolume(t *testing.T) {
// Arrange
volumePluginMgr, fakePlugin := controllervolumetesting.GetTestVolumePluginMgr((t))
dsw := cache.NewDesiredStateOfWorld(volumePluginMgr)
asw := cache.NewActualStateOfWorld(volumePluginMgr)
ad := attacherdetacher.NewAttacherDetacher(volumePluginMgr)
reconciler := NewReconciler(
- reconcilerLoopPeriod, maxSafeToDetachDuration, dsw, asw, ad)
+ reconcilerLoopPeriod, maxWaitForUnmountDuration, dsw, asw, ad)
podName := "pod-name"
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
dsw.AddNode(nodeName)
volumeExists := dsw.VolumeExists(volumeName, nodeName)
@@ -133,9 +145,10 @@ func Test_Run_Positive_OneDesiredVolumeAttachThenDetachWithMarkVolume(t *testing
generatedVolumeName,
nodeName)
}
- asw.MarkVolumeNodeSafeToDetach(generatedVolumeName, nodeName)
+ asw.SetVolumeMountedByNode(generatedVolumeName, nodeName, true /* mounted */)
+ asw.SetVolumeMountedByNode(generatedVolumeName, nodeName, false /* mounted */)
- // Assert -- Marked SafeToDetach
+ // Assert
waitForNewDetacherCallCount(t, 1 /* expectedCallCount */, fakePlugin)
verifyNewAttacherCallCount(t, false /* expectZeroNewAttacherCallCount */, fakePlugin)
waitForAttachCallCount(t, 1 /* expectedAttachCallCount */, fakePlugin)
@@ -143,17 +156,22 @@ func Test_Run_Positive_OneDesiredVolumeAttachThenDetachWithMarkVolume(t *testing
waitForDetachCallCount(t, 1 /* expectedDetachCallCount */, fakePlugin)
}
-func Test_Run_Positive_OneDesiredVolumeAttachThenDetachWithoutMarkVolume(t *testing.T) {
+// Populates desiredStateOfWorld cache with one node/volume/pod tuple.
+// Calls Run()
+// Verifies there is one attach call and no detach calls.
+// Deletes the node/volume/pod tuple from desiredStateOfWorld cache without first marking the node/volume as unmounted.
+// Verifies there is one detach call and no (new) attach calls.
+func Test_Run_Positive_OneDesiredVolumeAttachThenDetachWithMountedVolume(t *testing.T) {
// Arrange
volumePluginMgr, fakePlugin := controllervolumetesting.GetTestVolumePluginMgr((t))
dsw := cache.NewDesiredStateOfWorld(volumePluginMgr)
asw := cache.NewActualStateOfWorld(volumePluginMgr)
ad := attacherdetacher.NewAttacherDetacher(volumePluginMgr)
reconciler := NewReconciler(
- reconcilerLoopPeriod, maxSafeToDetachDuration, dsw, asw, ad)
+ reconcilerLoopPeriod, maxWaitForUnmountDuration, dsw, asw, ad)
podName := "pod-name"
- volumeName := "volume-name"
- volumeSpec := controllervolumetesting.GetTestVolumeSpec(volumeName, volumeName)
+ volumeName := api.UniqueDeviceName("volume-name")
+ volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
nodeName := "node-name"
dsw.AddNode(nodeName)
volumeExists := dsw.VolumeExists(volumeName, nodeName)
diff --git a/pkg/controller/volume/testing/testvolumespec.go b/pkg/controller/volume/testing/testvolumespec.go
index f6678890501..3b85f4910a3 100644
--- a/pkg/controller/volume/testing/testvolumespec.go
+++ b/pkg/controller/volume/testing/testvolumespec.go
@@ -22,13 +22,13 @@ import (
)
// GetTestVolumeSpec returns a test volume spec
-func GetTestVolumeSpec(volumeName, diskName string) *volume.Spec {
+func GetTestVolumeSpec(volumeName string, diskName api.UniqueDeviceName) *volume.Spec {
return &volume.Spec{
Volume: &api.Volume{
Name: volumeName,
VolumeSource: api.VolumeSource{
GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{
- PDName: diskName,
+ PDName: string(diskName),
FSType: "fake",
ReadOnly: false,
},
diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go
index 167831cb0ee..2e14419f971 100644
--- a/pkg/kubelet/kubelet.go
+++ b/pkg/kubelet/kubelet.go
@@ -92,6 +92,7 @@ import (
"k8s.io/kubernetes/pkg/util/wait"
"k8s.io/kubernetes/pkg/version"
"k8s.io/kubernetes/pkg/volume"
+ attachdetachutil "k8s.io/kubernetes/pkg/volume/util/attachdetach"
"k8s.io/kubernetes/pkg/watch"
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/predicates"
"k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache"
@@ -237,6 +238,7 @@ func NewMainKubelet(
babysitDaemons bool,
evictionConfig eviction.Config,
kubeOptions []Option,
+ enableControllerAttachDetach bool,
) (*Kubelet, error) {
if rootDirectory == "" {
return nil, fmt.Errorf("invalid root directory %q", rootDirectory)
@@ -360,6 +362,7 @@ func NewMainKubelet(
reservation: reservation,
enableCustomMetrics: enableCustomMetrics,
babysitDaemons: babysitDaemons,
+ enableControllerAttachDetach: enableControllerAttachDetach,
}
if klet.flannelExperimentalOverlay {
@@ -823,6 +826,11 @@ type Kubelet struct {
// the number of allowed pods per core
podsPerCore int
+
+ // enableControllerAttachDetach indicates the Attach/Detach controller
+ // should manage attachment/detachment of volumes scheduled to this node,
+ // and disable kubelet from executing any attach/detach operations
+ enableControllerAttachDetach bool
}
// Validate given node IP belongs to the current host
@@ -1030,6 +1038,14 @@ func (kl *Kubelet) initialNodeStatus() (*api.Node, error) {
})
}
+ if kl.enableControllerAttachDetach {
+ if node.Annotations == nil {
+ node.Annotations = make(map[string]string)
+ }
+
+ node.Annotations[attachdetachutil.ControllerManagedAnnotation] = "true"
+ }
+
// @question: should this be place after the call to the cloud provider? which also applies labels
for k, v := range kl.nodeLabels {
if cv, found := node.ObjectMeta.Labels[k]; found {
@@ -2110,7 +2126,11 @@ func (kl *Kubelet) cleanupOrphanedVolumes(pods []*api.Pod, runningPods []*kubeco
// volume is unmounted. some volumes also require detachment from the node.
if cleaner.Detacher != nil && len(refs) == 1 {
-
+ // There is a bug in this code, where len(refs) is zero in some
+ // cases, and so RemoveVolumeInUse sometimes never gets called.
+ // The Attach/Detach Refactor should fix this, in the mean time,
+ // the controller timeout for safe mount is set to 3 minutes, so
+ // it will still detach the volume.
detacher := *cleaner.Detacher
devicePath, _, err := mount.GetDeviceNameFromMount(kl.mounter, refs[0])
if err != nil {
@@ -2122,9 +2142,19 @@ func (kl *Kubelet) cleanupOrphanedVolumes(pods []*api.Pod, runningPods []*kubeco
}
pdName := path.Base(refs[0])
- err = detacher.Detach(pdName, kl.hostname)
- if err != nil {
- glog.Errorf("Could not detach volume %q at %q: %v", name, volumePath, err)
+ if kl.enableControllerAttachDetach {
+ // Attach/Detach controller is enabled and this volume type
+ // implments a detacher
+ uniqueDeviceName := attachdetachutil.GetUniqueDeviceName(
+ cleaner.PluginName, pdName)
+ kl.volumeManager.RemoveVolumeInUse(
+ api.UniqueDeviceName(uniqueDeviceName))
+ } else {
+ // Attach/Detach controller is disabled
+ err = detacher.Detach(pdName, kl.hostname)
+ if err != nil {
+ glog.Errorf("Could not detach volume %q at %q: %v", name, volumePath, err)
+ }
}
go func() {
@@ -3397,6 +3427,11 @@ func (kl *Kubelet) recordNodeSchedulableEvent(node *api.Node) {
}
}
+// Update VolumesInUse field in Node Status
+func (kl *Kubelet) setNodeVolumesInUseStatus(node *api.Node) {
+ node.Status.VolumesInUse = kl.volumeManager.GetVolumesInUse()
+}
+
// setNodeStatus fills in the Status fields of the given Node, overwriting
// any fields that are currently set.
// TODO(madhusudancs): Simplify the logic for setting node conditions and
@@ -3425,6 +3460,7 @@ func (kl *Kubelet) defaultNodeStatusFuncs() []func(*api.Node) error {
withoutError(kl.setNodeOODCondition),
withoutError(kl.setNodeMemoryPressureCondition),
withoutError(kl.setNodeReadyCondition),
+ withoutError(kl.setNodeVolumesInUseStatus),
withoutError(kl.recordNodeSchedulableEvent),
}
}
@@ -3446,6 +3482,7 @@ func (kl *Kubelet) tryUpdateNodeStatus() error {
if node == nil {
return fmt.Errorf("no node instance returned for %q", kl.nodeName)
}
+
// Flannel is the authoritative source of pod CIDR, if it's running.
// This is a short term compromise till we get flannel working in
// reservation mode.
diff --git a/pkg/kubelet/volume_manager.go b/pkg/kubelet/volume_manager.go
index d7243251889..432f1d8ba02 100644
--- a/pkg/kubelet/volume_manager.go
+++ b/pkg/kubelet/volume_manager.go
@@ -19,6 +19,7 @@ package kubelet
import (
"sync"
+ "k8s.io/kubernetes/pkg/api"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/types"
)
@@ -26,14 +27,19 @@ import (
// volumeManager manages the volumes for the pods running on the kubelet.
// Currently it only does book keeping, but it can be expanded to
// take care of the volumePlugins.
+// TODO(saad-ali): note that volumeManager will be completley refactored as part
+// of mount/unmount refactor.
type volumeManager struct {
- lock sync.RWMutex
- volumeMaps map[types.UID]kubecontainer.VolumeMap
+ lock sync.RWMutex
+ volumeMaps map[types.UID]kubecontainer.VolumeMap
+ volumesInUse []api.UniqueDeviceName
}
func newVolumeManager() *volumeManager {
- vm := &volumeManager{}
- vm.volumeMaps = make(map[types.UID]kubecontainer.VolumeMap)
+ vm := &volumeManager{
+ volumeMaps: make(map[types.UID]kubecontainer.VolumeMap),
+ volumesInUse: []api.UniqueDeviceName{},
+ }
return vm
}
@@ -60,3 +66,38 @@ func (vm *volumeManager) DeleteVolumes(podUID types.UID) {
defer vm.lock.Unlock()
delete(vm.volumeMaps, podUID)
}
+
+// AddVolumeInUse adds specified volume to volumesInUse list, if it doesn't
+// already exist
+func (vm *volumeManager) AddVolumeInUse(uniqueDeviceName api.UniqueDeviceName) {
+ vm.lock.Lock()
+ defer vm.lock.Unlock()
+ for _, volume := range vm.volumesInUse {
+ if volume == uniqueDeviceName {
+ // Volume already exists in list
+ return
+ }
+ }
+
+ vm.volumesInUse = append(vm.volumesInUse, uniqueDeviceName)
+}
+
+// RemoveVolumeInUse removes the specified volume from volumesInUse list, if it
+// exists
+func (vm *volumeManager) RemoveVolumeInUse(uniqueDeviceName api.UniqueDeviceName) {
+ vm.lock.Lock()
+ defer vm.lock.Unlock()
+ for i := len(vm.volumesInUse) - 1; i >= 0; i-- {
+ if vm.volumesInUse[i] == uniqueDeviceName {
+ // Volume exists, remove it
+ vm.volumesInUse = append(vm.volumesInUse[:i], vm.volumesInUse[i+1:]...)
+ }
+ }
+}
+
+// GetVolumesInUse returns the volumesInUse list
+func (vm *volumeManager) GetVolumesInUse() []api.UniqueDeviceName {
+ vm.lock.RLock()
+ defer vm.lock.RUnlock()
+ return vm.volumesInUse
+}
diff --git a/pkg/kubelet/volumes.go b/pkg/kubelet/volumes.go
index 3651043fee0..3d0427aa3ae 100644
--- a/pkg/kubelet/volumes.go
+++ b/pkg/kubelet/volumes.go
@@ -34,6 +34,7 @@ import (
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/util/strings"
"k8s.io/kubernetes/pkg/volume"
+ "k8s.io/kubernetes/pkg/volume/util/attachdetach"
)
const (
@@ -155,7 +156,7 @@ func (kl *Kubelet) mountExternalVolumes(pod *api.Pod) (kubecontainer.VolumeMap,
// some volumes require attachment before mounter's setup.
// The plugin can be nil, but non-nil errors are legitimate errors.
// For non-nil plugins, Attachment to a node is required before Mounter's setup.
- attacher, err := kl.newVolumeAttacherFromPlugins(volSpec, pod)
+ attacher, attachablePlugin, err := kl.newVolumeAttacherFromPlugins(volSpec, pod)
if err != nil {
glog.Errorf("Could not create volume attacher for pod %s: %v", pod.UID, err)
return nil, err
@@ -169,9 +170,11 @@ func (kl *Kubelet) mountExternalVolumes(pod *api.Pod) (kubecontainer.VolumeMap,
return nil, err
}
if notMountPoint {
- err = attacher.Attach(volSpec, kl.hostname)
- if err != nil {
- return nil, err
+ if !kl.enableControllerAttachDetach {
+ err = attacher.Attach(volSpec, kl.hostname)
+ if err != nil {
+ return nil, err
+ }
}
devicePath, err := attacher.WaitForAttach(volSpec, maxWaitForVolumeOps)
@@ -179,6 +182,18 @@ func (kl *Kubelet) mountExternalVolumes(pod *api.Pod) (kubecontainer.VolumeMap,
return nil, err
}
+ if kl.enableControllerAttachDetach {
+ // Attach/Detach controller is enabled and this volume type
+ // implements an attacher
+ uniqueDeviceName, err := attachdetach.GetUniqueDeviceNameFromSpec(
+ attachablePlugin, volSpec)
+ if err != nil {
+ return nil, err
+ }
+ kl.volumeManager.AddVolumeInUse(
+ api.UniqueDeviceName(uniqueDeviceName))
+ }
+
if err = attacher.MountDevice(volSpec, devicePath, deviceMountPath, kl.mounter); err != nil {
return nil, err
}
@@ -245,8 +260,9 @@ func (kl *Kubelet) getPodVolumes(podUID types.UID) ([]*volumeTuple, error) {
// cleaner is a union struct to allow separating detaching from the cleaner.
// some volumes require detachment but not all. Unmounter cannot be nil but Detacher is optional.
type cleaner struct {
- Unmounter volume.Unmounter
- Detacher *volume.Detacher
+ PluginName string
+ Unmounter volume.Unmounter
+ Detacher *volume.Detacher
}
// getPodVolumesFromDisk examines directory structure to determine volumes that
@@ -274,13 +290,13 @@ func (kl *Kubelet) getPodVolumesFromDisk() map[string]cleaner {
// or volume objects.
// Try to use a plugin for this volume.
- unmounter, err := kl.newVolumeUnmounterFromPlugins(volume.Kind, volume.Name, podUID)
+ unmounter, pluginName, err := kl.newVolumeUnmounterFromPlugins(volume.Kind, volume.Name, podUID)
if err != nil {
glog.Errorf("Could not create volume unmounter for %s: %v", volume.Name, err)
continue
}
- tuple := cleaner{Unmounter: unmounter}
+ tuple := cleaner{PluginName: pluginName, Unmounter: unmounter}
detacher, err := kl.newVolumeDetacherFromPlugins(volume.Kind, volume.Name, podUID)
// plugin can be nil but a non-nil error is a legitimate error
if err != nil {
@@ -359,52 +375,52 @@ func (kl *Kubelet) newVolumeMounterFromPlugins(spec *volume.Spec, pod *api.Pod,
if err != nil {
return nil, fmt.Errorf("failed to instantiate mounter for volume: %s using plugin: %s with a root cause: %v", spec.Name(), plugin.Name(), err)
}
- glog.V(10).Infof("Used volume plugin %q to mount %s", plugin.Name(), spec.Name())
+ glog.V(10).Infof("Using volume plugin %q to mount %s", plugin.Name(), spec.Name())
return physicalMounter, nil
}
// newVolumeAttacherFromPlugins attempts to find a plugin from a volume spec
// and then create an Attacher.
// Returns:
-// - an attacher if one exists
+// - an attacher if one exists, nil otherwise
+// - the AttachableVolumePlugin if attacher exists, nil otherewise
// - an error if no plugin was found for the volume
-// or the attacher failed to instantiate
-// - nil if there is no appropriate attacher for this volume
-func (kl *Kubelet) newVolumeAttacherFromPlugins(spec *volume.Spec, pod *api.Pod) (volume.Attacher, error) {
+// or the attacher failed to instantiate, nil otherwise
+func (kl *Kubelet) newVolumeAttacherFromPlugins(spec *volume.Spec, pod *api.Pod) (volume.Attacher, volume.AttachableVolumePlugin, error) {
plugin, err := kl.volumePluginMgr.FindAttachablePluginBySpec(spec)
if err != nil {
- return nil, fmt.Errorf("can't use volume plugins for %s: %v", spec.Name(), err)
+ return nil, nil, fmt.Errorf("can't use volume plugins for %s: %v", spec.Name(), err)
}
if plugin == nil {
// Not found but not an error.
- return nil, nil
+ return nil, nil, nil
}
attacher, err := plugin.NewAttacher()
if err != nil {
- return nil, fmt.Errorf("failed to instantiate volume attacher for %s: %v", spec.Name(), err)
+ return nil, nil, fmt.Errorf("failed to instantiate volume attacher for %s: %v", spec.Name(), err)
}
- glog.V(3).Infof("Used volume plugin %q to attach %s/%s", plugin.Name(), spec.Name())
- return attacher, nil
+ glog.V(3).Infof("Using volume plugin %q to attach %s/%s", plugin.Name(), spec.Name())
+ return attacher, plugin, nil
}
// newVolumeUnmounterFromPlugins attempts to find a plugin by name and then
// create an Unmounter.
// Returns a valid Unmounter or an error.
-func (kl *Kubelet) newVolumeUnmounterFromPlugins(kind string, name string, podUID types.UID) (volume.Unmounter, error) {
+func (kl *Kubelet) newVolumeUnmounterFromPlugins(kind string, name string, podUID types.UID) (volume.Unmounter, string, error) {
plugName := strings.UnescapeQualifiedNameForDisk(kind)
plugin, err := kl.volumePluginMgr.FindPluginByName(plugName)
if err != nil {
// TODO: Maybe we should launch a cleanup of this dir?
- return nil, fmt.Errorf("can't use volume plugins for %s/%s: %v", podUID, kind, err)
+ return nil, "", fmt.Errorf("can't use volume plugins for %s/%s: %v", podUID, kind, err)
}
unmounter, err := plugin.NewUnmounter(name, podUID)
if err != nil {
- return nil, fmt.Errorf("failed to instantiate volume plugin for %s/%s: %v", podUID, kind, err)
+ return nil, "", fmt.Errorf("failed to instantiate volume plugin for %s/%s: %v", podUID, kind, err)
}
- glog.V(5).Infof("Used volume plugin %q to unmount %s/%s", plugin.Name(), podUID, kind)
- return unmounter, nil
+ glog.V(5).Infof("Using volume plugin %q to unmount %s/%s", plugin.Name(), podUID, kind)
+ return unmounter, plugin.Name(), nil
}
// newVolumeDetacherFromPlugins attempts to find a plugin by a name and then
diff --git a/pkg/volume/gce_pd/attacher.go b/pkg/volume/gce_pd/attacher.go
index 9a58175336b..060ace5528a 100644
--- a/pkg/volume/gce_pd/attacher.go
+++ b/pkg/volume/gce_pd/attacher.go
@@ -26,7 +26,6 @@ import (
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/util/exec"
- "k8s.io/kubernetes/pkg/util/keymutex"
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/volume"
@@ -40,25 +39,10 @@ var _ volume.Attacher = &gcePersistentDiskAttacher{}
var _ volume.AttachableVolumePlugin = &gcePersistentDiskPlugin{}
-// Singleton key mutex for keeping attach/detach operations for the
-// same PD atomic
-// TODO(swagiaal): Once the Mount/Unmount manager is implemented this
-// will no longer be needed and should be removed.
-var attachDetachMutex = keymutex.NewKeyMutex()
-
func (plugin *gcePersistentDiskPlugin) NewAttacher() (volume.Attacher, error) {
return &gcePersistentDiskAttacher{host: plugin.host}, nil
}
-func (plugin *gcePersistentDiskPlugin) GetUniqueVolumeName(spec *volume.Spec) (string, error) {
- volumeSource, _ := getVolumeSource(spec)
- if volumeSource == nil {
- return "", fmt.Errorf("Spec does not reference a GCE volume type")
- }
-
- return fmt.Sprintf("%s/%s:%v", gcePersistentDiskPluginName, volumeSource.PDName, volumeSource.ReadOnly), nil
-}
-
func (plugin *gcePersistentDiskPlugin) GetDeviceName(spec *volume.Spec) (string, error) {
volumeSource, _ := getVolumeSource(spec)
if volumeSource == nil {
@@ -68,34 +52,42 @@ func (plugin *gcePersistentDiskPlugin) GetDeviceName(spec *volume.Spec) (string,
return volumeSource.PDName, nil
}
+// Attach checks with the GCE cloud provider if the specified volume is already
+// attached to the specified node. If the volume is attached, it succeeds
+// (returns nil). If it is not, Attach issues a call to the GCE cloud provider
+// to attach it.
+// Callers are responsible for retryinging on failure.
+// Callers are responsible for thread safety between concurrent attach and
+// detach operations.
func (attacher *gcePersistentDiskAttacher) Attach(spec *volume.Spec, hostName string) error {
volumeSource, readOnly := getVolumeSource(spec)
pdName := volumeSource.PDName
- // Block execution until any pending detach operations for this PD have completed
- attachDetachMutex.LockKey(pdName)
- defer attachDetachMutex.UnlockKey(pdName)
-
gceCloud, err := getCloudProvider(attacher.host.GetCloudProvider())
if err != nil {
return err
}
- for numRetries := 0; numRetries < maxRetries; numRetries++ {
- if numRetries > 0 {
- glog.Warningf("Retrying attach for GCE PD %q (retry count=%v).", pdName, numRetries)
- }
-
- if err = gceCloud.AttachDisk(pdName, hostName, readOnly); err != nil {
- glog.Errorf("Error attaching PD %q: %+v", pdName, err)
- time.Sleep(errorSleepDuration)
- continue
- }
+ attached, err := gceCloud.DiskIsAttached(pdName, hostName)
+ if err != nil {
+ // Log error and continue with attach
+ glog.Errorf(
+ "Error checking if PD (%q) is already attached to current node (%q). Will continue and try attach anyway. err=%v",
+ pdName, hostName, err)
+ }
+ if attached {
+ // Volume is already attached to node.
+ glog.Infof("Attach operation is successful. PD %q is already attached to node %q.", pdName, hostName)
return nil
}
- return err
+ if err = gceCloud.AttachDisk(pdName, hostName, readOnly); err != nil {
+ glog.Errorf("Error attaching PD %q to node %q: %+v", pdName, hostName, err)
+ return err
+ }
+
+ return nil
}
func (attacher *gcePersistentDiskAttacher) WaitForAttach(spec *volume.Spec, timeout time.Duration) (string, error) {
@@ -183,33 +175,41 @@ func (plugin *gcePersistentDiskPlugin) NewDetacher() (volume.Detacher, error) {
return &gcePersistentDiskDetacher{host: plugin.host}, nil
}
+// Detach checks with the GCE cloud provider if the specified volume is already
+// attached to the specified node. If the volume is not attached, it succeeds
+// (returns nil). If it is attached, Detach issues a call to the GCE cloud
+// provider to attach it.
+// Callers are responsible for retryinging on failure.
+// Callers are responsible for thread safety between concurrent attach and detach
+// operations.
func (detacher *gcePersistentDiskDetacher) Detach(deviceMountPath string, hostName string) error {
pdName := path.Base(deviceMountPath)
- // Block execution until any pending attach/detach operations for this PD have completed
- attachDetachMutex.LockKey(pdName)
- defer attachDetachMutex.UnlockKey(pdName)
-
gceCloud, err := getCloudProvider(detacher.host.GetCloudProvider())
if err != nil {
return err
}
- for numRetries := 0; numRetries < maxRetries; numRetries++ {
- if numRetries > 0 {
- glog.Warningf("Retrying detach for GCE PD %q (retry count=%v).", pdName, numRetries)
- }
-
- if err = gceCloud.DetachDisk(pdName, hostName); err != nil {
- glog.Errorf("Error detaching PD %q: %v", pdName, err)
- time.Sleep(errorSleepDuration)
- continue
- }
+ attached, err := gceCloud.DiskIsAttached(pdName, hostName)
+ if err != nil {
+ // Log error and continue with detach
+ glog.Errorf(
+ "Error checking if PD (%q) is already attached to current node (%q). Will continue and try detach anyway. err=%v",
+ pdName, hostName, err)
+ }
+ if !attached {
+ // Volume is not attached to node. Success!
+ glog.Infof("Detach operation is successful. PD %q was not attached to node %q.", pdName, hostName)
return nil
}
- return err
+ if err = gceCloud.DetachDisk(pdName, hostName); err != nil {
+ glog.Errorf("Error detaching PD %q from node %q: %v", pdName, hostName, err)
+ return err
+ }
+
+ return nil
}
func (detacher *gcePersistentDiskDetacher) WaitForDetach(devicePath string, timeout time.Duration) error {
diff --git a/pkg/volume/plugins.go b/pkg/volume/plugins.go
index b49e4026ce7..6643cc8b994 100644
--- a/pkg/volume/plugins.go
+++ b/pkg/volume/plugins.go
@@ -134,13 +134,6 @@ type AttachableVolumePlugin interface {
NewAttacher() (Attacher, error)
NewDetacher() (Detacher, error)
- // GetUniqueVolumeName returns a unique name representing the volume
- // defined in spec. e.g. pluginname-deviceName-readwrite
- // This helps ensures that the same operation (attach/detach) is never
- // started on the same volume.
- // If the plugin does not support the given spec, this returns an error.
- GetUniqueVolumeName(spec *Spec) (string, error)
-
// GetDeviceName returns the name or ID of the device referenced in the
// specified volume spec. This is passed by callers to the Deatch method.
// If the plugin does not support the given spec, this returns an error.
diff --git a/pkg/volume/testing/testing.go b/pkg/volume/testing/testing.go
index bbe7b80804a..1ad7ca24153 100644
--- a/pkg/volume/testing/testing.go
+++ b/pkg/volume/testing/testing.go
@@ -237,12 +237,6 @@ func (plugin *FakeVolumePlugin) GetNewDetacherCallCount() int {
return plugin.NewDetacherCallCount
}
-func (plugin *FakeVolumePlugin) GetUniqueVolumeName(spec *Spec) (string, error) {
- plugin.RLock()
- defer plugin.RUnlock()
- return plugin.Name() + "/" + spec.Name(), nil
-}
-
func (plugin *FakeVolumePlugin) GetDeviceName(spec *Spec) (string, error) {
return spec.Name(), nil
}
diff --git a/pkg/volume/util/attachdetach/attachdetach.go b/pkg/volume/util/attachdetach/attachdetach.go
new file mode 100644
index 00000000000..1d4a57ac122
--- /dev/null
+++ b/pkg/volume/util/attachdetach/attachdetach.go
@@ -0,0 +1,71 @@
+/*
+Copyright 2016 The Kubernetes Authors All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package attachdetach contains consts and helper methods used by various
+// attach/detach components in controller and kubelet
+package attachdetach
+
+import (
+ "fmt"
+
+ "k8s.io/kubernetes/pkg/api"
+ "k8s.io/kubernetes/pkg/volume"
+)
+
+const (
+ // ControllerManagedAnnotation is the key of the annotation on Node objects
+ // that indicates attach/detach operations for the node should be managed
+ // by the attach/detach controller
+ ControllerManagedAnnotation string = "volumes.kubernetes.io/controller-managed-attach-detach"
+)
+
+// GetUniqueDeviceName returns a unique name representing the device with the
+// spcified deviceName of the pluginName volume type.
+// The returned name can be used to uniquely reference the device. For example,
+// to prevent operations (attach/detach) from being triggered on the same volume
+func GetUniqueDeviceName(
+ pluginName, deviceName string) api.UniqueDeviceName {
+ return api.UniqueDeviceName(fmt.Sprintf("%s/%s", pluginName, deviceName))
+}
+
+// GetUniqueDeviceNameFromSpec uses the given AttachableVolumePlugin to
+// generate a unique name representing the device defined in the specified
+// volume spec.
+// This returned name can be used to uniquely reference the device. For example,
+// to prevent operations (attach/detach) from being triggered on the same volume.
+// If the given plugin does not support the volume spec, this returns an error.
+func GetUniqueDeviceNameFromSpec(
+ attachableVolumePlugin volume.AttachableVolumePlugin,
+ volumeSpec *volume.Spec) (api.UniqueDeviceName, error) {
+ if attachableVolumePlugin == nil {
+ return "", fmt.Errorf(
+ "attachablePlugin should not be nil. volumeSpec.Name=%q",
+ volumeSpec.Name())
+ }
+
+ deviceName, err := attachableVolumePlugin.GetDeviceName(volumeSpec)
+ if err != nil || deviceName == "" {
+ return "", fmt.Errorf(
+ "failed to GetDeviceName from AttachablePlugin for volumeSpec %q err=%v",
+ volumeSpec.Name(),
+ err)
+ }
+
+ return GetUniqueDeviceName(
+ attachableVolumePlugin.Name(),
+ deviceName),
+ nil
+}