Skip to content

Commit

Permalink
Add support for compatibility attributes
Browse files Browse the repository at this point in the history
* Cluster/Service/Provider Templates now have
  support for compatibility attributes
* Managed/Management Clusters now have
  support to report the compatibility
  attributes
* amends to the templates controller
  in regards of the API changes
* amends to other parts of the code

Closes Mirantis#354
  • Loading branch information
zerospiel committed Sep 27, 2024
1 parent 040227a commit b18664f
Show file tree
Hide file tree
Showing 29 changed files with 865 additions and 359 deletions.
101 changes: 97 additions & 4 deletions api/v1alpha1/clustertemplate_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,117 @@
package v1alpha1

import (
"errors"
"fmt"
"strings"

"github.com/Masterminds/semver/v3"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// ClusterTemplateSpec defines the desired state of ClusterTemplate
type ClusterTemplateSpec struct {
TemplateSpecCommon `json:",inline"`
Helm HelmSpec `json:"helm"`
// Compatible K8S version of the cluster set in the SemVer format.
KubertenesVersion string `json:"k8sVersion,omitempty"`
// Providers represent required/exposed CAPI providers depending on the template type.
// Should be set if not present in the Helm chart metadata.
Providers ProvidersConstrainted `json:"providers,omitempty"`
}

// ClusterTemplateStatus defines the observed state of ClusterTemplate
type ClusterTemplateStatus struct {
// Providers represent required/exposed CAPI providers depending on the template type.
Providers ProvidersConstrainted `json:"providers,omitempty"`

TemplateStatusCommon `json:",inline"`
}

func (t *ClusterTemplate) GetSpec() *TemplateSpecCommon {
return &t.Spec.TemplateSpecCommon
// 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 { //nolint:dupl // no sane approach to fully avoid duplication in terms of types
parseProviders := func(typ providersType) ([]NameConstraint, error) {
var (
pspec, pstatus []NameConstraint
anno string
)
switch typ {
case bootstrapProvidersType:
pspec, pstatus = t.Spec.Providers.BootstrapProviders, t.Status.Providers.BootstrapProviders
anno = ChartAnnotationBootstrapProviders
case controlPlaneProvidersType:
pspec, pstatus = t.Spec.Providers.ControlPlaneProviders, t.Status.Providers.ControlPlaneProviders
anno = ChartAnnotationControlPlaneProviders
case infrastructureProvidersType:
pspec, pstatus = t.Spec.Providers.InfrastructureProviders, t.Status.Providers.InfrastructureProviders
anno = ChartAnnotationInfraProviders
}

if len(pspec) > 0 {
return pstatus, nil
}

providers := annotations[anno]
if len(providers) == 0 {
return []NameConstraint{}, nil
}

var (
constrainted = strings.Split(providers, ",")
merr error
)

pstatus = make([]NameConstraint, 0, len(constrainted))

for _, v := range constrainted {
nc := strings.SplitN(v, " ", 1)
if len(nc) == 0 { // BCE (bound check elimination)
continue
}

n := NameConstraint{Name: nc[0]}
if len(nc) < 2 {
pstatus = append(pstatus, n)
continue
}

c := strings.TrimSpace(nc[1])
if _, err := semver.NewConstraint(c); err != nil { // validation
merr = errors.Join(merr, fmt.Errorf("failed to parse constraint %s in the %s: %v", c, v, err))
continue
}

n.Constraint = c
pstatus = append(pstatus, n)
}

return pstatus, merr
}

var err error
t.Status.Providers.BootstrapProviders, err = parseProviders(bootstrapProvidersType)
if err != nil {
return fmt.Errorf("failed to parse bootstrap providers: %v", err)
}

t.Status.Providers.ControlPlaneProviders, err = parseProviders(controlPlaneProvidersType)
if err != nil {
return fmt.Errorf("failed to parse controlPlane providers: %v", err)
}

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

return nil
}

func (t *ClusterTemplate) GetHelmSpec() *HelmSpec {
return &t.Spec.Helm
}

func (t *ClusterTemplate) GetStatus() *TemplateStatusCommon {
func (t *ClusterTemplate) GetCommonStatus() *TemplateStatusCommon {
return &t.Status.TemplateStatusCommon
}

Expand Down
89 changes: 80 additions & 9 deletions api/v1alpha1/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,53 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
)

// Providers is a structure holding different types of CAPI providers
type 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"`
}
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"`
}

