sigs.k8s.io/cluster-api-provider-azure@v1.17.0/test/e2e/aks_versions.go (about) 1 //go:build e2e 2 // +build e2e 3 4 /* 5 Copyright 2022 The Kubernetes Authors. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 */ 19 20 package e2e 21 22 import ( 23 "context" 24 "fmt" 25 26 "github.com/Azure/azure-sdk-for-go/sdk/azidentity" 27 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v4" 28 . "github.com/onsi/gomega" 29 "github.com/pkg/errors" 30 "golang.org/x/mod/semver" 31 "k8s.io/utils/ptr" 32 "sigs.k8s.io/cluster-api/test/framework/clusterctl" 33 ) 34 35 // GetAKSKubernetesVersion gets the kubernetes version for AKS clusters as specified by the environment variable defined by versionVar. 36 func GetAKSKubernetesVersion(ctx context.Context, e2eConfig *clusterctl.E2EConfig, versionVar string) (string, error) { 37 e2eAKSVersion := e2eConfig.GetVariable(versionVar) 38 location := e2eConfig.GetVariable(AzureLocation) 39 subscriptionID := getSubscriptionID(Default) 40 41 var err error 42 var maxVersion string 43 switch e2eAKSVersion { 44 case "latest": 45 maxVersion, err = GetLatestStableAKSKubernetesVersion(ctx, subscriptionID, location) 46 Expect(err).NotTo(HaveOccurred()) 47 case "latest-1": 48 maxVersion, err = GetNextLatestStableAKSKubernetesVersion(ctx, subscriptionID, location) 49 Expect(err).NotTo(HaveOccurred()) 50 default: 51 maxVersion, err = GetWorkingAKSKubernetesVersion(ctx, subscriptionID, location, e2eAKSVersion) 52 Expect(err).NotTo(HaveOccurred()) 53 } 54 55 return maxVersion, nil 56 } 57 58 // GetWorkingAKSKubernetesVersion returns an available Kubernetes version of AKS given a desired semver version, if possible. 59 // If the desired version is available, we return it. 60 // If the desired version is not available, we check for any available patch version using desired version's Major.Minor semver release. 61 // If no versions are available in the desired version's Major.Minor semver release, we return an error. 62 func GetWorkingAKSKubernetesVersion(ctx context.Context, subscriptionID, location, version string) (string, error) { 63 cred, err := azidentity.NewDefaultAzureCredential(nil) 64 if err != nil { 65 return "", errors.Wrap(err, "failed to create a default credential") 66 } 67 managedClustersClient, err := armcontainerservice.NewManagedClustersClient(subscriptionID, cred, nil) 68 if err != nil { 69 return "", errors.Wrap(err, "failed to create a ContainerServices client") 70 } 71 result, err := managedClustersClient.ListKubernetesVersions(ctx, location, nil) 72 if err != nil { 73 return "", errors.Wrap(err, "failed to list Orchestrators") 74 } 75 76 var latestStableVersionDesired bool 77 // We're not doing much input validation here, 78 // we assume that if the prefix is 'stable-' that the remainder of the string is in the format <Major>.<Minor> 79 if isStableVersion, _ := validateStableReleaseString(version); isStableVersion { 80 latestStableVersionDesired = true 81 // Form a fully valid semver version @ the initial patch release (".0") 82 version = fmt.Sprintf("%s.0", version[7:]) 83 } 84 85 // semver comparisons below require a "v" prefix 86 if version[:1] != "v" { 87 version = fmt.Sprintf("v%s", version) 88 } 89 // Create a var of the patch ".0" equivalent of the inputted version 90 baseVersion := fmt.Sprintf("%s.0", semver.MajorMinor(version)) 91 maxVersion := fmt.Sprintf("%s.0", semver.MajorMinor(version)) 92 var foundWorkingVersion bool 93 for _, minor := range result.KubernetesVersionListResult.Values { 94 for patch := range minor.PatchVersions { 95 orchVersion := patch 96 97 // semver comparisons require a "v" prefix 98 if patch[:1] != "v" { 99 orchVersion = "v" + patch 100 } 101 if semver.MajorMinor(orchVersion) != semver.MajorMinor(baseVersion) { 102 continue 103 } 104 // if the inputted version matches with an available AKS version we can return immediately 105 if orchVersion == version && !latestStableVersionDesired { 106 return version, nil 107 } 108 // or, keep track of the highest aks version for a given major.minor 109 if semver.Compare(orchVersion, maxVersion) >= 0 { 110 maxVersion = orchVersion 111 foundWorkingVersion = true 112 } 113 } 114 } 115 116 // This means there is no version supported by AKS for this major.minor 117 if !foundWorkingVersion { 118 return "", errors.New(fmt.Sprintf("No AKS versions found for %s", semver.MajorMinor(baseVersion))) 119 } 120 121 return maxVersion, nil 122 } 123 124 // GetLatestStableAKSKubernetesVersion returns the latest stable available Kubernetes version of AKS. 125 func GetLatestStableAKSKubernetesVersion(ctx context.Context, subscriptionID, location string) (string, error) { 126 return getLatestStableAKSKubernetesVersionOffset(ctx, subscriptionID, location, 0) 127 } 128 129 // GetNextLatestStableAKSKubernetesVersion returns the stable available 130 // Kubernetes version of AKS immediately preceding the latest. 131 func GetNextLatestStableAKSKubernetesVersion(ctx context.Context, subscriptionID, location string) (string, error) { 132 return getLatestStableAKSKubernetesVersionOffset(ctx, subscriptionID, location, 1) 133 } 134 135 func getLatestStableAKSKubernetesVersionOffset(ctx context.Context, subscriptionID, location string, offset int) (string, error) { 136 cred, err := azidentity.NewDefaultAzureCredential(nil) 137 if err != nil { 138 return "", errors.Wrap(err, "failed to create a default credential") 139 } 140 managedClustersClient, err := armcontainerservice.NewManagedClustersClient(subscriptionID, cred, nil) 141 if err != nil { 142 return "", errors.Wrap(err, "failed to create a ContainerServices client") 143 } 144 result, err := managedClustersClient.ListKubernetesVersions(ctx, location, nil) 145 if err != nil { 146 return "", errors.Wrap(err, "failed to list Orchestrators") 147 } 148 149 var orchestratorversions []string 150 var foundWorkingVersion bool 151 var version string 152 var maxVersion string 153 154 for _, minor := range result.KubernetesVersionListResult.Values { 155 for patch := range minor.PatchVersions { 156 // semver comparisons require a "v" prefix 157 if patch[:1] != "v" && !ptr.Deref(minor.IsPreview, false) { 158 version = "v" + patch 159 } 160 orchestratorversions = append(orchestratorversions, version) 161 } 162 } 163 semver.Sort(orchestratorversions) 164 maxVersion = orchestratorversions[len(orchestratorversions)-1-offset] 165 if semver.IsValid(maxVersion) { 166 foundWorkingVersion = true 167 } 168 if !foundWorkingVersion { 169 return "", errors.New("latest stable AKS version not found") 170 } 171 return maxVersion, nil 172 }