Skip to content

Commit

Permalink
Merge branch 'Mirantis:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
briantd authored Oct 24, 2024
2 parents 0491a75 + 59a0e13 commit fb175a9
Show file tree
Hide file tree
Showing 37 changed files with 760 additions and 950 deletions.
36 changes: 18 additions & 18 deletions api/v1alpha1/clustertemplate_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,44 +30,39 @@ const (

// ClusterTemplateSpec defines the desired state of ClusterTemplate
type ClusterTemplateSpec struct {
Helm HelmSpec `json:"helm"`
Helm HelmSpec `json:"helm"`
CAPIContracts CompatibilityContracts `json:"capiContracts,omitempty"`
// Kubernetes exact version in the SemVer format provided by this ClusterTemplate.
KubernetesVersion string `json:"k8sVersion,omitempty"`
// Providers represent required CAPI providers with constrained compatibility versions set.
// Providers represent required CAPI providers with supported contract versions.
// Should be set if not present in the Helm chart metadata.
// Compatibility attributes are optional to be defined.
Providers ProvidersTupled `json:"providers,omitempty"`
Providers Providers `json:"providers,omitempty"`
}

// ClusterTemplateStatus defines the observed state of ClusterTemplate
type ClusterTemplateStatus struct {
CAPIContracts CompatibilityContracts `json:"capiContracts,omitempty"`
// Kubernetes exact version in the SemVer format provided by this ClusterTemplate.
KubernetesVersion string `json:"k8sVersion,omitempty"`
// Providers represent required CAPI providers with constrained compatibility versions set
// Providers represent required CAPI providers with supported contract versions
// if the latter has been given.
Providers ProvidersTupled `json:"providers,omitempty"`
Providers Providers `json:"providers,omitempty"`

TemplateStatusCommon `json:",inline"`
}

// FillStatusWithProviders sets the status of the template with providers
// either from the spec or from the given annotations.
func (t *ClusterTemplate) FillStatusWithProviders(annotations map[string]string) error {
var err error
t.Status.Providers.BootstrapProviders, err = parseProviders(t, bootstrapProvidersType, annotations, semver.NewConstraint)
if err != nil {
return fmt.Errorf("failed to parse ClusterTemplate bootstrap providers: %v", err)
}
t.Status.Providers = getProvidersList(t, annotations)

t.Status.Providers.ControlPlaneProviders, err = parseProviders(t, controlPlaneProvidersType, annotations, semver.NewConstraint)
contractsStatus, err := getCAPIContracts(t, annotations)
if err != nil {
return fmt.Errorf("failed to parse ClusterTemplate controlPlane providers: %v", err)
return fmt.Errorf("failed to get CAPI contract versions for ClusterTemplate %s/%s: %v", t.GetNamespace(), t.GetName(), err)
}

t.Status.Providers.InfrastructureProviders, err = parseProviders(t, infrastructureProvidersType, annotations, semver.NewConstraint)
if err != nil {
return fmt.Errorf("failed to parse ClusterTemplate infrastructure providers: %v", err)
}
t.Status.CAPIContracts = contractsStatus

kversion := annotations[ChartAnnotationKubernetesVersion]
if t.Spec.KubernetesVersion != "" {
Expand All @@ -78,16 +73,21 @@ func (t *ClusterTemplate) FillStatusWithProviders(annotations map[string]string)
}

if _, err := semver.NewVersion(kversion); err != nil {
return fmt.Errorf("failed to parse kubernetes version %s: %w", kversion, err)
return fmt.Errorf("failed to parse kubernetes version %s for ClusterTemplate %s/%s: %w", kversion, t.GetNamespace(), t.GetName(), err)
}

t.Status.KubernetesVersion = kversion

return nil
}

// GetContracts returns .spec.capiContracts of the Template.
func (t *ClusterTemplate) GetContracts() CompatibilityContracts {
return t.Spec.CAPIContracts
}

// GetSpecProviders returns .spec.providers of the Template.
func (t *ClusterTemplate) GetSpecProviders() ProvidersTupled {
func (t *ClusterTemplate) GetSpecProviders() Providers {
return t.Spec.Providers
}

Expand Down
76 changes: 10 additions & 66 deletions api/v1alpha1/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,39 +36,16 @@ const (
)

type (
// Providers hold different types of CAPI providers.
Providers struct {
// InfrastructureProviders is the list of CAPI infrastructure providers
InfrastructureProviders []string `json:"infrastructure,omitempty"`
// BootstrapProviders is the list of CAPI bootstrap providers
BootstrapProviders []string `json:"bootstrap,omitempty"`
// ControlPlaneProviders is the list of CAPI control plane providers
ControlPlaneProviders []string `json:"controlPlane,omitempty"`
}

// Holds different types of CAPI providers with either
// an exact or constrained version in the SemVer format. The requirement
// is determined by a consumer of this type.
ProvidersTupled struct {
// List of CAPI infrastructure providers with either an exact or constrained version in the SemVer format.
// Compatibility attributes are optional to be defined.
InfrastructureProviders []ProviderTuple `json:"infrastructure,omitempty"`
// List of CAPI bootstrap providers with either an exact or constrained version in the SemVer format.
// Compatibility attributes are optional to be defined.
BootstrapProviders []ProviderTuple `json:"bootstrap,omitempty"`
// List of CAPI control plane providers with either an exact or constrained version in the SemVer format.
// Compatibility attributes are optional to be defined.
ControlPlaneProviders []ProviderTuple `json:"controlPlane,omitempty"`
}

// Represents name of the provider with either an exact or constrained version in the SemVer format.
ProviderTuple struct {
// Name of the provider.
Name string `json:"name,omitempty"`
// Compatibility restriction in the SemVer format (exact or constrained version).
// Optional to be defined.
VersionOrConstraint string `json:"versionOrConstraint,omitempty"`
}
// Holds different types of CAPI providers.
Providers []string

// Holds key-value pairs with compatibility [contract versions],
// where the key is the core CAPI contract version,
// and the value is an underscore-delimited (_) list of provider contract versions
// supported by the core CAPI.
//
// [contract versions]: https://cluster-api.sigs.k8s.io/developer/providers/contracts
CompatibilityContracts map[string]string
)

const (
Expand Down Expand Up @@ -144,36 +121,3 @@ func ExtractServiceTemplateName(rawObj client.Object) []string {

return templates
}

func (c ProvidersTupled) BootstrapProvidersNames() []string {
return c.names(bootstrapProvidersType)
}

func (c ProvidersTupled) ControlPlaneProvidersNames() []string {
return c.names(controlPlaneProvidersType)
}

func (c ProvidersTupled) InfrastructureProvidersNames() []string {
return c.names(infrastructureProvidersType)
}

func (c ProvidersTupled) names(typ providersType) []string {
f := func(nn []ProviderTuple) []string {
res := make([]string, len(nn))
for i, v := range nn {
res[i] = v.Name
}
return res
}

switch typ {
case bootstrapProvidersType:
return f(c.BootstrapProviders)
case controlPlaneProvidersType:
return f(c.ControlPlaneProviders)
case infrastructureProvidersType:
return f(c.InfrastructureProviders)
default:
return []string{}
}
}
74 changes: 74 additions & 0 deletions api/v1alpha1/compatibility_contract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright 2024
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package v1alpha1

import (
"strconv"
"strings"
)

// isCAPIContractVersion determines whether a given string
// represents a version in the CAPI contract version format (e.g. v1_v1beta1_v1alpha1, etc.).
func isCAPIContractVersion(version string) bool {
for _, v := range strings.Split(version, "_") {
if !isCAPIContractSingleVersion(v) {
return false
}
}

return true
}

// isCAPIContractSingleVersion determines whether a given string
// represents a single version in the CAPI contract version format (e.g. v1, v1beta1, v1alpha1, etc.).
func isCAPIContractSingleVersion(version string) bool {
if !strings.HasPrefix(version, "v") {
return false
}

parts := strings.Split(version, "v")
if len(parts) != 2 || parts[0] != "" || strings.IndexByte(version, '_') != -1 { // skip v1_v1beta1 list of versions
return false
}

const (
alphaPrefix, betaPrefix = "alpha", "beta"
)

versionNumber := parts[1]
alphaIndex := strings.Index(versionNumber, alphaPrefix)
betaIndex := strings.Index(versionNumber, betaPrefix)

if alphaIndex != -1 {
return isNonMajor(versionNumber, alphaPrefix, alphaIndex)
} else if betaIndex != -1 {
return isNonMajor(versionNumber, betaPrefix, betaIndex)
}

_, err := strconv.Atoi(strings.TrimSpace(versionNumber))
return err == nil
}

func isNonMajor(version, prefix string, prefixIdx int) bool {
majorVer := version[:prefixIdx]
prefixedVer := version[prefixIdx+len(prefix):]

if _, err := strconv.Atoi(majorVer); err != nil {
return false
}

_, err := strconv.Atoi(prefixedVer)
return err == nil
}
72 changes: 72 additions & 0 deletions api/v1alpha1/compatibility_contract_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2024
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package v1alpha1

import "testing"

func Test_isCAPIContractVersion(t *testing.T) {
tests := []struct {
version string
isValid bool
}{
{"v1", true},
{"v1alpha1", true},
{"v1beta1", true},
{"v2", true},
{"v3alpha2", true},
{"v33beta22", true},
{"v1alpha1_v1beta1", true},
{"v1alpha1v1alha2_v1beta1", false},
{"v4beta1", true},
{"invalid", false},
{"v1alpha", false},
{"v1beta", false},
{"v1alpha1beta1", false},
}

for _, test := range tests {
result := isCAPIContractVersion(test.version)
if result != test.isValid {
t.Errorf("isValidVersion(%q) = %v, want %v", test.version, result, test.isValid)
}
}
}

func Test_isCAPIContractSingleVersion(t *testing.T) {
tests := []struct {
version string
isValid bool
}{
{"v1", true},
{"v1alpha1", true},
{"v1beta1", true},
{"v2", true},
{"v3alpha2", true},
{"v33beta22", true},
{"v4beta1", true},
{"invalid", false},
{"v1alpha", false},
{"v1beta", false},
{"v1alpha1beta1", false},
{"v1alpha1_v1beta1", false},
}

for _, test := range tests {
result := isCAPIContractSingleVersion(test.version)
if result != test.isValid {
t.Errorf("isValidVersion(%q) = %v, want %v", test.version, result, test.isValid)
}
}
}
11 changes: 9 additions & 2 deletions api/v1alpha1/management_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,20 @@ func GetDefaultProviders() []Provider {

// ManagementStatus defines the observed state of Management
type ManagementStatus struct {
// For each CAPI provider name holds its compatibility [contract versions]
// in a key-value pairs, where the key is the core CAPI contract version,
// and the value is an underscore-delimited (_) list of provider contract versions
// supported by the core CAPI.
//
// [contract versions]: https://cluster-api.sigs.k8s.io/developer/providers/contracts
CAPIContracts map[string]CompatibilityContracts `json:"capiContracts,omitempty"`
// Components indicates the status of installed HMC components and CAPI providers.
Components map[string]ComponentStatus `json:"components,omitempty"`
// Release indicates the current Release object.
Release string `json:"release,omitempty"`
// AvailableProviders holds all CAPI providers available along with
// their exact compatibility versions if specified in ProviderTemplates on the Management cluster.
AvailableProviders ProvidersTupled `json:"availableProviders,omitempty"`
// their supported contract versions, if specified in ProviderTemplates, on the Management cluster.
AvailableProviders Providers `json:"availableProviders,omitempty"`
// ObservedGeneration is the last observed generation.
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
}
Expand Down
Loading

0 comments on commit fb175a9

Please sign in to comment.