pkg/armhelpers/utils/util.go (132 lines of code) (raw):
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
// Package utils provides helper methods to assist with ARM operations.
package utils
import (
"net/url"
"regexp"
"strconv"
"strings"
"github.com/Azure/aks-engine-azurestack/pkg/api"
compute "github.com/Azure/azure-sdk-for-go/profile/p20200901/resourcemanager/compute/armcompute"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
const (
// TODO: merge with the RP code
k8sLinuxVMNamingFormat = "^[0-9a-zA-Z]{3}-(.+)-([0-9a-fA-F]{8})-{0,2}([0-9]+)$"
k8sLinuxVMAgentPoolNameIndex = 1
k8sLinuxVMAgentClusterIDIndex = 2
k8sLinuxVMAgentIndexArrayIndex = 3
// here there are 2 capture groups
// the first is the agent pool name, which can contain -s
// the second group is the Cluster ID for the cluster
vmssNamingFormat = "^[0-9a-zA-Z]+-(.+)-([0-9a-fA-F]{8})-vmss$"
vmssAgentPoolNameIndex = 1
vmssClusterIDIndex = 2
k8sWindowsOldVMNamingFormat = "^([a-fA-F0-9]{5})([0-9a-zA-Z]{3})([9])([a-zA-Z0-9]{3,5})$"
k8sWindowsVMNamingFormat = "^([a-fA-F0-9]{4})([0-9a-zA-Z]{3})([0-9]{3,8})$"
windowsVmssNamingFormatV2 = "aks([0-9a-zA-Z]+)$"
windowsVmssAgentPoolNameIndexV2 = 1
)
var vmnameLinuxRegexp *regexp.Regexp
var vmssnameRegexp *regexp.Regexp
var vmnameWindowsRegexp *regexp.Regexp
var oldvmnameWindowsRegexp *regexp.Regexp
var windowsVmssRegexV2 *regexp.Regexp
func init() {
vmnameLinuxRegexp = regexp.MustCompile(k8sLinuxVMNamingFormat)
vmnameWindowsRegexp = regexp.MustCompile(k8sWindowsVMNamingFormat)
oldvmnameWindowsRegexp = regexp.MustCompile(k8sWindowsOldVMNamingFormat)
vmssnameRegexp = regexp.MustCompile(vmssNamingFormat)
windowsVmssRegexV2 = regexp.MustCompile(windowsVmssNamingFormatV2)
}
// ResourceName returns the last segment (the resource name) for the specified resource identifier.
func ResourceName(id string) (string, error) {
parts := strings.Split(id, "/")
name := parts[len(parts)-1]
if len(name) == 0 {
return "", errors.Errorf("resource name was missing from identifier")
}
return name, nil
}
// SplitBlobURI returns a decomposed blob URI parts: accountName, containerName, blobName.
func SplitBlobURI(uri string) (string, string, string, error) {
parsed, err := url.Parse(uri)
if err != nil {
return "", "", "", err
}
accountName := strings.Split(parsed.Host, ".")[0]
urlParts := strings.Split(parsed.Path, "/")
containerName := urlParts[1]
blobPath := strings.Join(urlParts[2:], "/")
return accountName, containerName, blobPath, nil
}
// K8sLinuxVMNameParts returns parts of Linux VM name e.g: k8s-agentpool1-11290731-0
func K8sLinuxVMNameParts(vmName string) (poolIdentifier, nameSuffix string, agentIndex int, err error) {
vmNameParts := vmnameLinuxRegexp.FindStringSubmatch(vmName)
if len(vmNameParts) != 4 {
return "", "", -1, errors.Errorf("resource name was missing from identifier")
}
vmNum, err := strconv.Atoi(vmNameParts[k8sLinuxVMAgentIndexArrayIndex])
if err != nil {
return "", "", -1, errors.Wrap(err, "Error parsing VM Name")
}
return vmNameParts[k8sLinuxVMAgentPoolNameIndex], vmNameParts[k8sLinuxVMAgentClusterIDIndex], vmNum, nil
}
// VmssNameParts returns parts of Linux VM name e.g: k8s-agentpool1-11290731-0
func VmssNameParts(vmssName string) (poolIdentifier, nameSuffix string, err error) {
vmssNameParts := vmssnameRegexp.FindStringSubmatch(vmssName)
if len(vmssNameParts) != 3 {
return "", "", errors.New("resource name was missing from identifier")
}
return vmssNameParts[vmssAgentPoolNameIndex], vmssNameParts[vmssClusterIDIndex], nil
}
// WindowsVmssNameParts returns the agent pool name from the VMSS name.
// For windows, the VMSS name is 'aks' concatenated with agentpool name
func WindowsVmssNameParts(vmssName string) (poolIdentifier string, err error) {
vmssNameParts := windowsVmssRegexV2.FindStringSubmatch(vmssName)
if len(vmssNameParts) != 2 {
return "", errors.New("resource name was missing from identifier")
}
return vmssNameParts[windowsVmssAgentPoolNameIndexV2], nil
}
// WindowsVMNameParts returns parts of Windows VM name
func WindowsVMNameParts(vmName string) (poolPrefix string, orch string, poolIndex int, agentIndex int, err error) {
var poolInfo string
vmNameParts := oldvmnameWindowsRegexp.FindStringSubmatch(vmName)
if len(vmNameParts) != 5 {
vmNameParts = vmnameWindowsRegexp.FindStringSubmatch(vmName)
if len(vmNameParts) != 4 {
return "", "", -1, -1, errors.New("resource name was missing from identifier")
}
poolInfo = vmNameParts[3]
} else {
poolInfo = vmNameParts[4]
}
poolPrefix = vmNameParts[1]
orch = vmNameParts[2]
poolIndex, err = strconv.Atoi(poolInfo[:2])
if err != nil {
return "", "", -1, -1, errors.Wrap(err, "Error parsing VM Name")
}
agentIndex, err = strconv.Atoi(poolInfo[2:])
if err != nil {
return "", "", -1, -1, errors.Wrap(err, "Error parsing VM Name")
}
return poolPrefix, orch, poolIndex, agentIndex, nil
}
// GetVMNameIndex return VM index of a node in the Kubernetes cluster
func GetVMNameIndex(osType compute.OperatingSystemTypes, vmName string) (int, error) {
var agentIndex int
var err error
if osType == compute.OperatingSystemTypesLinux {
_, _, agentIndex, err = K8sLinuxVMNameParts(vmName)
if err != nil {
log.Errorln(err)
return 0, err
}
return agentIndex, nil
}
if osType == compute.OperatingSystemTypesWindows {
_, _, _, agentIndex, err = WindowsVMNameParts(vmName)
if err != nil {
log.Errorln(err)
return 0, err
}
return agentIndex, nil
}
return 0, errors.Errorf("Unknown os type. Can't determine vm index")
}
// GetK8sVMName reconstructs the VM name
func GetK8sVMName(p *api.Properties, agentPoolProfile *api.AgentPoolProfile, agentIndex int) (string, error) {
vmPrefix := p.GetAgentVMPrefix(agentPoolProfile, p.GetAgentPoolIndexByName(agentPoolProfile.Name))
if vmPrefix != "" {
return vmPrefix + strconv.Itoa(agentIndex), nil
}
return "", errors.Errorf("Failed to reconstruct VM Name")
}