diff --git a/README.md b/README.md
index 67b13829..cd1e0ca9 100644
--- a/README.md
+++ b/README.md
@@ -408,7 +408,7 @@ proxy_url = VALUE
| [function\_app\_storage\_account\_prefix](#input\_function\_app\_storage\_account\_prefix) | Weka storage account name prefix | `string` | `"weka"` | no |
| [function\_app\_subnet\_delegation\_cidr](#input\_function\_app\_subnet\_delegation\_cidr) | Subnet delegation enables you to designate a specific subnet for an Azure PaaS service. | `string` | `"10.0.1.0/25"` | no |
| [function\_app\_subnet\_delegation\_id](#input\_function\_app\_subnet\_delegation\_id) | Required to specify if subnet\_name were used to specify pre-defined subnets for weka. Function subnet delegation requires an additional subnet, and in the case of pre-defined networking this one also should be pre-defined | `string` | `""` | no |
-| [function\_app\_version](#input\_function\_app\_version) | Function app code version (hash) | `string` | `"9611fb1feebc0c4e3f8b34671fa909fe"` | no |
+| [function\_app\_version](#input\_function\_app\_version) | Function app code version (hash) | `string` | `"abe83f3f0224c29630262452d616dffd"` | no |
| [get\_weka\_io\_token](#input\_get\_weka\_io\_token) | The token to download the Weka release from get.weka.io. | `string` | `""` | no |
| [hotspare](#input\_hotspare) | Number of hotspares to set on weka cluster. Refer to https://docs.weka.io/overview/ssd-capacity-management#hot-spare | `number` | `1` | no |
| [install\_cluster\_dpdk](#input\_install\_cluster\_dpdk) | Install weka cluster with DPDK | `bool` | `true` | no |
diff --git a/function-app/code/common/common.go b/function-app/code/common/common.go
index 5d01e7b7..e13a8c00 100644
--- a/function-app/code/common/common.go
+++ b/function-app/code/common/common.go
@@ -2,7 +2,9 @@ package common
import (
"context"
+ "crypto/sha256"
"encoding/json"
+ "errors"
"fmt"
"io"
"net/http"
@@ -962,7 +964,7 @@ func RetrySetDeletionProtectionAndReport(
counter++
// deletion protection invoked by terminate function
if maxAttempts == 0 {
- msg := fmt.Sprintf("Deletion protection set authorization isn't ready, will retry on next scale down workflow")
+ msg := "Deletion protection set authorization isn't ready, will retry on next scale down workflow"
ReportMsg(ctx, hostName, subscriptionId, resourceGroupName, stateContainerName, stateStorageName, "debug", msg)
return
}
@@ -994,8 +996,11 @@ func GetWekaClusterPassword(ctx context.Context, keyVaultUri string) (password s
return GetKeyVaultValue(ctx, keyVaultUri, "weka-password")
}
-func GetVmScaleSetName(prefix, clusterName string, version string) string {
- return fmt.Sprintf("%s-%s-vmss-%s", prefix, clusterName, version)
+func GetVmScaleSetName(prefix, clusterName string, version int) string {
+ if version == 0 {
+ return fmt.Sprintf("%s-%s-vmss", prefix, clusterName)
+ }
+ return fmt.Sprintf("%s-%s-vmss-v%d", prefix, clusterName, version)
}
func GetScaleSetInstanceIds(ctx context.Context, subscriptionId, resourceGroupName, vmScaleSetName string) (instanceIds []string, err error) {
@@ -1111,12 +1116,19 @@ func ReadVmssConfig(ctx context.Context, storageName, containerName string) (vms
if err != nil {
return
}
+
+ // calculate hash of the config (used to identify vmss config changes)
+ // take first 10 chars of the hash
+ hash := sha256.Sum256(asByteArray)
+ hashStr := fmt.Sprintf("%x", hash)
+
err = json.Unmarshal(asByteArray, &vmssConfig)
if err != nil {
logger.Error().Err(err).Send()
return
}
+ vmssConfig.ConfigHash = hashStr[:10]
return
}
@@ -1233,23 +1245,29 @@ func GetScaleSetNameWithLatestConfiguration(ctx context.Context, subscriptionId,
return scaleSetName, nil
}
-func GetScaleSetsList(ctx context.Context, subscriptionId, resourceGroupName string, scaleSetNames []string) (scaleSets []*armcompute.VirtualMachineScaleSet, err error) {
+func GetScaleSetsByVersion(ctx context.Context, subscriptionId, resourceGroupName string, vmssState *VMSSState) (map[int]*armcompute.VirtualMachineScaleSet, error) {
logger := logging.LoggerFromCtx(ctx)
- for _, scaleSetName := range scaleSetNames {
+ scaleSetNames := make([]string, 0)
+ scaleSetsByVersion := make(map[int]*armcompute.VirtualMachineScaleSet, len(vmssState.Versions))
+
+ for _, version := range vmssState.Versions {
+ scaleSetName := GetVmScaleSetName(vmssState.Prefix, vmssState.ClusterName, version)
scaleSet, err := getScaleSet(ctx, subscriptionId, resourceGroupName, scaleSetName)
if err != nil {
// if scale set not found, ignore
- if getErr, ok := err.(*azcore.ResponseError); ok && getErr.ErrorCode == "ResourceNotFound" {
+ var responseErr *azcore.ResponseError
+ if errors.As(err, &responseErr) && responseErr.ErrorCode == "ResourceNotFound" {
continue
}
return nil, err
}
- scaleSets = append(scaleSets, scaleSet)
+ scaleSetsByVersion[version] = scaleSet
+ scaleSetNames = append(scaleSetNames, scaleSetName)
}
- logger.Info().Msgf("Found %d scale sets from list %v", len(scaleSets), scaleSetNames)
- return
+ logger.Info().Msgf("Found %d scale sets from list %v", len(scaleSetsByVersion), scaleSetNames)
+ return scaleSetsByVersion, nil
}
func GetVmssConfig(ctx context.Context, resourceGroupName string, scaleSet *armcompute.VirtualMachineScaleSet) *VMSSConfig {
@@ -1388,7 +1406,7 @@ func CreateOrUpdateVmss(ctx context.Context, subscriptionId, resourceGroupName,
return
}
- config.Tags["version"] = configHash
+ config.Tags["config_hash"] = configHash
config.Tags["config_applied_at"] = time.Now().Format(time.RFC3339)
size := int64(vmssSize)
forceDeletion := false
diff --git a/function-app/code/common/vmss_config.go b/function-app/code/common/vmss_config.go
index 0891933a..192ee2c5 100644
--- a/function-app/code/common/vmss_config.go
+++ b/function-app/code/common/vmss_config.go
@@ -1,8 +1,6 @@
package common
import (
- "crypto/sha256"
- "encoding/json"
"fmt"
"strings"
@@ -84,30 +82,21 @@ type VMSSConfig struct {
DataDisk DataDisk `json:"data_disk"`
PrimaryNIC PrimaryNIC `json:"primary_nic"`
SecondaryNICs SecondaryNICs `json:"secondary_nics"`
-}
-
-func GetConfigHash(c VMSSConfig) (string, error) {
- // calculate hash of the config (used to identify vmss config changes)
- // take first 10 chars of the hash
- jsonData, err := json.Marshal(c)
- if err != nil {
- return "", fmt.Errorf("cannot marshal vmss config: %v", err)
- }
- hash := sha256.Sum256(jsonData)
- hashStr := fmt.Sprintf("%x", hash)
- return hashStr[:10], nil
+ // ignore the following fields when marshaling to json
+ ConfigHash string `json:"-"`
}
// Compares two vmss configs - works with copies of VMSSConfig structs
// NOTES:
-// - does not compare "version" and "config_applied_at" tags, and names which include version
+// - does not compare "config_hash" and "config_applied_at" tags, and names which include version
func VmssConfigsDiff(old, new VMSSConfig) string {
old.CustomData, new.CustomData = "", ""
- old.Tags["version"], new.Tags["version"] = "", ""
+ old.Tags["config_hash"], new.Tags["config_hash"] = "", ""
old.Tags["config_applied_at"], new.Tags["config_applied_at"] = "", ""
old.ComputerNamePrefix, new.ComputerNamePrefix = "", ""
old.Name, new.Name = "", ""
+ old.ConfigHash, new.ConfigHash = "", ""
if len(old.PrimaryNIC.IPConfigurations) == len(new.PrimaryNIC.IPConfigurations) {
for i := range old.PrimaryNIC.IPConfigurations {
@@ -121,7 +110,7 @@ func VmssConfigsDiff(old, new VMSSConfig) string {
old.OSDisk.SizeGB = nil
}
- return cmp.Diff(new, old) // arguments order: (want, got)
+ return cmp.Diff(old, new) // arguments order: (want, got)
}
func GetRefreshVmssName(outdatedVmssName string, currentVmssVersion uint16) string {
@@ -133,12 +122,25 @@ func GetRefreshVmssName(outdatedVmssName string, currentVmssVersion uint16) stri
}
type VMSSState struct {
- Prefix string `json:"prefix"`
- ClusterName string `json:"cluster_name"`
- Versions []string `json:"active_versions"`
+ Prefix string `json:"prefix"`
+ ClusterName string `json:"cluster_name"`
+ Versions []int `json:"active_versions"`
}
-func (q *VMSSState) AddVersion(item string) {
+func (q *VMSSState) DeduceNextVersion() int {
+ if len(q.Versions) == 0 {
+ return 0
+ }
+ maxVersion := q.Versions[0]
+ for _, v := range q.Versions {
+ if v > maxVersion {
+ maxVersion = v
+ }
+ }
+ return maxVersion + 1
+}
+
+func (q *VMSSState) AddVersion(item int) {
// make sure version is added in the end of the queue
// and there are no duplicates
for i, v := range q.Versions {
@@ -152,7 +154,7 @@ func (q *VMSSState) AddVersion(item string) {
q.Versions = append(q.Versions, item)
}
-func (q *VMSSState) GetLatestVersion() string {
+func (q *VMSSState) GetLatestVersion() int {
return q.Versions[len(q.Versions)-1]
}
@@ -160,16 +162,7 @@ func (q *VMSSState) IsEmpty() bool {
return len(q.Versions) == 0
}
-func (q *VMSSState) ReplaceVersion(old, new string) {
- for i, v := range q.Versions {
- if v == old {
- q.Versions[i] = new
- return
- }
- }
-}
-
-func (q *VMSSState) RemoveVersion(item string) error {
+func (q *VMSSState) RemoveVersion(item int) error {
for i, v := range q.Versions {
// do not allow removing the last element of array
if v == item && i != len(q.Versions)-1 {
@@ -179,7 +172,7 @@ func (q *VMSSState) RemoveVersion(item string) error {
return fmt.Errorf("cannot remove the latest version from the queue")
}
}
- return fmt.Errorf("version %s not found in the queue", item)
+ return fmt.Errorf("version %d not found in the queue", item)
}
type VMSSStateVerbose struct {
diff --git a/function-app/code/functions/clusterize/clusterize.go b/function-app/code/functions/clusterize/clusterize.go
index 4582bba9..094ab4b1 100644
--- a/function-app/code/functions/clusterize/clusterize.go
+++ b/function-app/code/functions/clusterize/clusterize.go
@@ -176,14 +176,8 @@ func Clusterize(ctx context.Context, p ClusterizationParams) (clusterizeScript s
instanceName := strings.Split(p.VmName, ":")[0]
instanceId := common.GetScaleSetVmIndex(instanceName)
- vmssState, err := common.ReadVmssState(ctx, p.StateStorageName, p.StateContainerName)
- if err != nil {
- err = fmt.Errorf("failed to read vmss state: %w", err)
- logger.Error().Err(err).Send()
- return
- }
-
- vmScaleSetName := common.GetVmScaleSetName(p.Prefix, p.Cluster.ClusterName, vmssState.GetLatestVersion())
+ version := 0 // on cluserization step we are sure that the vmss version is 0 as no refresh was done yet
+ vmScaleSetName := common.GetVmScaleSetName(p.Prefix, p.Cluster.ClusterName, version)
vmName := p.VmName
ip, err := common.GetPublicIp(ctx, p.SubscriptionId, p.ResourceGroupName, vmScaleSetName, p.Prefix, p.Cluster.ClusterName, instanceId)
diff --git a/function-app/code/functions/scale_up/scale_up.go b/function-app/code/functions/scale_up/scale_up.go
index 4bbc02f9..4f5a9175 100644
--- a/function-app/code/functions/scale_up/scale_up.go
+++ b/function-app/code/functions/scale_up/scale_up.go
@@ -2,6 +2,7 @@ package scale_up
import (
"context"
+ "errors"
"fmt"
"net/http"
"os"
@@ -49,9 +50,9 @@ func Handler(w http.ResponseWriter, r *http.Request) {
}
scaleSetNames := common.GetScaleSetsNamesFromVmssState(ctx, subscriptionId, resourceGroupName, &vmssState)
- scaleSets, err := common.GetScaleSetsList(ctx, subscriptionId, resourceGroupName, scaleSetNames)
+ scaleSetsByVersion, err := common.GetScaleSetsByVersion(ctx, subscriptionId, resourceGroupName, &vmssState)
if err != nil {
- logger.Error().Err(err).Msgf("cannot get scale sets list")
+ logger.Error().Err(err).Msgf("cannot get scale sets")
common.WriteErrorResponse(w, err)
return
}
@@ -64,16 +65,9 @@ func Handler(w http.ResponseWriter, r *http.Request) {
return
}
- targetVersion, err := common.GetConfigHash(vmssConfig)
- if err != nil {
- logger.Error().Err(err).Msgf("cannot get vmss config hash")
- common.WriteErrorResponse(w, err)
- return
- }
-
// 1. Initial VMSS creation flow: initiale vmss creation if needed
if vmssState.IsEmpty() && !state.Clusterized {
- err := createVmss(ctx, &vmssConfig, &vmssState, state.DesiredSize, targetVersion)
+ err := createVmss(ctx, &vmssConfig, &vmssState, state.DesiredSize)
if err != nil {
logger.Error().Err(err).Msgf("cannot create initial vmss")
common.WriteErrorResponse(w, err)
@@ -89,22 +83,28 @@ func Handler(w http.ResponseWriter, r *http.Request) {
return
}
- if len(scaleSets) == 0 && !vmssState.IsEmpty() {
+ if len(scaleSetsByVersion) == 0 && !vmssState.IsEmpty() {
err := fmt.Errorf("cannot find scale sets %v", scaleSetNames)
logger.Error().Err(err).Send()
common.WriteErrorResponse(w, err)
return
}
- latestVmss := scaleSets[len(scaleSets)-1]
+ latestVmss, ok := scaleSetsByVersion[vmssState.GetLatestVersion()]
+ if !ok {
+ err := fmt.Errorf("cannot find latest vmss")
+ logger.Error().Err(err).Send()
+ common.WriteErrorResponse(w, err)
+ return
+ }
currentConfig := common.GetVmssConfig(ctx, resourceGroupName, latestVmss)
// 2. Update flow: compare current vmss config with expected vmss config and update if needed
- if !targetVersionIsLatestVersion(vmssState.Versions, targetVersion) {
+ if !targetConfigIsLatestConfig(latestVmss, vmssConfig.ConfigHash) {
diff := common.VmssConfigsDiff(*currentConfig, vmssConfig)
logger.Info().Msgf("vmss config diff: %s", diff)
- err := HandleVmssUpdate(ctx, currentConfig, &vmssConfig, &vmssState, state.DesiredSize, targetVersion)
+ err := HandleVmssUpdate(ctx, currentConfig, &vmssConfig, &vmssState, state.DesiredSize)
if err != nil {
common.WriteErrorResponse(w, err)
return
@@ -115,8 +115,8 @@ func Handler(w http.ResponseWriter, r *http.Request) {
returnMsg := ""
// 3. Refresh in progress flow: handle vmss refresh if needed
- if len(scaleSets) > 1 && targetVersionIsLatestVersion(vmssState.Versions, targetVersion) {
- err := progressVmssRefresh(ctx, scaleSets, &vmssState, state.DesiredSize)
+ if len(scaleSetsByVersion) > 1 && targetConfigIsLatestConfig(latestVmss, vmssConfig.ConfigHash) {
+ err := progressVmssRefresh(ctx, scaleSetsByVersion, &vmssState, state.DesiredSize)
if err != nil {
common.WriteErrorResponse(w, err)
return
@@ -134,15 +134,11 @@ func Handler(w http.ResponseWriter, r *http.Request) {
common.WriteSuccessResponse(w, returnMsg)
}
-func HandleVmssUpdate(ctx context.Context, currentConfig, newConfig *common.VMSSConfig, vmssState *common.VMSSState, desiredSize int, newVersion string) error {
+func HandleVmssUpdate(ctx context.Context, currentConfig, newConfig *common.VMSSConfig, vmssState *common.VMSSState, desiredSize int) error {
logger := logging.LoggerFromCtx(ctx)
- logger.Info().Msgf("updating vmss %s", currentConfig.Name)
- // leaseId, err := common.LockContainer(ctx, stateStorageName, stateContainerName)
- // if err != nil {
- // return err
- // }
- // defer common.UnlockContainer(ctx, stateStorageName, stateContainerName, leaseId)
+ newConfigHash := newConfig.ConfigHash
+ logger.Info().Msgf("updating vmss %s to new config_hash %s", currentConfig.Name, newConfigHash)
refreshNeeded := false
if currentConfig.SKU != newConfig.SKU {
@@ -150,78 +146,43 @@ func HandleVmssUpdate(ctx context.Context, currentConfig, newConfig *common.VMSS
logger.Info().Msg(msg)
refreshNeeded = true
} else {
- _, err := common.CreateOrUpdateVmss(ctx, subscriptionId, resourceGroupName, currentConfig.Name, newVersion, *newConfig, desiredSize)
+ _, err := common.CreateOrUpdateVmss(ctx, subscriptionId, resourceGroupName, currentConfig.Name, newConfigHash, *newConfig, desiredSize)
if err != nil {
- if updErr, ok := err.(*azcore.ResponseError); ok && updErr.ErrorCode == "PropertyChangeNotAllowed" {
+ var responseErr *azcore.ResponseError
+ if errors.As(err, &responseErr) && responseErr.ErrorCode == "PropertyChangeNotAllowed" {
refreshNeeded = true
}
logger.Error().Err(err).Msgf("cannot update vmss %s", currentConfig.Name)
- return err
} else {
- // replace active version in vmss state
- vmssState.ReplaceVersion(currentConfig.Tags["version"], newVersion)
- err = common.WriteVmssState(ctx, stateStorageName, stateContainerName, *vmssState)
- if err != nil {
- err = fmt.Errorf("cannot write vmss state: %w", err)
- return err
- }
+ logger.Info().Msgf("updated vmss %s to new config_hash %s", currentConfig.Name, newConfigHash)
}
}
if refreshNeeded {
- err := initiateVmssRefresh(ctx, newConfig, vmssState, newVersion, desiredSize)
+ err := createVmss(ctx, newConfig, vmssState, desiredSize)
if err != nil {
- logger.Error().Err(err).Msgf("cannot initiate vmss refresh")
+ logger.Error().Err(err).Msg("cannot create 'refresh' vmss")
return err
}
logger.Info().Msgf("initiated vmss refresh")
- return nil
}
-
- logger.Info().Msgf("updated vmss %s", currentConfig.Name)
return nil
}
-func initiateVmssRefresh(ctx context.Context, vmssConfig *common.VMSSConfig, vmssState *common.VMSSState, newVersion string, desiredSize int) error {
- // Make sure that vmss current size is equal to "desired" number of weka instances
- logger := logging.LoggerFromCtx(ctx)
- logger.Info().Msgf("initiate vmss refresh")
-
- newVmssName := common.GetVmScaleSetName(prefix, clusterName, newVersion)
-
- // if public ip address is assigned to vmss, domainNameLabel should differ (avoid VMScaleSetDnsRecordsInUse error)
- for i := range vmssConfig.PrimaryNIC.IPConfigurations {
- newDnsLabelName := fmt.Sprintf("%s-v%s", vmssConfig.PrimaryNIC.IPConfigurations[i].PublicIPAddress.DomainNameLabel, newVersion)
- vmssConfig.PrimaryNIC.IPConfigurations[i].PublicIPAddress.DomainNameLabel = newDnsLabelName
- }
-
- // update hostname prefix
- vmssConfig.ComputerNamePrefix = fmt.Sprintf("%s-v%s", vmssConfig.ComputerNamePrefix, newVersion)
-
- logger.Info().Msgf("creating new vmss %s of size %d", newVmssName, desiredSize)
-
- err := createVmss(ctx, vmssConfig, vmssState, desiredSize, newVersion)
- if err != nil {
- logger.Error().Err(err).Msgf("cannot create 'refresh' vmss %s", newVmssName)
- return err
- }
- return nil
-}
-
-func progressVmssRefresh(ctx context.Context, activeScaleSets []*armcompute.VirtualMachineScaleSet, vmssState *common.VMSSState, desiredSize int) error {
+func progressVmssRefresh(ctx context.Context, scaleSetsByVersion map[int]*armcompute.VirtualMachineScaleSet, vmssState *common.VMSSState, desiredSize int) error {
// Terminology:
// "Outdated" vmss -- vmss that was used before refresh
// "Refresh" vmss -- vmss that was created during refresh
// "desired" number of weka instances -- number of weka instances expected by the user (stored in state)
logger := logging.LoggerFromCtx(ctx)
logger.Info().Msg("progressing vmss refresh")
- logger.Info().Msgf("active scale sets number: %d", len(activeScaleSets))
+ logger.Info().Msgf("active scale sets number: %d", len(scaleSetsByVersion))
- latestVmss := activeScaleSets[len(activeScaleSets)-1]
- latestVmssSize := int(*latestVmss.SKU.Capacity)
outdatedVmssTotalSize := 0
-
- for _, vmss := range activeScaleSets[:len(activeScaleSets)-1] {
+ for version, vmss := range scaleSetsByVersion {
+ if version == vmssState.GetLatestVersion() {
+ continue
+ }
size := int(*vmss.SKU.Capacity)
outdatedVmssTotalSize += size
@@ -233,9 +194,8 @@ func progressVmssRefresh(ctx context.Context, activeScaleSets []*armcompute.Virt
logger.Error().Err(err).Send()
return err
}
- // delete outdated hash from vmss state
- version := vmss.Tags["version"]
- err = vmssState.RemoveVersion(*version)
+ // delete outdated vmss version from state
+ err = vmssState.RemoveVersion(version)
if err != nil {
logger.Error().Err(err).Send()
return err
@@ -248,17 +208,32 @@ func progressVmssRefresh(ctx context.Context, activeScaleSets []*armcompute.Virt
}
}
+ latestVmss := scaleSetsByVersion[vmssState.GetLatestVersion()]
+ latestVmssSize := int(*latestVmss.SKU.Capacity)
logger.Info().Msgf("refresh vmss (%s) size is %d, outdated vmss(es) total size is %d", *latestVmss.Name, latestVmssSize, outdatedVmssTotalSize)
return nil
}
-func createVmss(ctx context.Context, vmssConfig *common.VMSSConfig, vmssState *common.VMSSState, vmssSize int, vmssVersion string) error {
+func createVmss(ctx context.Context, vmssConfig *common.VMSSConfig, vmssState *common.VMSSState, vmssSize int) error {
logger := logging.LoggerFromCtx(ctx)
+ vmssVersion := vmssState.DeduceNextVersion()
+ vmssConfigHash := vmssConfig.ConfigHash
vmssName := common.GetVmScaleSetName(prefix, clusterName, vmssVersion)
- logger.Info().Msgf("creating vmss %s", vmssName)
- vmssId, err := common.CreateOrUpdateVmss(ctx, subscriptionId, resourceGroupName, vmssName, vmssVersion, *vmssConfig, vmssSize)
+ if vmssVersion > 0 {
+ // if public ip address is assigned to vmss, domainNameLabel should differ (avoid VMScaleSetDnsRecordsInUse error)
+ for i := range vmssConfig.PrimaryNIC.IPConfigurations {
+ newDnsLabelName := fmt.Sprintf("%s-v%d", vmssConfig.PrimaryNIC.IPConfigurations[i].PublicIPAddress.DomainNameLabel, vmssVersion)
+ vmssConfig.PrimaryNIC.IPConfigurations[i].PublicIPAddress.DomainNameLabel = newDnsLabelName
+ }
+ // update hostname prefix
+ vmssConfig.ComputerNamePrefix = fmt.Sprintf("%s-v%d", vmssConfig.ComputerNamePrefix, vmssVersion)
+ }
+
+ logger.Info().Msgf("creating new vmss %s of size %d", vmssName, vmssSize)
+
+ vmssId, err := common.CreateOrUpdateVmss(ctx, subscriptionId, resourceGroupName, vmssName, vmssConfigHash, *vmssConfig, vmssSize)
if err != nil {
return err
}
@@ -272,7 +247,8 @@ func createVmss(ctx context.Context, vmssConfig *common.VMSSConfig, vmssState *c
err = common.AssignVmssContributorRoleToFunctionApp(ctx, subscriptionId, resourceGroupName, *vmssId, functionAppName)
if err != nil {
- if getErr, ok := err.(*azcore.ResponseError); ok && getErr.ErrorCode == "RoleAssignmentExists" {
+ var responseErr *azcore.ResponseError
+ if errors.As(err, &responseErr) && (responseErr.ErrorCode == "RoleAssignmentExists" || responseErr.RawResponse.StatusCode == 409) {
logger.Info().Msgf("vmss %s 'contributor' role is already assigned to function app", vmssName)
return nil
}
@@ -284,9 +260,7 @@ func createVmss(ctx context.Context, vmssConfig *common.VMSSConfig, vmssState *c
return nil
}
-func targetVersionIsLatestVersion(activeVersions []string, targetVersion string) bool {
- if len(activeVersions) == 0 {
- return false
- }
- return activeVersions[len(activeVersions)-1] == targetVersion
+func targetConfigIsLatestConfig(latestScaleSet *armcompute.VirtualMachineScaleSet, targetVersion string) bool {
+ configHash := latestScaleSet.Tags["config_hash"]
+ return configHash != nil && *configHash == targetVersion
}
diff --git a/variables.tf b/variables.tf
index c965a01f..3199f4e9 100644
--- a/variables.tf
+++ b/variables.tf
@@ -321,7 +321,7 @@ variable "function_app_storage_account_container_prefix" {
variable "function_app_version" {
type = string
description = "Function app code version (hash)"
- default = "9611fb1feebc0c4e3f8b34671fa909fe"
+ default = "abe83f3f0224c29630262452d616dffd"
}
variable "function_app_dist" {