55 "fmt"
66 "net/http"
77 "regexp"
8+ "sort"
89 "strings"
910 "time"
1011
@@ -743,6 +744,33 @@ func (r *clusterResource) Create(ctx context.Context, req resource.CreateRequest
743744 tflog .Info (ctx , "SKE cluster created" )
744745}
745746
747+ func sortK8sVersions (versions []ske.KubernetesVersion ) {
748+ sort .Slice (versions , func (i , j int ) bool {
749+ v1 , v2 := (versions )[i ].Version , (versions )[j ].Version
750+ if v1 == nil {
751+ return false
752+ }
753+ if v2 == nil {
754+ return true
755+ }
756+
757+ // we have to make copies of the input strings to add prefixes,
758+ // otherwise we would be changing the passed elements
759+ t1 , t2 := * v1 , * v2
760+
761+ if ! strings .HasPrefix (t1 , "v" ) {
762+ t1 = "v" + t1
763+ }
764+ if ! strings .HasPrefix (t2 , "v" ) {
765+ t2 = "v" + t2
766+ }
767+ return semver .Compare (t1 , t2 ) > 0
768+ })
769+ }
770+
771+ // loadAvailableVersions loads the available k8s and machine versions from the API.
772+ // The k8s versions are sorted descending order, i.e. the latest versions (including previews)
773+ // are listed first
746774func (r * clusterResource ) loadAvailableVersions (ctx context.Context ) ([]ske.KubernetesVersion , []ske.MachineImage , error ) {
747775 c := r .skeClient
748776 res , err := c .ListProviderOptions (ctx ).Execute ()
@@ -793,7 +821,7 @@ func (r *clusterResource) createOrUpdateCluster(ctx context.Context, diags *diag
793821 // cluster vars
794822 projectId := model .ProjectId .ValueString ()
795823 name := model .Name .ValueString ()
796- kubernetes , hasDeprecatedVersion , err := toKubernetesPayload (model , availableKubernetesVersions , currentKubernetesVersion )
824+ kubernetes , hasDeprecatedVersion , err := toKubernetesPayload (model , availableKubernetesVersions , currentKubernetesVersion , diags )
797825 if err != nil {
798826 core .LogAndAddError (ctx , diags , "Error creating/updating cluster" , fmt .Sprintf ("Creating cluster config API payload: %v" , err ))
799827 return
@@ -1813,7 +1841,7 @@ func mapExtensions(ctx context.Context, cl *ske.Cluster, m *Model) error {
18131841 return nil
18141842}
18151843
1816- func toKubernetesPayload (m * Model , availableVersions []ske.KubernetesVersion , currentKubernetesVersion * string ) (kubernetesPayload * ske.Kubernetes , hasDeprecatedVersion bool , err error ) {
1844+ func toKubernetesPayload (m * Model , availableVersions []ske.KubernetesVersion , currentKubernetesVersion * string , diags * diag. Diagnostics ) (kubernetesPayload * ske.Kubernetes , hasDeprecatedVersion bool , err error ) {
18171845 providedVersionMin := m .KubernetesVersionMin .ValueStringPointer ()
18181846
18191847 if ! m .KubernetesVersion .IsNull () {
@@ -1823,7 +1851,7 @@ func toKubernetesPayload(m *Model, availableVersions []ske.KubernetesVersion, cu
18231851 providedVersionMin = conversion .StringValueToPointer (m .KubernetesVersion )
18241852 }
18251853
1826- versionUsed , hasDeprecatedVersion , err := latestMatchingKubernetesVersion (availableVersions , providedVersionMin , currentKubernetesVersion )
1854+ versionUsed , hasDeprecatedVersion , err := latestMatchingKubernetesVersion (availableVersions , providedVersionMin , currentKubernetesVersion , diags )
18271855 if err != nil {
18281856 return nil , false , fmt .Errorf ("getting latest matching kubernetes version: %w" , err )
18291857 }
@@ -1835,9 +1863,7 @@ func toKubernetesPayload(m *Model, availableVersions []ske.KubernetesVersion, cu
18351863 return k , hasDeprecatedVersion , nil
18361864}
18371865
1838- func latestMatchingKubernetesVersion (availableVersions []ske.KubernetesVersion , kubernetesVersionMin , currentKubernetesVersion * string ) (version * string , deprecated bool , err error ) {
1839- deprecated = false
1840-
1866+ func latestMatchingKubernetesVersion (availableVersions []ske.KubernetesVersion , kubernetesVersionMin , currentKubernetesVersion * string , diags * diag.Diagnostics ) (version * string , deprecated bool , err error ) {
18411867 if availableVersions == nil {
18421868 return nil , false , fmt .Errorf ("nil available kubernetes versions" )
18431869 }
@@ -1865,58 +1891,113 @@ func latestMatchingKubernetesVersion(availableVersions []ske.KubernetesVersion,
18651891 }
18661892 }
18671893
1868- var fullVersion bool
1869- versionExp := validate .FullVersionRegex
1870- versionRegex := regexp .MustCompile (versionExp )
1871- if versionRegex .MatchString (* kubernetesVersionMin ) {
1872- fullVersion = true
1873- }
1894+ versionRegex := regexp .MustCompile (validate .FullVersionRegex )
1895+ fullVersion := versionRegex .MatchString (* kubernetesVersionMin )
18741896
18751897 providedVersionPrefixed := "v" + * kubernetesVersionMin
1876-
18771898 if ! semver .IsValid (providedVersionPrefixed ) {
18781899 return nil , false , fmt .Errorf ("provided version is invalid" )
18791900 }
18801901
1881- var versionUsed * string
1882- var state * string
1883- var availableVersionsArray []string
1884- // Get the higher available version that matches the major, minor and patch version provided by the user
1885- for _ , v := range availableVersions {
1886- if v .State == nil || v .Version == nil {
1902+ var (
1903+ selectedVersion * ske.KubernetesVersion
1904+ availableVersionsArray []string
1905+ )
1906+ if fullVersion {
1907+ availableVersionsArray , selectedVersion = selectFullVersion (availableVersions , providedVersionPrefixed )
1908+ } else {
1909+ availableVersionsArray , selectedVersion = selectMatchingVersion (availableVersions , providedVersionPrefixed )
1910+ }
1911+
1912+ deprecated = isDeprecated (selectedVersion )
1913+
1914+ if isPreview (selectedVersion ) {
1915+ diags .AddWarning ("preview version selected" , fmt .Sprintf ("only the preview version %q matched the selection criteria" , * selectedVersion .Version ))
1916+ }
1917+
1918+ // Throwing error if we could not match the version with the available versions
1919+ if selectedVersion == nil {
1920+ return nil , false , fmt .Errorf ("provided version is not one of the available kubernetes versions, available versions are: %s" , strings .Join (availableVersionsArray , "," ))
1921+ }
1922+
1923+ return selectedVersion .Version , deprecated , nil
1924+ }
1925+
1926+ func selectFullVersion (availableVersions []ske.KubernetesVersion , kubernetesVersionMin string ) (availableVersionsArray []string , selectedVersion * ske.KubernetesVersion ) {
1927+ for _ , versionCandidate := range availableVersions {
1928+ if versionCandidate .State == nil || versionCandidate .Version == nil {
18871929 continue
18881930 }
1889- availableVersionsArray = append (availableVersionsArray , * v .Version )
1890- vPreffixed := "v" + * v .Version
1931+ availableVersionsArray = append (availableVersionsArray , * versionCandidate .Version )
1932+ vPrefixed := "v" + * versionCandidate .Version
18911933
1892- if fullVersion {
1893- // [MAJOR].[MINOR].[PATCH] version provided, match available version
1894- if semver .Compare (vPreffixed , providedVersionPrefixed ) == 0 {
1895- versionUsed = v .Version
1896- state = v .State
1897- break
1898- }
1899- } else {
1900- // [MAJOR].[MINOR] version provided, get the latest non-preview patch version
1901- if semver .MajorMinor (vPreffixed ) == semver .MajorMinor (providedVersionPrefixed ) &&
1902- (semver .Compare (vPreffixed , providedVersionPrefixed ) == 1 || semver .Compare (vPreffixed , providedVersionPrefixed ) == 0 ) &&
1903- (v .State != nil && * v .State != VersionStatePreview ) {
1904- versionUsed = v .Version
1905- state = v .State
1934+ // [MAJOR].[MINOR].[PATCH] version provided, match available version
1935+ if semver .Compare (vPrefixed , kubernetesVersionMin ) == 0 {
1936+ selectedVersion = & versionCandidate
1937+ break
1938+ }
1939+ }
1940+ return availableVersionsArray , selectedVersion
1941+ }
1942+
1943+ func selectMatchingVersion (availableVersions []ske.KubernetesVersion , kubernetesVersionMin string ) (availableVersionsArray []string , selectedVersion * ske.KubernetesVersion ) {
1944+ sortK8sVersions (availableVersions )
1945+ for _ , candidateVersion := range availableVersions {
1946+ if candidateVersion .State == nil || candidateVersion .Version == nil {
1947+ continue
1948+ }
1949+ availableVersionsArray = append (availableVersionsArray , * candidateVersion .Version )
1950+ vPreffixed := "v" + * candidateVersion .Version
1951+
1952+ // [MAJOR].[MINOR] version provided, get the latest non-preview patch version
1953+ if semver .MajorMinor (vPreffixed ) == semver .MajorMinor (kubernetesVersionMin ) &&
1954+ (semver .Compare (vPreffixed , kubernetesVersionMin ) >= 0 ) &&
1955+ (candidateVersion .State != nil ) {
1956+ // take the current version as a candidate, if we have no other version inspected before
1957+ // OR the previously found version was a preview version
1958+ if selectedVersion == nil || (isSupported (& candidateVersion ) && isPreview (selectedVersion )) {
1959+ selectedVersion = & candidateVersion
19061960 }
1961+ // all other cases are ignored
19071962 }
19081963 }
1964+ return availableVersionsArray , selectedVersion
1965+ }
19091966
1910- if versionUsed != nil {
1911- deprecated = strings .EqualFold (* state , VersionStateDeprecated )
1967+ func isDeprecated (v * ske.KubernetesVersion ) bool {
1968+ if v == nil {
1969+ return false
19121970 }
19131971
1914- // Throwing error if we could not match the version with the available versions
1915- if versionUsed == nil {
1916- return nil , false , fmt .Errorf ("provided version is not one of the available kubernetes versions, available versions are: %s" , strings .Join (availableVersionsArray , "," ))
1972+ if v .State == nil {
1973+ return false
19171974 }
19181975
1919- return versionUsed , deprecated , nil
1976+ return * v .State == VersionStateDeprecated
1977+ }
1978+
1979+ func isPreview (v * ske.KubernetesVersion ) bool {
1980+ if v == nil {
1981+ return false
1982+ }
1983+
1984+ if v .State == nil {
1985+ return false
1986+ }
1987+
1988+ return * v .State == VersionStatePreview
1989+ }
1990+
1991+ func isSupported (v * ske.KubernetesVersion ) bool {
1992+ if v == nil {
1993+ return false
1994+ }
1995+
1996+ if v .State == nil {
1997+ return false
1998+ }
1999+
2000+ return * v .State == VersionStateSupported
19202001}
19212002
19222003func getLatestSupportedKubernetesVersion (versions []ske.KubernetesVersion ) (* string , error ) {
0 commit comments