// ProvidersVersioned hold different types of CAPI providers with supported versions.
ProvidersVersioned struct {
// List of CAPI infrastructure providers with the supported version.
InfrastructureProviders []NameVersion `json:"infrastructure,omitempty"`
// List of CAPI bootstrap providers with the supported version.
BootstrapProviders []NameVersion `json:"bootstrap,omitempty"`
// List of CAPI control plane providers with the supported version.
ControlPlaneProviders []NameVersion `json:"controlPlane,omitempty"`
}

// ProvidersConstrainted hold different types of CAPI providers with constrainted versions.
ProvidersConstrainted struct {
// List of CAPI infrastructure providers with the SemVer constraint.
InfrastructureProviders []NameConstraint `json:"infrastructure,omitempty"`
// List of CAPI bootstrap providers with the SemVer constraint.
BootstrapProviders []NameConstraint `json:"bootstrap,omitempty"`
// List of CAPI control plane providers with the SemVer constraint.
ControlPlaneProviders []NameConstraint `json:"controlPlane,omitempty"`
}

// NameVersion represents name of the provider with its supported version.
NameVersion struct {
// Name of the provider.
Name string `json:"name,omitempty"`
// Supported exact version in the SemVer format.
Version string `json:"version,omitempty"`
}

// NameConstraint represents name of the provider with its constrainted version.
NameConstraint struct {
// Name of the provider.
Name string `json:"name,omitempty"`
// Supported versions set via the SemVer constraint.
Constraint string `json:"constraint,omitempty"`
}
)

const (
// Provider CAPA
Expand Down Expand Up @@ -80,3 +118,36 @@ func ExtractReleaseVersion(rawObj client.Object) []string {
}
return []string{release.Spec.Version}
}

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

func (c ProvidersConstrainted) ControlPlaneProvidersNames() []string {
return c.names(bootstrapProvidersType)
}

func (c ProvidersConstrainted) InfrastructureProvidersNames() []string {
return c.names(bootstrapProvidersType)
}

func (c ProvidersConstrainted) names(typ providersType) []string {
f := func(nn []NameConstraint) []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{}
}
}
5 changes: 4 additions & 1 deletion api/v1alpha1/managedcluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ type ManagedClusterSpec struct {

// ManagedClusterStatus defines the observed state of ManagedCluster
type ManagedClusterStatus struct {
// Currently compatible K8S version of the cluster. Being set only if
// the corresponding ClusterTemplate provided it in the spec.
KubertenesVersion string `json:"k8sVersion,omitempty"`
// Conditions contains details for the current state of the ManagedCluster
Conditions []metav1.Condition `json:"conditions,omitempty"`
// ObservedGeneration is the last observed generation.
Expand All @@ -88,7 +91,7 @@ type ManagedClusterStatus struct {

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:shortName=hmc-deploy;deploy
// +kubebuilder:resource:shortName=hmcmc;mc
// +kubebuilder:printcolumn:name="ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="Ready",priority=0
// +kubebuilder:printcolumn:name="status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description="Status",priority=0
// +kubebuilder:printcolumn:name="dryRun",type="string",JSONPath=".spec.dryRun",description="Dry Run",priority=1
Expand Down
18 changes: 9 additions & 9 deletions api/v1alpha1/management_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,35 +75,35 @@ func (in *Component) HelmValues() (values map[string]any, err error) {
return values, err
}

func (m *ManagementSpec) SetProvidersDefaults() error {
m.Providers = []Provider{
func GetDefaultProviders() []Provider {
return []Provider{
{Name: ProviderK0smotronName},
{Name: ProviderCAPAName},
{Name: ProviderAzureName},
{Name: ProviderVSphereName},
{Name: ProviderSveltosName},
}
return nil
}

// ManagementStatus defines the observed state of Management
type ManagementStatus struct {
// ObservedGeneration is the last observed generation.
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// AvailableProviders holds all CAPI providers available on the Management cluster.
AvailableProviders Providers `json:"availableProviders,omitempty"`
// Components indicates the status of installed HMC components and CAPI providers.
Components map[string]ComponentStatus `json:"components,omitempty"`
// AvailableProviders holds all CAPI providers available along with
// their versions if specified in ProviderTemplates on the Management cluster.
AvailableProviders ProvidersVersioned `json:"availableProviders,omitempty"`
// ObservedGeneration is the last observed generation.
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
}

// ComponentStatus is the status of Management component installation
type ComponentStatus struct {
// Template is the name of the Template associated with this component.
Template string `json:"template,omitempty"`
// Success represents if a component installation was successful
Success bool `json:"success,omitempty"`
// Error stores as error message in case of failed installation
Error string `json:"error,omitempty"`
// Success represents if a component installation was successful
Success bool `json:"success,omitempty"`
}

// +kubebuilder:object:root=true
Expand Down
Loading

0 comments on commit b18664f

Please sign in to comment.