Code change:
cloud                 *Cloud } <del> // AttachDisk attaches a vhd to vm. The vhd must exist, can be identified by diskName, diskURI, and lun. func (c *controllerCommon) AttachDisk(isManagedDisk bool, diskName, diskURI string, nodeName types.NodeName, lun int32, cachingMode compute.CachingTypes) error { // 1. vmType is standard, attach with availabilitySet.AttachDisk. </del> <ins> // getNodeVMSet gets the VMSet interface based on config.VMType and the real virtual machine type. func (c *controllerCommon) getNodeVMSet(nodeName types.NodeName) (VMSet, error) { // 1. vmType is standard, return cloud.vmSet directly. </ins> if c.cloud.VMType == vmTypeStandard { <del> return c.cloud.vmSet.AttachDisk(isManagedDisk, diskName, diskURI, nodeName, lun, cachingMode) </del> <ins> return c.cloud.vmSet, nil </ins> } // 2. vmType is Virtual Machine Scale Set (vmss), convert vmSet to scaleSet. ss, ok := c.cloud.vmSet.(*scaleSet) if !ok { <del> return fmt.Errorf("error of converting vmSet (%q) to scaleSet with vmType %q", c.cloud.vmSet, c.cloud.VMType) </del> <ins> return nil, fmt.Errorf("error of converting vmSet (%q) to scaleSet with vmType %q", c.cloud.vmSet, c.cloud.VMType) </ins> } <del> // 3. If the node is managed by availability set, then attach with availabilitySet.AttachDisk. </del> <ins> // 3. If the node is managed by availability set, then return ss.availabilitySet. </ins> managedByAS, err := ss.isNodeManagedByAvailabilitySet(mapNodeNameToVMName(nodeName)) if err != nil { <del> return err </del> <ins> return nil, err </ins> } if managedByAS { // vm is managed by availability set. <del> return ss.availabilitySet.AttachDisk(isManagedDisk, diskName, diskURI, nodeName, lun, cachingMode) </del> <ins> return ss.availabilitySet, nil </ins> } <del> // 4. Node is managed by vmss, attach with scaleSet.AttachDisk. return ss.AttachDisk(isManagedDisk, diskName, diskURI, nodeName, lun, cachingMode) </del> <ins> // 4. Node is managed by vmss return ss, nil </ins> } <del> // DetachDiskByName detaches a vhd from host. The vhd can be identified by diskName or diskURI. func (c *controllerCommon) DetachDiskByName(diskName, diskURI string, nodeName types.NodeName) error { // 1. vmType is standard, detach with availabilitySet.DetachDiskByName. if c.cloud.VMType == vmTypeStandard { return c.cloud.vmSet.DetachDiskByName(diskName, diskURI, nodeName) </del> <ins> // AttachDisk attaches a vhd to vm. The vhd must exist, can be identified by diskName, diskURI, and lun. func (c *controllerCommon) AttachDisk(isManagedDisk bool, diskName, diskURI string, nodeName types.NodeName, lun int32, cachingMode compute.CachingTypes) error { vmset, err := c.getNodeVMSet(nodeName) if err != nil { return err </ins> } <del> // 2. vmType is Virtual Machine Scale Set (vmss), convert vmSet to scaleSet. ss, ok := c.cloud.vmSet.(*scaleSet) if !ok { return fmt.Errorf("error of converting vmSet (%q) to scaleSet with vmType %q", c.cloud.vmSet, c.cloud.VMType) } </del> <ins> return vmset.AttachDisk(isManagedDisk, diskName, diskURI, nodeName, lun, cachingMode) } </ins> <del> // 3. If the node is managed by availability set, then detach with availabilitySet.DetachDiskByName. managedByAS, err := ss.isNodeManagedByAvailabilitySet(mapNodeNameToVMName(nodeName)) </del> <ins> // DetachDiskByName detaches a vhd from host. The vhd can be identified by diskName or diskURI. func (c *controllerCommon) DetachDiskByName(diskName, diskURI string, nodeName types.NodeName) error { vmset, err := c.getNodeVMSet(nodeName) </ins> if err != nil { return err } <del> if managedByAS { // vm is managed by availability set. return ss.availabilitySet.DetachDiskByName(diskName, diskURI, nodeName) } // 4. Node is managed by vmss, detach with scaleSet.DetachDiskByName. return ss.DetachDiskByName(diskName, diskURI, nodeName) </del> <ins> return vmset.DetachDiskByName(diskName, diskURI, nodeName) </ins> } <del> // GetDiskLun finds the lun on the host that the vhd is attached to, given a vhd's diskName and diskURI. func (c *controllerCommon) GetDiskLun(diskName, diskURI string, nodeName types.NodeName) (int32, error) { // 1. vmType is standard, get with availabilitySet.GetDiskLun. if c.cloud.VMType == vmTypeStandard { return c.cloud.vmSet.GetDiskLun(diskName, diskURI, nodeName) </del> <ins> // getNodeDataDisks invokes vmSet interfaces to get data disks for the node. func (c *controllerCommon) getNodeDataDisks(nodeName types.NodeName) ([]compute.DataDisk, error) { vmset, err := c.getNodeVMSet(nodeName) if err != nil { return nil, err </ins> } <del> // 2. vmType is Virtual Machine Scale Set (vmss), convert vmSet to scaleSet. ss, ok := c.cloud.vmSet.(*scaleSet) if !ok { return -1, fmt.Errorf("error of converting vmSet (%q) to scaleSet with vmType %q", c.cloud.vmSet, c.cloud.VMType) } </del> <ins> return vmset.GetDataDisks(nodeName) } </ins> <del> // 3. If the node is managed by availability set, then get with availabilitySet.GetDiskLun. managedByAS, err := ss.isNodeManagedByAvailabilitySet(mapNodeNameToVMName(nodeName)) </del> <ins> // GetDiskLun finds the lun on the host that the vhd is attached to, given a vhd's diskName and diskURI. func (c *controllerCommon) GetDiskLun(diskName, diskURI string, nodeName types.NodeName) (int32, error) { disks, err := c.getNodeDataDisks(nodeName) </ins> if err != nil { <ins> glog.Errorf("error of getting data disks for node %q: %v", nodeName, err) </ins> return -1, err } <del> if managedByAS { // vm is managed by availability set. return ss.availabilitySet.GetDiskLun(diskName, diskURI, nodeName) } // 4. Node is managed by vmss, get with scaleSet.GetDiskLun. return ss.GetDiskLun(diskName, diskURI, nodeName) </del> <ins> for _, disk := range disks { if disk.Lun != nil && (disk.Name != nil && diskName != "" && *disk.Name == diskName) || (disk.Vhd != nil && disk.Vhd.URI != nil && diskURI != "" && *disk.Vhd.URI == diskURI) || (disk.ManagedDisk != nil && *disk.ManagedDisk.ID == diskURI) { // found the disk glog.V(4).Infof("azureDisk - find disk: lun %d name %q uri %q", *disk.Lun, diskName, diskURI) return *disk.Lun, nil } } return -1, fmt.Errorf("Cannot find Lun for disk %s", diskName) </ins> } // GetNextDiskLun searches all vhd attachment on the host and find unused lun. Return -1 if all luns are used. func (c *controllerCommon) GetNextDiskLun(nodeName types.NodeName) (int32, error) { <del> // 1. vmType is standard, get with availabilitySet.GetNextDiskLun. if c.cloud.VMType == vmTypeStandard { return c.cloud.vmSet.GetNextDiskLun(nodeName) } // 2. vmType is Virtual Machine Scale Set (vmss), convert vmSet to scaleSet. ss, ok := c.cloud.vmSet.(*scaleSet) if !ok { return -1, fmt.Errorf("error of converting vmSet (%q) to scaleSet with vmType %q", c.cloud.vmSet, c.cloud.VMType) } // 3. If the node is managed by availability set, then get with availabilitySet.GetNextDiskLun. managedByAS, err := ss.isNodeManagedByAvailabilitySet(mapNodeNameToVMName(nodeName)) </del> <ins> disks, err := c.getNodeDataDisks(nodeName) </ins> if err != nil { <ins> glog.Errorf("error of getting data disks for node %q: %v", nodeName, err) </ins> return -1, err } <del> if managedByAS { // vm is managed by availability set. return ss.availabilitySet.GetNextDiskLun(nodeName) } // 4. Node is managed by vmss, get with scaleSet.GetNextDiskLun. return ss.GetNextDiskLun(nodeName) </del> <ins> used := make([]bool, maxLUN) for _, disk := range disks { if disk.Lun != nil { used[*disk.Lun] = true } } for k, v := range used { if !v { return int32(k), nil } } return -1, fmt.Errorf("all luns are used") </ins> } // DisksAreAttached checks if a list of volumes are attached to the node with the specified NodeName. func (c *controllerCommon) DisksAreAttached(diskNames []string, nodeName types.NodeName) (map[string]bool, error) { <del> // 1. vmType is standard, check with availabilitySet.DisksAreAttached. if c.cloud.VMType == vmTypeStandard { return c.cloud.vmSet.DisksAreAttached(diskNames, nodeName) } // 2. vmType is Virtual Machine Scale Set (vmss), convert vmSet to scaleSet. ss, ok := c.cloud.vmSet.(*scaleSet) if !ok { return nil, fmt.Errorf("error of converting vmSet (%q) to scaleSet with vmType %q", c.cloud.vmSet, c.cloud.VMType) </del> <ins> attached := make(map[string]bool) for _, diskName := range diskNames { attached[diskName] = false </ins> } <del> // 3. If the node is managed by availability set, then check with availabilitySet.DisksAreAttached. managedByAS, err := ss.isNodeManagedByAvailabilitySet(mapNodeNameToVMName(nodeName)) </del> <ins> disks, err := c.getNodeDataDisks(nodeName) </ins> if err != nil { <del> return nil, err </del> <ins> if err == cloudprovider.InstanceNotFound { // if host doesn't exist, no need to detach glog.Warningf("azureDisk - Cannot find node %q, DisksAreAttached will assume disks %v are not attached to it.", nodeName, diskNames) return attached, nil } return attached, err </ins> } <del> if managedByAS { // vm is managed by availability set. return ss.availabilitySet.DisksAreAttached(diskNames, nodeName) </del> <ins> for _, disk := range disks { for _, diskName := range diskNames { if disk.Name != nil && diskName != "" && *disk.Name == diskName { attached[diskName] = true } } </ins> } <del> // 4. Node is managed by vmss, check with scaleSet.DisksAreAttached. return ss.DisksAreAttached(diskNames, nodeName) </del> <ins> return attached, nil </ins> }

Related discussion:
text: <!-- This form is for bug reports and feature requests ONLY! If you're looking for help check and the . --Is this a BUG REPORT or FEATURE REQUEST?: /kind cleanup What happened: VirtualMachineScaleSetVM and VirtualMachine are different data structures (because of different API versions), so the disk attach operation seems duplicate between vm and vmss. That means although disk operations are in same logic, some duplication are still required now. They should use same api version in the future and should be merged into same code base, e.g. move sharing logic to Refer What you expected to happen: vmss and vm disk operations should share same code logic. How to reproduce it (as minimally and precisely as possible): Anything else we need to know?: Environment: Kubernetes version (use ): Cloud provider or hardware configuration: OS (e.g. from /etc/os-release): Kernel (e.g. ): Install tools: Others:
I have checked with currently we can’t merge, e.g. following two structs are different since they using different sdk-for-azure versions. While in the end when sdk-for-azure version is upgraded, above two versions should be same and at that time, we should merge functions like ‘AttachDisk’, ‘DetachDiskByName’, 'GetDiskLun', GetNextDiskLun, DisksAreAttached