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.UniqueDeviceName

+

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 +}