зеркало из https://github.com/Azure/aks-engine.git
845 строки
29 KiB
Go
845 строки
29 KiB
Go
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT license.
|
|
|
|
package transform
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
const (
|
|
//Field names
|
|
customDataFieldName = "customData"
|
|
dependsOnFieldName = "dependsOn"
|
|
hardwareProfileFieldName = "hardwareProfile"
|
|
imageReferenceFieldName = "imageReference"
|
|
nameFieldName = "name"
|
|
osProfileFieldName = "osProfile"
|
|
propertiesFieldName = "properties"
|
|
resourcesFieldName = "resources"
|
|
outputsFieldName = "outputs"
|
|
storageProfileFieldName = "storageProfile"
|
|
typeFieldName = "type"
|
|
vmSizeFieldName = "vmSize"
|
|
dataDisksFieldName = "dataDisks"
|
|
createOptionFieldName = "createOption"
|
|
tagsFieldName = "tags"
|
|
managedDiskFieldName = "managedDisk"
|
|
windowsConfigurationFieldName = "windowsConfiguration"
|
|
platformFaultDomainCountFieldName = "platformFaultDomainCount"
|
|
singlePlacementGroupFieldName = "singlePlacementGroup"
|
|
proximityPlacementGroupFieldName = "proximityPlacementGroup"
|
|
|
|
// ARM resource Types
|
|
nsgResourceType = "Microsoft.Network/networkSecurityGroups"
|
|
rtResourceType = "Microsoft.Network/routeTables"
|
|
vmResourceType = "Microsoft.Compute/virtualMachines"
|
|
vmExtensionType = "Microsoft.Compute/virtualMachines/extensions"
|
|
nicResourceType = "Microsoft.Network/networkInterfaces"
|
|
vnetResourceType = "Microsoft.Network/virtualNetworks"
|
|
vmasResourceType = "Microsoft.Compute/availabilitySets"
|
|
vmssResourceType = "Microsoft.Compute/virtualMachineScaleSets"
|
|
lbResourceType = "Microsoft.Network/loadBalancers"
|
|
roleResourceType = "Microsoft.Authorization/roleAssignments"
|
|
keyVaultResourceType = "Microsoft.KeyVault/vaults"
|
|
publicIPAddressResourceType = "Microsoft.Network/publicIPAddresses"
|
|
storageAccountsResourceType = "Microsoft.Storage/storageAccounts"
|
|
userAssignedIdentityResourceType = "Microsoft.ManagedIdentity/userAssignedIdentities"
|
|
|
|
// resource ids
|
|
nsgID = "nsgID"
|
|
rtID = "routeTableID"
|
|
vnetID = "vnetID"
|
|
agentLbID = "agentLbID"
|
|
)
|
|
|
|
// Translator defines all required interfaces for i18n.Translator.
|
|
type Translator interface {
|
|
// T translates a text string, based on GNU's gettext library.
|
|
T(msgid string, vars ...interface{}) string
|
|
// NT translates a text string into the appropriate plural form, based on GNU's gettext library.
|
|
NT(msgid, msgidPlural string, n int, vars ...interface{}) string
|
|
// Errorf produces an error with a translated error string.
|
|
Errorf(msgid string, vars ...interface{}) error
|
|
// NErrorf produces an error with a translated error string in the appropriate plural form.
|
|
NErrorf(msgid, msgidPlural string, n int, vars ...interface{}) error
|
|
}
|
|
|
|
// Transformer represents the object that transforms template
|
|
type Transformer struct {
|
|
Translator Translator
|
|
}
|
|
|
|
type tMap map[string]interface{}
|
|
type resource map[string]interface{}
|
|
|
|
func (t tMap) Resources(logger *logrus.Entry) []resource {
|
|
resourcesInterfaces := t[resourcesFieldName].([]interface{})
|
|
resources := make([]resource, 0)
|
|
for index, ri := range resourcesInterfaces {
|
|
if r, ok := ri.(map[string]interface{}); ok {
|
|
resources = append(resources, r)
|
|
} else {
|
|
logger.Warnf("Template improperly formatted for resource at index %d", index)
|
|
}
|
|
}
|
|
return resources
|
|
}
|
|
|
|
func (r resource) Type() string {
|
|
return r[typeFieldName].(string)
|
|
}
|
|
|
|
func (r resource) Name() string {
|
|
return r[nameFieldName].(string)
|
|
}
|
|
|
|
func (r resource) Properties() map[string]interface{} {
|
|
prop, ok := r[propertiesFieldName].(map[string]interface{})
|
|
if !ok {
|
|
return map[string]interface{}{}
|
|
}
|
|
return prop
|
|
}
|
|
|
|
func (r resource) RemoveProperty(logger *logrus.Entry, key string) {
|
|
properties := r.Properties()
|
|
_, ok := properties[key]
|
|
if ok {
|
|
logger.Infof("Removing %s property from %s", key, r.Name())
|
|
delete(properties, key)
|
|
}
|
|
}
|
|
|
|
func (r resource) removeDependencyType(logger *logrus.Entry, depType string) []string {
|
|
deps := r.DependsOn()
|
|
var newDependsOn []string
|
|
for _, dep := range deps {
|
|
depVal := dep.(string)
|
|
if !strings.Contains(depVal, depType) {
|
|
logger.Infof("Removing %s dependency from %s", depType, r.Name())
|
|
newDependsOn = append(newDependsOn, depVal)
|
|
}
|
|
}
|
|
return newDependsOn
|
|
}
|
|
|
|
func (r resource) DependsOn() []interface{} {
|
|
deps, ok := r[dependsOnFieldName].([]interface{})
|
|
if !ok {
|
|
return []interface{}{}
|
|
}
|
|
return deps
|
|
}
|
|
|
|
func (t *Transformer) RemoveImmutableResourceProperties(logger *logrus.Entry, templateMap map[string]interface{}) {
|
|
tm := tMap(templateMap)
|
|
for _, resource := range tm.Resources(logger) {
|
|
if resource.Type() == vmssResourceType {
|
|
resource.RemoveProperty(logger, platformFaultDomainCountFieldName)
|
|
resource.RemoveProperty(logger, singlePlacementGroupFieldName)
|
|
resource.RemoveProperty(logger, proximityPlacementGroupFieldName)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (t *Transformer) RemoveJumpboxResourcesFromTemplate(logger *logrus.Entry, templateMap map[string]interface{}) error {
|
|
logger.Debugf("Running RemoveJumpboxResourcesFromTemplate...")
|
|
resources := templateMap[resourcesFieldName].([]interface{})
|
|
indexesToRemove := []int{}
|
|
for index, resource := range resources {
|
|
resourceMap, ok := resource.(map[string]interface{})
|
|
if !ok {
|
|
return errors.Errorf("Template improperly formatted for resource")
|
|
}
|
|
|
|
resourceName, ok := resourceMap[nameFieldName].(string)
|
|
if !ok {
|
|
logger.Warnf("Resource does not have a name property")
|
|
continue
|
|
} else if strings.Contains(resourceName, "variables('jumpbox") || strings.Contains(resourceName, "parameters('jumpbox") {
|
|
indexesToRemove = append(indexesToRemove, index)
|
|
}
|
|
}
|
|
templateMap[resourcesFieldName] = removeIndexesFromArray(resources, indexesToRemove)
|
|
return nil
|
|
}
|
|
|
|
func (t *Transformer) RemoveKMSResourcesFromTemplate(logger *logrus.Entry, templateMap map[string]interface{}) error {
|
|
logger.Debugf("Running RemoveKMSResourcesFromTemplate...")
|
|
resources := templateMap[resourcesFieldName].([]interface{})
|
|
indexesToRemove := []int{}
|
|
for index, resource := range resources {
|
|
resourceMap, ok := resource.(map[string]interface{})
|
|
if !ok {
|
|
return errors.Errorf("Template improperly formatted for resource")
|
|
}
|
|
|
|
resourceName, ok := resourceMap[nameFieldName].(string)
|
|
if !ok {
|
|
logger.Warnf("Resource does not have a name property")
|
|
continue
|
|
} else if strings.Contains(resourceName, "variables('clusterKeyVaultName") {
|
|
indexesToRemove = append(indexesToRemove, index)
|
|
}
|
|
}
|
|
templateMap[resourcesFieldName] = removeIndexesFromArray(resources, indexesToRemove)
|
|
return nil
|
|
}
|
|
|
|
// NormalizeForK8sSLBScalingOrUpgrade takes a template and removes elements that are unwanted in a K8s Standard LB cluster scale up/down case
|
|
func (t *Transformer) NormalizeForK8sSLBScalingOrUpgrade(logger *logrus.Entry, templateMap map[string]interface{}) error {
|
|
logger.Debugf("Running NormalizeForK8sSLBScalingOrUpgrade...")
|
|
lbIndex := -1
|
|
resources := templateMap[resourcesFieldName].([]interface{})
|
|
|
|
for index, resource := range resources {
|
|
resourceMap, ok := resource.(map[string]interface{})
|
|
if !ok {
|
|
logger.Warnf("Template improperly formatted for resource")
|
|
continue
|
|
}
|
|
|
|
resourceType, ok := resourceMap[typeFieldName].(string)
|
|
resourceName := resourceMap[nameFieldName].(string)
|
|
|
|
// remove agentLB if found
|
|
if ok && resourceType == lbResourceType && strings.Contains(resourceName, "variables('agentLbName')") {
|
|
lbIndex = index
|
|
}
|
|
|
|
dependencies, ok := resourceMap[dependsOnFieldName].([]interface{})
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
// remove agentLB from dependsOn if found
|
|
for dIndex := len(dependencies) - 1; dIndex >= 0; dIndex-- {
|
|
dependency := dependencies[dIndex].(string)
|
|
if strings.Contains(dependency, lbResourceType) || strings.Contains(dependency, agentLbID) {
|
|
dependencies = append(dependencies[:dIndex], dependencies[dIndex+1:]...)
|
|
}
|
|
}
|
|
|
|
// remove KeyVault from dependsOn if found
|
|
for dIndex := len(dependencies) - 1; dIndex >= 0; dIndex-- {
|
|
dependency := dependencies[dIndex].(string)
|
|
if strings.Contains(dependency, keyVaultResourceType) {
|
|
dependencies = append(dependencies[:dIndex], dependencies[dIndex+1:]...)
|
|
}
|
|
}
|
|
|
|
if len(dependencies) > 0 {
|
|
resourceMap[dependsOnFieldName] = dependencies
|
|
} else {
|
|
delete(resourceMap, dependsOnFieldName)
|
|
}
|
|
}
|
|
indexesToRemove := []int{}
|
|
if lbIndex != -1 {
|
|
indexesToRemove = append(indexesToRemove, lbIndex)
|
|
}
|
|
templateMap[resourcesFieldName] = removeIndexesFromArray(resources, indexesToRemove)
|
|
return nil
|
|
}
|
|
|
|
// NormalizeForK8sVMASScalingUp takes a template and removes elements that are unwanted in a K8s VMAS scale up/down case
|
|
func (t *Transformer) NormalizeForK8sVMASScalingUp(logger *logrus.Entry, templateMap map[string]interface{}) error {
|
|
if err := t.RemoveResourcesAndOutputsForScaling(logger, templateMap); err != nil {
|
|
return err
|
|
}
|
|
rtIndex := -1
|
|
nsgIndex := -1
|
|
vnetIndex := -1
|
|
vmasIndexes := make([]int, 0)
|
|
|
|
resources := templateMap[resourcesFieldName].([]interface{})
|
|
for index, resource := range resources {
|
|
resourceMap, ok := resource.(map[string]interface{})
|
|
if !ok {
|
|
logger.Warnf("Template improperly formatted for resource")
|
|
continue
|
|
}
|
|
|
|
resourceType, ok := resourceMap[typeFieldName].(string)
|
|
resourceName := resourceMap[nameFieldName].(string)
|
|
|
|
if ok && resourceType == nsgResourceType && !strings.Contains(resourceName, "variables('jumpboxNetworkSecurityGroupName')") {
|
|
|
|
if nsgIndex != -1 {
|
|
err := t.Translator.Errorf("Found 2 resources with type %s in the template. There should only be 1", nsgResourceType)
|
|
logger.Errorf(err.Error())
|
|
return err
|
|
}
|
|
nsgIndex = index
|
|
}
|
|
if ok && resourceType == rtResourceType {
|
|
if rtIndex != -1 {
|
|
err := t.Translator.Errorf("Found 2 resources with type %s in the template. There should only be 1", rtResourceType)
|
|
logger.Warnf(err.Error())
|
|
return err
|
|
}
|
|
rtIndex = index
|
|
}
|
|
if ok && resourceType == vnetResourceType {
|
|
if vnetIndex != -1 {
|
|
err := t.Translator.Errorf("Found 2 resources with type %s in the template. There should only be 1", vnetResourceType)
|
|
logger.Warnf(err.Error())
|
|
return err
|
|
}
|
|
vnetIndex = index
|
|
}
|
|
if ok && resourceType == vmasResourceType {
|
|
// All availability sets can be removed
|
|
vmasIndexes = append(vmasIndexes, index)
|
|
}
|
|
|
|
dependencies, ok := resourceMap[dependsOnFieldName].([]interface{})
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
for dIndex := len(dependencies) - 1; dIndex >= 0; dIndex-- {
|
|
dependency := dependencies[dIndex].(string)
|
|
if strings.Contains(dependency, nsgResourceType) || strings.Contains(dependency, nsgID) ||
|
|
strings.Contains(dependency, rtResourceType) || strings.Contains(dependency, rtID) ||
|
|
strings.Contains(dependency, vnetResourceType) || strings.Contains(dependency, vnetID) ||
|
|
strings.Contains(dependency, vmasResourceType) {
|
|
dependencies = append(dependencies[:dIndex], dependencies[dIndex+1:]...)
|
|
}
|
|
}
|
|
|
|
if len(dependencies) > 0 {
|
|
resourceMap[dependsOnFieldName] = dependencies
|
|
} else {
|
|
delete(resourceMap, dependsOnFieldName)
|
|
}
|
|
}
|
|
|
|
indexesToRemove := []int{}
|
|
|
|
if rtIndex == -1 {
|
|
logger.Debugf("Found no resources with type %s in the template.", rtResourceType)
|
|
} else {
|
|
indexesToRemove = append(indexesToRemove, rtIndex)
|
|
}
|
|
|
|
if vnetIndex != -1 {
|
|
indexesToRemove = append(indexesToRemove, vnetIndex)
|
|
}
|
|
|
|
if len(vmasIndexes) != 0 {
|
|
indexesToRemove = append(indexesToRemove, vmasIndexes...)
|
|
}
|
|
if nsgIndex > 0 {
|
|
indexesToRemove = append(indexesToRemove, nsgIndex)
|
|
}
|
|
|
|
templateMap[resourcesFieldName] = removeIndexesFromArray(resources, indexesToRemove)
|
|
|
|
return nil
|
|
}
|
|
|
|
func removeIndexesFromArray(array []interface{}, indexes []int) []interface{} {
|
|
sort.Sort(sort.Reverse(sort.IntSlice(indexes)))
|
|
for _, index := range indexes {
|
|
array = append(array[:index], array[index+1:]...)
|
|
}
|
|
return array
|
|
}
|
|
|
|
// NormalizeMasterResourcesForVMSSPoolUpgrade removes superfluous template resources for upgrading VMSS nodes
|
|
func (t *Transformer) NormalizeMasterResourcesForVMSSPoolUpgrade(logger *logrus.Entry, templateMap map[string]interface{}) error {
|
|
resources := templateMap[resourcesFieldName].([]interface{})
|
|
indexesToRemove := []int{}
|
|
//update master nodes resources
|
|
for index, resource := range resources {
|
|
resourceMap, ok := resource.(map[string]interface{})
|
|
if !ok {
|
|
logger.Warnf("Template improperly formatted")
|
|
continue
|
|
}
|
|
|
|
if resourceType, ok := resourceMap[typeFieldName].(string); ok {
|
|
if resourceType != vmssResourceType && resourceType != roleResourceType {
|
|
indexesToRemove = append(indexesToRemove, index)
|
|
continue
|
|
}
|
|
resourceName, ok := resourceMap[nameFieldName].(string)
|
|
if !ok {
|
|
logger.Warnf("Template improperly formatted")
|
|
continue
|
|
}
|
|
if (resourceType == vmssResourceType || resourceType == roleResourceType) && strings.Contains(resourceName, "variables('masterVMNamePrefix')") {
|
|
indexesToRemove = append(indexesToRemove, index)
|
|
continue
|
|
}
|
|
// If our role assignment derives from a static user-assigned ID created or referenced during cluster creation, we don't need to re-create it
|
|
if resourceType == roleResourceType && strings.Contains(resourceName, "variables('userAssignedID')") {
|
|
indexesToRemove = append(indexesToRemove, index)
|
|
continue
|
|
}
|
|
}
|
|
|
|
if _, ok := resourceMap[dependsOnFieldName].([]interface{}); ok {
|
|
delete(resourceMap, dependsOnFieldName)
|
|
}
|
|
}
|
|
templateMap[resourcesFieldName] = removeIndexesFromArray(resources, indexesToRemove)
|
|
delete(templateMap, outputsFieldName)
|
|
|
|
return nil
|
|
}
|
|
|
|
// RemoveResourcesAndOutputsForScaling takes a template and removes elements that are unwanted in any scale up/down case
|
|
func (t *Transformer) RemoveResourcesAndOutputsForScaling(logger *logrus.Entry, templateMap map[string]interface{}) error {
|
|
resources := templateMap[resourcesFieldName].([]interface{})
|
|
indexesToRemove := []int{}
|
|
//remove master nodes resources from agent pool scaling template
|
|
for index, resource := range resources {
|
|
resourceMap, ok := resource.(map[string]interface{})
|
|
if !ok {
|
|
logger.Warnf("Template improperly formatted")
|
|
continue
|
|
}
|
|
|
|
var resourceName string
|
|
resourceName, ok = resourceMap[nameFieldName].(string)
|
|
if !ok {
|
|
logger.Warnf("Template improperly formatted")
|
|
continue
|
|
}
|
|
if strings.Contains(resourceName, "variables('master") {
|
|
indexesToRemove = append(indexesToRemove, index)
|
|
}
|
|
if strings.Contains(resourceName, "variables('agentPublicIPAddressName')") {
|
|
indexesToRemove = append(indexesToRemove, index)
|
|
}
|
|
continue
|
|
}
|
|
templateMap[resourcesFieldName] = removeIndexesFromArray(resources, indexesToRemove)
|
|
delete(templateMap, outputsFieldName)
|
|
|
|
return nil
|
|
}
|
|
|
|
// NormalizeResourcesForK8sMasterUpgrade takes a template and removes elements that are unwanted in any scale up/down case
|
|
func (t *Transformer) NormalizeResourcesForK8sMasterUpgrade(logger *logrus.Entry, templateMap map[string]interface{}, isMasterManagedDisk bool, agentPoolsToPreserve map[string]bool) error {
|
|
resources := templateMap[resourcesFieldName].([]interface{})
|
|
resourceTypeToProcess := map[string]bool{
|
|
vmResourceType: true, vmExtensionType: true, nicResourceType: true,
|
|
vnetResourceType: true, nsgResourceType: true, lbResourceType: true,
|
|
vmssResourceType: true, vmasResourceType: true, roleResourceType: true,
|
|
publicIPAddressResourceType: true, storageAccountsResourceType: true,
|
|
keyVaultResourceType: true, rtResourceType: true,
|
|
userAssignedIdentityResourceType: true}
|
|
logger.Infoln(fmt.Sprintf("Resource count before running NormalizeResourcesForK8sMasterUpgrade: %d", len(resources)))
|
|
|
|
filteredResources := resources[:0]
|
|
|
|
// remove agent nodes resources if needed and set dataDisk createOption to attach
|
|
for _, resource := range resources {
|
|
filteredResources = append(filteredResources, resource)
|
|
resourceMap, ok := resource.(map[string]interface{})
|
|
if !ok {
|
|
logger.Warnf("Template improperly formatted for field name: %s", resourcesFieldName)
|
|
continue
|
|
}
|
|
|
|
resourceType, ok := resourceMap[typeFieldName].(string)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
_, process := resourceTypeToProcess[resourceType]
|
|
if !process {
|
|
continue
|
|
}
|
|
|
|
filteredResources = removeVMAS(logger, filteredResources, resourceMap)
|
|
|
|
resourceName, ok := resourceMap[nameFieldName].(string)
|
|
if !ok {
|
|
logger.Warnf("Template improperly formatted for field name: %s", nameFieldName)
|
|
continue
|
|
}
|
|
|
|
switch resourceType {
|
|
case vmssResourceType, vmResourceType, vmExtensionType, roleResourceType, nicResourceType:
|
|
if !strings.Contains(resourceName, "variables('masterVMNamePrefix')") {
|
|
filteredResources = filteredResources[:len(filteredResources)-1]
|
|
continue
|
|
}
|
|
if resourceType == nicResourceType {
|
|
delete(resourceMap, dependsOnFieldName)
|
|
}
|
|
case nsgResourceType:
|
|
if strings.Contains(resourceName, "variables('nsgName')") {
|
|
filteredResources = filteredResources[:len(filteredResources)-1]
|
|
continue
|
|
}
|
|
case publicIPAddressResourceType, rtResourceType, userAssignedIdentityResourceType:
|
|
filteredResources = filteredResources[:len(filteredResources)-1]
|
|
continue
|
|
case lbResourceType:
|
|
if strings.Contains(resourceName, "variables('masterInternalLbName')") || strings.Contains(resourceName, "variables('masterLbName')") {
|
|
filteredResources = filteredResources[:len(filteredResources)-1]
|
|
continue
|
|
}
|
|
case vnetResourceType:
|
|
if strings.Contains(resourceName, "variables('virtualNetworkName')") {
|
|
filteredResources = filteredResources[:len(filteredResources)-1]
|
|
continue
|
|
}
|
|
case storageAccountsResourceType:
|
|
if !strings.Contains(resourceName, "variables('clusterKeyVaultName')") {
|
|
filteredResources = filteredResources[:len(filteredResources)-1]
|
|
continue
|
|
}
|
|
case keyVaultResourceType:
|
|
if !strings.Contains(resourceName, "variables('clusterKeyVaultName')") {
|
|
filteredResources = filteredResources[:len(filteredResources)-1]
|
|
continue
|
|
}
|
|
}
|
|
|
|
if resourceType == vmssResourceType || resourceType == vnetResourceType {
|
|
RemoveNsgDependency(logger, resourceName, resourceMap)
|
|
continue
|
|
}
|
|
|
|
if strings.EqualFold(resourceType, vmResourceType) &&
|
|
strings.Contains(resourceName, "variables('masterVMNamePrefix')") {
|
|
resourceProperties, ok := resourceMap[propertiesFieldName].(map[string]interface{})
|
|
if !ok {
|
|
logger.Warnf("Template improperly formatted for field name: %s, resource name: %s", propertiesFieldName, resourceName)
|
|
continue
|
|
}
|
|
|
|
storageProfile, ok := resourceProperties[storageProfileFieldName].(map[string]interface{})
|
|
if !ok {
|
|
logger.Warnf("Template improperly formatted: %s", storageProfileFieldName)
|
|
continue
|
|
}
|
|
|
|
dataDisks, ok := storageProfile[dataDisksFieldName].([]interface{})
|
|
if !ok {
|
|
logger.Warnf("Template improperly formatted for field name: %s, property name: %s", storageProfileFieldName, dataDisksFieldName)
|
|
continue
|
|
}
|
|
|
|
dataDisk, ok := dataDisks[0].(map[string]interface{})
|
|
if !ok {
|
|
logger.Warnf("Template improperly formatted for field name: %s, there is no data disks defined", dataDisksFieldName)
|
|
continue
|
|
}
|
|
|
|
dataDisk[createOptionFieldName] = "attach"
|
|
|
|
if isMasterManagedDisk {
|
|
managedDisk := compute.ManagedDiskParameters{}
|
|
id := "[concat('/subscriptions/', variables('subscriptionId'), '/resourceGroups/', variables('resourceGroup'),'/providers/Microsoft.Compute/disks/', variables('masterVMNamePrefix'), copyIndex(variables('masterOffset')),'-etcddisk')]"
|
|
managedDisk.ID = &id
|
|
diskInterface := &managedDisk
|
|
dataDisk[managedDiskFieldName] = diskInterface
|
|
}
|
|
}
|
|
}
|
|
|
|
templateMap[resourcesFieldName] = filteredResources
|
|
delete(templateMap, outputsFieldName)
|
|
|
|
logger.Infoln(fmt.Sprintf("Resource count after running NormalizeResourcesForK8sMasterUpgrade: %d",
|
|
len(templateMap[resourcesFieldName].([]interface{}))))
|
|
return nil
|
|
}
|
|
|
|
func removeVMAS(logger *logrus.Entry, resources []interface{}, resource resource) []interface{} {
|
|
// remove vmas
|
|
if strings.EqualFold(resource.Type(), vmasResourceType) {
|
|
return resources[:len(resources)-1]
|
|
}
|
|
// remove dependencies on vmas
|
|
if strings.EqualFold(resource.Type(), vmResourceType) {
|
|
resource[dependsOnFieldName] = resource.removeDependencyType(logger, vmasResourceType)
|
|
}
|
|
return resources
|
|
}
|
|
|
|
//RemoveNsgDependency Removes the nsg dependency from the resource
|
|
func RemoveNsgDependency(logger *logrus.Entry, resourceName string, resourceMap map[string]interface{}) {
|
|
|
|
if resourceName != "" && resourceMap != nil {
|
|
dependencies, ok := resourceMap[dependsOnFieldName].([]interface{})
|
|
if !ok {
|
|
logger.Warnf("Could not find dependencies for resourceName: %s", resourceName)
|
|
return
|
|
}
|
|
|
|
for dIndex := len(dependencies) - 1; dIndex >= 0; dIndex-- {
|
|
dependency := dependencies[dIndex].(string)
|
|
if strings.Contains(dependency, nsgResourceType) || strings.Contains(dependency, nsgID) {
|
|
dependencies = append(dependencies[:dIndex], dependencies[dIndex+1:]...)
|
|
}
|
|
}
|
|
|
|
if len(dependencies) > 0 {
|
|
resourceMap[dependsOnFieldName] = dependencies
|
|
} else {
|
|
delete(resourceMap, dependsOnFieldName)
|
|
}
|
|
|
|
return
|
|
}
|
|
}
|
|
|
|
// NormalizeResourcesForK8sAgentUpgrade takes a template and removes elements that are unwanted in any scale/upgrade case
|
|
func (t *Transformer) NormalizeResourcesForK8sAgentUpgrade(logger *logrus.Entry, templateMap map[string]interface{}, isMasterManagedDisk bool, agentPoolsToPreserve map[string]bool) error {
|
|
logger.Infoln("Running NormalizeResourcesForK8sMasterUpgrade....")
|
|
resources := templateMap[resourcesFieldName].([]interface{})
|
|
resourceTypeToProcess := map[string]bool{
|
|
vmResourceType: true, vmExtensionType: true, nicResourceType: true,
|
|
vnetResourceType: true, nsgResourceType: true, lbResourceType: true,
|
|
vmssResourceType: true, vmasResourceType: true, roleResourceType: true,
|
|
storageAccountsResourceType: true}
|
|
logger.Infoln(fmt.Sprintf("Resource count before running NormalizeResourcesForK8sMasterUpgrade: %d", len(resources)))
|
|
|
|
filteredResources := resources[:0]
|
|
|
|
for _, resource := range resources {
|
|
filteredResources = append(filteredResources, resource)
|
|
resourceMap, ok := resource.(map[string]interface{})
|
|
if !ok {
|
|
logger.Warnf("Template improperly formatted for field name: %s", resourcesFieldName)
|
|
continue
|
|
}
|
|
|
|
resourceType, ok := resourceMap[typeFieldName].(string)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
_, process := resourceTypeToProcess[resourceType]
|
|
if !process {
|
|
continue
|
|
}
|
|
|
|
filteredResources = removeVMAS(logger, filteredResources, resourceMap)
|
|
|
|
resourceName, ok := resourceMap[nameFieldName].(string)
|
|
if !ok {
|
|
logger.Warnf("Template improperly formatted for field name: %s", nameFieldName)
|
|
continue
|
|
}
|
|
|
|
// Remove control plane resources
|
|
switch resourceType {
|
|
case vmResourceType, vmExtensionType, roleResourceType, nicResourceType:
|
|
if strings.Contains(resourceName, "variables('masterVMNamePrefix')") {
|
|
filteredResources = filteredResources[:len(filteredResources)-1]
|
|
continue
|
|
}
|
|
case nsgResourceType:
|
|
if strings.Contains(resourceName, "variables('nsgName')") {
|
|
filteredResources = filteredResources[:len(filteredResources)-1]
|
|
continue
|
|
}
|
|
case publicIPAddressResourceType, lbResourceType:
|
|
if strings.Contains(resourceName, "variables('master") {
|
|
filteredResources = filteredResources[:len(filteredResources)-1]
|
|
continue
|
|
}
|
|
case vnetResourceType:
|
|
RemoveNsgDependency(logger, resourceName, resourceMap)
|
|
case vmssResourceType:
|
|
filteredResources = filteredResources[:len(filteredResources)-1]
|
|
continue
|
|
}
|
|
|
|
tags, _ := resourceMap[tagsFieldName].(map[string]interface{})
|
|
poolName := fmt.Sprint(tags["poolName"])
|
|
|
|
// Remove resources for node pools not being upgraded
|
|
switch resourceType {
|
|
case vmResourceType:
|
|
logger.Infoln(fmt.Sprintf("Evaluating if agent pool: %s, resource: %s needs to be removed", poolName, resourceName))
|
|
logger.Infoln(fmt.Sprintf("agentPoolsToPreserve: %v...", agentPoolsToPreserve))
|
|
|
|
removeVM := true
|
|
|
|
for pool, preserve := range agentPoolsToPreserve {
|
|
if strings.Contains(resourceName, "variables('"+pool) && preserve {
|
|
removeVM = false
|
|
}
|
|
}
|
|
|
|
if removeVM {
|
|
logger.Infoln(fmt.Sprintf("Removing agent resource: %s from template", resourceName))
|
|
filteredResources = filteredResources[:len(filteredResources)-1]
|
|
}
|
|
case roleResourceType:
|
|
logger.Infoln(fmt.Sprintf("Evaluating if agent resource: %s needs to be removed", resourceName))
|
|
removeRole := true
|
|
|
|
for pool, preserve := range agentPoolsToPreserve {
|
|
if strings.Contains(resourceName, "variables('"+pool) && preserve {
|
|
removeRole = false
|
|
}
|
|
}
|
|
|
|
if removeRole {
|
|
logger.Infoln(fmt.Sprintf("Removing agent resource: %s from template", resourceName))
|
|
filteredResources = filteredResources[:len(filteredResources)-1]
|
|
}
|
|
case vmExtensionType:
|
|
logger.Infoln(fmt.Sprintf("Evaluating if extension: %s needs to be removed", resourceName))
|
|
logger.Infoln(fmt.Sprintf("agentPoolsToPreserve: %v...", agentPoolsToPreserve))
|
|
|
|
removeExtension := true
|
|
for poolName, preserve := range agentPoolsToPreserve {
|
|
if strings.Contains(resourceName, "variables('"+poolName) && preserve {
|
|
removeExtension = false
|
|
}
|
|
}
|
|
|
|
if removeExtension {
|
|
logger.Infoln(fmt.Sprintf("Removing extension: %s from template", resourceName))
|
|
filteredResources = filteredResources[:len(filteredResources)-1]
|
|
}
|
|
case storageAccountsResourceType:
|
|
logger.Infoln(fmt.Sprintf("Evaluating if storage account: %s needs to be removed", resourceName))
|
|
logger.Infoln(fmt.Sprintf("agentPoolsToPreserve: %v...", agentPoolsToPreserve))
|
|
|
|
removeStorageAccount := true
|
|
for poolName, preserve := range agentPoolsToPreserve {
|
|
if strings.Contains(resourceName, "variables('"+poolName) && preserve {
|
|
removeStorageAccount = false
|
|
}
|
|
}
|
|
|
|
if removeStorageAccount {
|
|
logger.Infoln(fmt.Sprintf("Removing storage account: %s from template", resourceName))
|
|
filteredResources = filteredResources[:len(filteredResources)-1]
|
|
}
|
|
case nicResourceType:
|
|
logger.Infoln(fmt.Sprintf("Evaluating if NIC: %s needs to be removed", resourceName))
|
|
logger.Infoln(fmt.Sprintf("agentPoolsToPreserve: %v...", agentPoolsToPreserve))
|
|
|
|
removeNIC := true
|
|
for poolName, preserve := range agentPoolsToPreserve {
|
|
if strings.Contains(resourceName, "variables('"+poolName) && preserve {
|
|
removeNIC = false
|
|
}
|
|
}
|
|
|
|
if removeNIC {
|
|
logger.Infoln(fmt.Sprintf("Removing NIC: %s from template", resourceName))
|
|
filteredResources = filteredResources[:len(filteredResources)-1]
|
|
}
|
|
}
|
|
}
|
|
|
|
templateMap[resourcesFieldName] = filteredResources
|
|
|
|
logger.Infoln(fmt.Sprintf("Resource count after running NormalizeResourcesForK8sMasterUpgrade: %d",
|
|
len(templateMap[resourcesFieldName].([]interface{}))))
|
|
|
|
logger.Infoln("Running NormalizeForK8sVMASScalingUp....")
|
|
if err := t.NormalizeForK8sVMASScalingUp(logger, templateMap); err != nil {
|
|
log.Fatalln(err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// NormalizeForK8sAddVMASPool takes a template and removes elements that are unwanted in a K8s VMAS add pool case
|
|
func (t *Transformer) NormalizeForK8sAddVMASPool(l *logrus.Entry, templateMap map[string]interface{}) error {
|
|
t.RemoveImmutableResourceProperties(l, templateMap)
|
|
if err := t.RemoveJumpboxResourcesFromTemplate(l, templateMap); err != nil {
|
|
return err
|
|
}
|
|
if err := t.RemoveResourcesAndOutputsForScaling(l, templateMap); err != nil {
|
|
return err
|
|
}
|
|
if err := removeSingleOfType(l, templateMap, vnetResourceType); err != nil {
|
|
return err
|
|
}
|
|
if err := removeSingleOfType(l, templateMap, rtResourceType); err != nil {
|
|
return err
|
|
}
|
|
if err := removeSingleOfType(l, templateMap, nsgResourceType); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// removeSingleOfType takes a template and removes references to resources of the given type
|
|
func removeSingleOfType(logger *logrus.Entry, templateMap map[string]interface{}, typeToRemove string) error {
|
|
logger.Debugf("Looking for resources of type %s from the template.", typeToRemove)
|
|
indexToRemove := -1
|
|
|
|
templateResources := templateMap[resourcesFieldName].([]interface{})
|
|
for i, r := range templateResources {
|
|
resource, ok := r.(map[string]interface{})
|
|
if !ok {
|
|
logger.Warnf("Template improperly formatted for resource")
|
|
continue
|
|
}
|
|
|
|
resourceType, found := resource[typeFieldName].(string)
|
|
if found && resourceType == typeToRemove {
|
|
if indexToRemove != -1 {
|
|
err := errors.Errorf("Found at least 2 resources of type %s in the template but only 1 is expected", vnetResourceType)
|
|
logger.Warnf(err.Error())
|
|
return err
|
|
}
|
|
indexToRemove = i
|
|
}
|
|
|
|
deps, found := resource[dependsOnFieldName].([]interface{})
|
|
if !found {
|
|
continue
|
|
}
|
|
for idep := len(deps) - 1; idep >= 0; idep-- {
|
|
dep := deps[idep].(string)
|
|
if strings.Contains(dep, typeToRemove) || containsResourceID(dep, typeToRemove) {
|
|
deps = append(deps[:idep], deps[idep+1:]...)
|
|
}
|
|
}
|
|
if len(deps) > 0 {
|
|
resource[dependsOnFieldName] = deps
|
|
} else {
|
|
delete(resource, dependsOnFieldName)
|
|
}
|
|
}
|
|
|
|
if indexToRemove != -1 {
|
|
logger.Debugf("Removing resource of type %s from the template.", typeToRemove)
|
|
templateMap[resourcesFieldName] = removeIndexesFromArray(templateResources, []int{indexToRemove})
|
|
} else {
|
|
logger.Debugf("No resources of type %s were found.", typeToRemove)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func containsResourceID(dep, typeToRemove string) bool {
|
|
switch typeToRemove {
|
|
case nsgResourceType:
|
|
return strings.Contains(dep, nsgID)
|
|
case rtResourceType:
|
|
return strings.Contains(dep, rtID)
|
|
case vnetResourceType:
|
|
return strings.Contains(dep, vnetID)
|
|
default:
|
|
return false
|
|
}
|
|
}
|