This repository has been archived by the owner on Oct 24, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 521
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: update VMSS node pools (#3830)
- Loading branch information
1 parent
a5609f4
commit 4f4b8e7
Showing
8 changed files
with
433 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,260 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT license. | ||
|
||
package cmd | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
"strconv" | ||
|
||
"github.com/Azure/aks-engine/pkg/api" | ||
"github.com/Azure/aks-engine/pkg/armhelpers" | ||
"github.com/Azure/aks-engine/pkg/engine" | ||
"github.com/Azure/aks-engine/pkg/helpers" | ||
"github.com/Azure/aks-engine/pkg/i18n" | ||
"github.com/leonelquinteros/gotext" | ||
"github.com/pkg/errors" | ||
log "github.com/sirupsen/logrus" | ||
"github.com/spf13/cobra" | ||
prefixed "github.com/x-cray/logrus-prefixed-formatter" | ||
) | ||
|
||
type updateCmd struct { | ||
authArgs | ||
|
||
// user input | ||
apiModelPath string | ||
resourceGroupName string | ||
location string | ||
agentPoolToUpdate string | ||
|
||
// derived | ||
containerService *api.ContainerService | ||
apiVersion string | ||
agentPool *api.AgentPoolProfile | ||
client armhelpers.AKSEngineClient | ||
locale *gotext.Locale | ||
nameSuffix string | ||
agentPoolIndex int | ||
logger *log.Entry | ||
kubeconfig string | ||
} | ||
|
||
const ( | ||
updateName = "update" | ||
updateShortDescription = "Update an existing AKS Engine-created VMSS node pool" | ||
updateLongDescription = "Update an existing AKS Engine-created VMSS node pool in a Kubernetes cluster by updating its VMSS model" | ||
) | ||
|
||
// newUpdateCmd returns an instance reference of updateCmd | ||
func newUpdateCmd() *cobra.Command { | ||
uc := updateCmd{} | ||
|
||
updateCmd := &cobra.Command{ | ||
Use: updateName, | ||
Short: updateShortDescription, | ||
Long: updateLongDescription, | ||
RunE: uc.run, | ||
} | ||
|
||
f := updateCmd.Flags() | ||
f.StringVarP(&uc.location, "location", "l", "", "location the cluster is deployed in") | ||
f.StringVarP(&uc.resourceGroupName, "resource-group", "g", "", "the resource group where the cluster is deployed") | ||
f.StringVarP(&uc.apiModelPath, "api-model", "m", "", "path to the generated apimodel.json file") | ||
f.StringVar(&uc.agentPoolToUpdate, "node-pool", "", "node pool to scale") | ||
addAuthFlags(&uc.authArgs, f) | ||
|
||
return updateCmd | ||
} | ||
|
||
func (uc *updateCmd) validate(cmd *cobra.Command) error { | ||
log.Debugln("validating update command line arguments...") | ||
var err error | ||
|
||
uc.locale, err = i18n.LoadTranslations() | ||
if err != nil { | ||
return errors.Wrap(err, "error loading translation files") | ||
} | ||
|
||
if uc.resourceGroupName == "" { | ||
_ = cmd.Usage() | ||
return errors.New("--resource-group must be specified") | ||
} | ||
|
||
if uc.location == "" { | ||
_ = cmd.Usage() | ||
return errors.New("--location must be specified") | ||
} | ||
|
||
uc.location = helpers.NormalizeAzureRegion(uc.location) | ||
|
||
if uc.apiModelPath == "" { | ||
_ = cmd.Usage() | ||
return errors.New("--api-model must be specified") | ||
} | ||
|
||
if uc.agentPoolToUpdate == "" { | ||
_ = cmd.Usage() | ||
return errors.New("--node-pool must be specified") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (uc *updateCmd) load() error { | ||
logger := log.New() | ||
logger.Formatter = new(prefixed.TextFormatter) | ||
uc.logger = log.NewEntry(log.New()) | ||
var err error | ||
|
||
ctx, cancel := context.WithTimeout(context.Background(), armhelpers.DefaultARMOperationTimeout) | ||
defer cancel() | ||
|
||
if _, err = os.Stat(uc.apiModelPath); os.IsNotExist(err) { | ||
return errors.Errorf("specified api model does not exist (%s)", uc.apiModelPath) | ||
} | ||
|
||
apiloader := &api.Apiloader{ | ||
Translator: &i18n.Translator{ | ||
Locale: uc.locale, | ||
}, | ||
} | ||
uc.containerService, uc.apiVersion, err = apiloader.LoadContainerServiceFromFile(uc.apiModelPath, true, true, nil) | ||
if err != nil { | ||
return errors.Wrap(err, "error parsing the api model") | ||
} | ||
|
||
if uc.containerService.Properties.IsCustomCloudProfile() { | ||
if err = writeCustomCloudProfile(uc.containerService); err != nil { | ||
return errors.Wrap(err, "error writing custom cloud profile") | ||
} | ||
|
||
if err = uc.containerService.Properties.SetCustomCloudSpec(api.AzureCustomCloudSpecParams{IsUpgrade: false, IsScale: true}); err != nil { | ||
return errors.Wrap(err, "error parsing the api model") | ||
} | ||
} | ||
|
||
if err = uc.authArgs.validateAuthArgs(); err != nil { | ||
return err | ||
} | ||
|
||
if uc.client, err = uc.authArgs.getClient(); err != nil { | ||
return errors.Wrap(err, "failed to get client") | ||
} | ||
|
||
_, err = uc.client.EnsureResourceGroup(ctx, uc.resourceGroupName, uc.location, nil) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if uc.containerService.Location == "" { | ||
uc.containerService.Location = uc.location | ||
} else if uc.containerService.Location != uc.location { | ||
return errors.New("--location does not match api model location") | ||
} | ||
|
||
agentPoolIndex := -1 | ||
for i, pool := range uc.containerService.Properties.AgentPoolProfiles { | ||
if pool.Name == uc.agentPoolToUpdate { | ||
agentPoolIndex = i | ||
uc.agentPool = pool | ||
uc.agentPoolIndex = i | ||
} | ||
} | ||
if agentPoolIndex == -1 { | ||
return errors.Errorf("node pool %s was not found in the deployed api model", uc.agentPoolToUpdate) | ||
} | ||
if uc.agentPool.AvailabilityProfile != api.VirtualMachineScaleSets { | ||
return errors.Errorf("aks-engine node pool update requires a VMSS node pool, %s is backed by a VM Availability Set", uc.agentPoolToUpdate) | ||
} | ||
|
||
//allows to identify VMs in the resource group that belong to this cluster. | ||
uc.nameSuffix = uc.containerService.Properties.GetClusterID() | ||
log.Debugf("Cluster ID used in all agent pools: %s", uc.nameSuffix) | ||
|
||
uc.kubeconfig, err = engine.GenerateKubeConfig(uc.containerService.Properties, uc.location) | ||
if err != nil { | ||
return errors.New("Unable to derive kubeconfig from api model") | ||
} | ||
return nil | ||
} | ||
|
||
func (uc *updateCmd) run(cmd *cobra.Command, args []string) error { | ||
if err := uc.validate(cmd); err != nil { | ||
return errors.Wrap(err, "failed to validate scale command") | ||
} | ||
if err := uc.load(); err != nil { | ||
return errors.Wrap(err, "failed to load existing container service") | ||
} | ||
|
||
ctx, cancel := context.WithTimeout(context.Background(), armhelpers.DefaultARMOperationTimeout) | ||
defer cancel() | ||
|
||
sc := scaleCmd{ | ||
location: uc.location, | ||
apiModelPath: uc.apiModelPath, | ||
resourceGroupName: uc.resourceGroupName, | ||
agentPoolToScale: uc.agentPoolToUpdate, | ||
validateCmd: false, | ||
updateVMSSModel: true, | ||
loadAPIModel: false, | ||
persistAPIModel: false, | ||
} | ||
sc.RawAzureEnvironment = uc.RawAzureEnvironment | ||
sc.rawSubscriptionID = uc.rawSubscriptionID | ||
sc.SubscriptionID = uc.SubscriptionID | ||
sc.AuthMethod = uc.AuthMethod | ||
sc.rawClientID = uc.rawClientID | ||
sc.ClientID = uc.ClientID | ||
sc.ClientSecret = uc.ClientSecret | ||
sc.CertificatePath = uc.CertificatePath | ||
sc.PrivateKeyPath = uc.PrivateKeyPath | ||
sc.IdentitySystem = uc.IdentitySystem | ||
sc.language = uc.language | ||
sc.logger = uc.logger | ||
sc.containerService = uc.containerService | ||
sc.apiVersion = uc.apiVersion | ||
sc.client = uc.client | ||
sc.nameSuffix = uc.nameSuffix | ||
sc.kubeconfig = uc.kubeconfig | ||
sc.agentPool = uc.agentPool | ||
sc.agentPoolIndex = uc.agentPoolIndex | ||
|
||
for vmssListPage, err := sc.client.ListVirtualMachineScaleSets(ctx, sc.resourceGroupName); vmssListPage.NotDone(); err = vmssListPage.NextWithContext(ctx) { | ||
if err != nil { | ||
return errors.Wrap(err, "failed to get VMSS list in the resource group") | ||
} | ||
for _, vmss := range vmssListPage.Values() { | ||
vmssName := *vmss.Name | ||
if sc.agentPool.OSType == api.Windows { | ||
possibleIndex, nameMungingErr := strconv.Atoi(vmssName[len(vmssName)-2:]) | ||
if nameMungingErr != nil { | ||
continue | ||
} | ||
if !(sc.containerService.Properties.GetAgentVMPrefix(sc.agentPool, possibleIndex) == vmssName) { | ||
continue | ||
} | ||
} else { | ||
if !sc.vmInAgentPool(vmssName, vmss.Tags) { | ||
continue | ||
} | ||
} | ||
|
||
if vmss.Sku != nil { | ||
sc.newDesiredAgentCount = int(*vmss.Sku.Capacity) | ||
uc.agentPool.Count = sc.newDesiredAgentCount | ||
} else { | ||
return errors.Wrap(err, fmt.Sprintf("failed to find VMSS matching node pool %s in resource group %s", sc.agentPoolToScale, sc.resourceGroupName)) | ||
} | ||
} | ||
} | ||
|
||
err := sc.run(cmd, args) | ||
if err != nil { | ||
return errors.Wrap(err, "aks-engine update failed") | ||
} | ||
|
||
return nil | ||
} |
Oops, something went wrong.