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 Oct 8, 2024
1 parent 7927889 commit 6ac79c8
Show file tree
Hide file tree
Showing 31 changed files with 879 additions and 417 deletions.
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ linters-settings:
- pattern: "interface{}"
replacement: "any"
stylecheck:
checks: ["all", "-ST1000", "-ST1001"]
checks: ["all", "-ST1000", "-ST1001", "-ST1021"]
revive:
enable-all-rules: true
rules:
Expand Down
54 changes: 50 additions & 4 deletions api/v1alpha1/clustertemplate_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,72 @@
package v1alpha1

import (
"fmt"

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

const ClusterTemplateKind = "ClusterTemplate"

// 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 CAPI providers with constrainted compatibility versions set. Should be set if not present in the Helm chart metadata.
Providers ProvidersTupled `json:"providers,omitempty"`
}

// ClusterTemplateStatus defines the observed state of ClusterTemplate
type ClusterTemplateStatus struct {
// Compatible K8S version of the cluster set in the SemVer format.
KubertenesVersion string `json:"k8sVersion,omitempty"`
// Providers represent exposed CAPI providers with constrainted compatibility versions set.
Providers ProvidersTupled `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 {
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.ControlPlaneProviders, err = parseProviders(t, controlPlaneProvidersType, annotations, semver.NewConstraint)
if err != nil {
return fmt.Errorf("failed to parse ClusterTemplate controlPlane providers: %v", 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)
}

return nil
}

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

// GetStatusProviders returns .status.providers of the Template.
func (t *ClusterTemplate) GetStatusProviders() ProvidersTupled {
return t.Status.Providers
}

// GetHelmSpec returns .spec.helm of the Template.
func (t *ClusterTemplate) GetHelmSpec() *HelmSpec {
return &t.Spec.Helm
}

func (t *ClusterTemplate) GetStatus() *TemplateStatusCommon {
// GetCommonStatus returns common status of the Template.
func (t *ClusterTemplate) GetCommonStatus() *TemplateStatusCommon {
return &t.Status.TemplateStatusCommon
}

Expand Down
73 changes: 64 additions & 9 deletions api/v1alpha1/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,37 @@ 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"`
}

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

// Represents name of the provider with either an exact or constrainted version in the SemVer format.
ProviderTuple struct {
// Name of the provider.
Name string `json:"name,omitempty"`
// Compatibility restriction in the SemVer format (exact or constrainted version)
VersionOrContraint string `json:"versionOrContraint,omitempty"`
}
)

const (
// Provider CAPA
Expand Down Expand Up @@ -104,3 +126,36 @@ 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(bootstrapProvidersType)
}

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

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{}
}
}
3 changes: 3 additions & 0 deletions api/v1alpha1/managedcluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,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 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,37 +75,37 @@ 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"`
// 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"`
// 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
54 changes: 50 additions & 4 deletions api/v1alpha1/providertemplate_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,70 @@
package v1alpha1

import (
"fmt"

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

// ProviderTemplateSpec defines the desired state of ProviderTemplate
type ProviderTemplateSpec struct {
TemplateSpecCommon `json:",inline"`
Helm HelmSpec `json:"helm"`
// Compatible CAPI provider version set in the SemVer format.
CAPIVersion string `json:"capiVersion,omitempty"`
// Represents required CAPI providers with exact compatibility versions set. Should be set if not present in the Helm chart metadata.
Providers ProvidersTupled `json:"providers,omitempty"`
}

// ProviderTemplateStatus defines the observed state of ProviderTemplate
type ProviderTemplateStatus struct {
// Compatible CAPI provider version in the SemVer format.
CAPIVersion string `json:"capiVersion,omitempty"`
// Providers represent exposed CAPI providers with exact compatibility versions set.
Providers ProvidersTupled `json:"providers,omitempty"`

TemplateStatusCommon `json:",inline"`
}

func (t *ProviderTemplate) 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 *ProviderTemplate) FillStatusWithProviders(annotations map[string]string) error {
var err error
t.Status.Providers.BootstrapProviders, err = parseProviders(t, bootstrapProvidersType, annotations, semver.NewVersion)
if err != nil {
return fmt.Errorf("failed to parse ProviderTemplate bootstrap providers: %v", err)
}

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

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

return nil
}

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

// GetStatusProviders returns .status.providers of the Template.
func (t *ProviderTemplate) GetStatusProviders() ProvidersTupled {
return t.Status.Providers
}

// GetHelmSpec returns .spec.helm of the Template.
func (t *ProviderTemplate) GetHelmSpec() *HelmSpec {
return &t.Spec.Helm
}

func (t *ProviderTemplate) GetStatus() *TemplateStatusCommon {
// GetCommonStatus returns common status of the Template.
func (t *ProviderTemplate) GetCommonStatus() *TemplateStatusCommon {
return &t.Status.TemplateStatusCommon
}

Expand Down
62 changes: 58 additions & 4 deletions api/v1alpha1/servicetemplate_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,80 @@
package v1alpha1

import (
"strings"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const ServiceTemplateKind = "ServiceTemplate"

// ServiceTemplateSpec defines the desired state of ServiceTemplate
type ServiceTemplateSpec struct {
TemplateSpecCommon `json:",inline"`
Helm HelmSpec `json:"helm"`
// Constraint describing compatible K8S versions of the cluster set in the SemVer format.
KubertenesConstraint string `json:"k8sConstraint,omitempty"`
// Represents required CAPI providers. Should be set if not present in the Helm chart metadata.
Providers Providers `json:"providers,omitempty"`
}

// ServiceTemplateStatus defines the observed state of ServiceTemplate
type ServiceTemplateStatus struct {
// Constraint describing compatible K8S versions of the cluster set in the SemVer format.
KubertenesConstraint string `json:"k8sConstraint,omitempty"`
// Represents exposed CAPI providers.
Providers Providers `json:"providers,omitempty"`

TemplateStatusCommon `json:",inline"`
}

func (t *ServiceTemplate) GetSpec() *TemplateSpecCommon {
return &t.Spec.TemplateSpecCommon
// FillStatusWithProviders sets the status of the template with providers
// either from the spec or from the given annotations.
//
// The return parameter is noop and is always nil.
func (t *ServiceTemplate) FillStatusWithProviders(annotations map[string]string) error {
parseProviders := func(typ providersType) []string {
var (
pspec, pstatus []string
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
}

providers := annotations[anno]
if len(providers) == 0 {
return []string{}
}

return strings.Split(providers, ",")
}

t.Status.Providers.BootstrapProviders = parseProviders(bootstrapProvidersType)
t.Status.Providers.ControlPlaneProviders = parseProviders(controlPlaneProvidersType)
t.Status.Providers.InfrastructureProviders = parseProviders(infrastructureProvidersType)

return nil
}

// GetHelmSpec returns .spec.helm of the Template.
func (t *ServiceTemplate) GetHelmSpec() *HelmSpec {
return &t.Spec.Helm
}

func (t *ServiceTemplate) GetStatus() *TemplateStatusCommon {
// GetCommonStatus returns common status of the Template.
func (t *ServiceTemplate) GetCommonStatus() *TemplateStatusCommon {
return &t.Status.TemplateStatusCommon
}

Expand Down
Loading

0 comments on commit 6ac79c8

Please sign in to comment.