Skip to content

Commit

Permalink
Validate mgmt providers removal
Browse files Browse the repository at this point in the history
* forbid Management .spec.providers items
  removal if a ManagedCluster utilizes one
  of the removed providers
* amends to CRDs descriptions

Closes #554
  • Loading branch information
zerospiel authored and Kshatrix committed Nov 8, 2024
1 parent 025f1c3 commit 23ace50
Show file tree
Hide file tree
Showing 22 changed files with 529 additions and 300 deletions.
6 changes: 2 additions & 4 deletions api/v1alpha1/clustertemplate_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,8 @@ type ClusterTemplateSpec struct {
ProviderContracts CompatibilityContracts `json:"providerContracts,omitempty"`
// Kubernetes exact version in the SemVer format provided by this ClusterTemplate.
KubernetesVersion string `json:"k8sVersion,omitempty"`
// Providers represent required CAPI providers with supported contract versions.
// Providers represent required CAPI providers.
// Should be set if not present in the Helm chart metadata.
// Compatibility attributes are optional to be defined.
Providers Providers `json:"providers,omitempty"`
}

Expand All @@ -57,8 +56,7 @@ type ClusterTemplateStatus struct {
ProviderContracts CompatibilityContracts `json:"providerContracts,omitempty"`
// Kubernetes exact version in the SemVer format provided by this ClusterTemplate.
KubernetesVersion string `json:"k8sVersion,omitempty"`
// Providers represent required CAPI providers with supported contract versions
// if the latter has been given.
// Providers represent required CAPI providers.
Providers Providers `json:"providers,omitempty"`

TemplateStatusCommon `json:",inline"`
Expand Down
148 changes: 1 addition & 147 deletions api/v1alpha1/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,6 @@

package v1alpha1

import (
"context"

ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
)

const (
// SucceededReason indicates a condition or event observed a success, for example when declared desired state
// matches actual state, or a performed action succeeded.
Expand Down Expand Up @@ -57,144 +50,5 @@ const (
// Provider K0smotron
ProviderK0smotronName = "k0smotron"
// Provider Sveltos
ProviderSveltosName = "projectsveltos"
ProviderSveltosTargetNamespace = "projectsveltos"
ProviderSveltosCreateNamespace = true
ProviderSveltosName = "projectsveltos"
)

func SetupIndexers(ctx context.Context, mgr ctrl.Manager) error {
if err := SetupManagedClusterIndexer(ctx, mgr); err != nil {
return err
}

if err := SetupReleaseVersionIndexer(ctx, mgr); err != nil {
return err
}

if err := SetupReleaseTemplatesIndexer(ctx, mgr); err != nil {
return err
}

if err := SetupManagedClusterServicesIndexer(ctx, mgr); err != nil {
return err
}

if err := SetupMultiClusterServiceServicesIndexer(ctx, mgr); err != nil {
return err
}

if err := SetupClusterTemplateChainIndexer(ctx, mgr); err != nil {
return err
}

return SetupServiceTemplateChainIndexer(ctx, mgr)
}

const TemplateKey = ".spec.template"

func SetupManagedClusterIndexer(ctx context.Context, mgr ctrl.Manager) error {
return mgr.GetFieldIndexer().IndexField(ctx, &ManagedCluster{}, TemplateKey, ExtractTemplateName)
}

func ExtractTemplateName(rawObj client.Object) []string {
cluster, ok := rawObj.(*ManagedCluster)
if !ok {
return nil
}
return []string{cluster.Spec.Template}
}

const ReleaseVersionKey = ".spec.version"

func SetupReleaseVersionIndexer(ctx context.Context, mgr ctrl.Manager) error {
return mgr.GetFieldIndexer().IndexField(ctx, &Release{}, ReleaseVersionKey, ExtractReleaseVersion)
}

func ExtractReleaseVersion(rawObj client.Object) []string {
release, ok := rawObj.(*Release)
if !ok {
return nil
}
return []string{release.Spec.Version}
}

const ReleaseTemplatesKey = "releaseTemplates"

func SetupReleaseTemplatesIndexer(ctx context.Context, mgr ctrl.Manager) error {
return mgr.GetFieldIndexer().IndexField(ctx, &Release{}, ReleaseTemplatesKey, ExtractReleaseTemplates)
}

func ExtractReleaseTemplates(rawObj client.Object) []string {
release, ok := rawObj.(*Release)
if !ok {
return nil
}
return release.Templates()
}

const ServicesTemplateKey = ".spec.services[].Template"

func SetupManagedClusterServicesIndexer(ctx context.Context, mgr ctrl.Manager) error {
return mgr.GetFieldIndexer().IndexField(ctx, &ManagedCluster{}, ServicesTemplateKey, ExtractServiceTemplateFromManagedCluster)
}

func ExtractServiceTemplateFromManagedCluster(rawObj client.Object) []string {
cluster, ok := rawObj.(*ManagedCluster)
if !ok {
return nil
}

templates := []string{}
for _, s := range cluster.Spec.Services {
templates = append(templates, s.Template)
}

return templates
}

func SetupMultiClusterServiceServicesIndexer(ctx context.Context, mgr ctrl.Manager) error {
return mgr.GetFieldIndexer().IndexField(ctx, &MultiClusterService{}, ServicesTemplateKey, ExtractServiceTemplateFromMultiClusterService)
}

func ExtractServiceTemplateFromMultiClusterService(rawObj client.Object) []string {
cluster, ok := rawObj.(*MultiClusterService)
if !ok {
return nil
}

templates := []string{}
for _, s := range cluster.Spec.Services {
templates = append(templates, s.Template)
}

return templates
}

const SupportedTemplateKey = ".spec.supportedTemplates[].Name"

func SetupClusterTemplateChainIndexer(ctx context.Context, mgr ctrl.Manager) error {
return mgr.GetFieldIndexer().IndexField(ctx, &ClusterTemplateChain{}, SupportedTemplateKey, ExtractSupportedTemplatesNames)
}

func SetupServiceTemplateChainIndexer(ctx context.Context, mgr ctrl.Manager) error {
return mgr.GetFieldIndexer().IndexField(ctx, &ServiceTemplateChain{}, SupportedTemplateKey, ExtractSupportedTemplatesNames)
}

func ExtractSupportedTemplatesNames(rawObj client.Object) []string {
chainSpec := TemplateChainSpec{}
switch chain := rawObj.(type) {
case *ClusterTemplateChain:
chainSpec = chain.Spec
case *ServiceTemplateChain:
chainSpec = chain.Spec
default:
return nil
}

supportedTemplates := make([]string, 0, len(chainSpec.SupportedTemplates))
for _, t := range chainSpec.SupportedTemplates {
supportedTemplates = append(supportedTemplates, t.Name)
}

return supportedTemplates
}
193 changes: 193 additions & 0 deletions api/v1alpha1/indexers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
// 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 (
"context"
"errors"

ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
)

func SetupIndexers(ctx context.Context, mgr ctrl.Manager) error {
var merr error
for _, f := range []func(context.Context, ctrl.Manager) error{
setupManagedClusterIndexer,
setupManagedClusterServicesIndexer,
setupReleaseVersionIndexer,
setupReleaseTemplatesIndexer,
setupClusterTemplateChainIndexer,
setupServiceTemplateChainIndexer,
setupClusterTemplateProvidersIndexer,
setupMultiClusterServiceServicesIndexer,
} {
merr = errors.Join(merr, f(ctx, mgr))
}

return merr
}

// managed cluster

// ManagedClusterTemplateIndexKey indexer field name to extract ClusterTemplate name reference from a ManagedCluster object.
const ManagedClusterTemplateIndexKey = ".spec.template"

func setupManagedClusterIndexer(ctx context.Context, mgr ctrl.Manager) error {
return mgr.GetFieldIndexer().IndexField(ctx, &ManagedCluster{}, ManagedClusterTemplateIndexKey, ExtractTemplateNameFromManagedCluster)
}

// ExtractTemplateNameFromManagedCluster returns referenced ClusterTemplate name
// declared in a ManagedCluster object.
func ExtractTemplateNameFromManagedCluster(rawObj client.Object) []string {
cluster, ok := rawObj.(*ManagedCluster)
if !ok {
return nil
}

return []string{cluster.Spec.Template}
}

// ManagedClusterServiceTemplatesIndexKey indexer field name to extract service templates names from a ManagedCluster object.
const ManagedClusterServiceTemplatesIndexKey = ".spec.services[].Template"

func setupManagedClusterServicesIndexer(ctx context.Context, mgr ctrl.Manager) error {
return mgr.GetFieldIndexer().IndexField(ctx, &ManagedCluster{}, ManagedClusterServiceTemplatesIndexKey, ExtractServiceTemplateNamesFromManagedCluster)
}

// ExtractServiceTemplateNamesFromManagedCluster returns a list of service templates names
// declared in a ManagedCluster object.
func ExtractServiceTemplateNamesFromManagedCluster(rawObj client.Object) []string {
cluster, ok := rawObj.(*ManagedCluster)
if !ok {
return nil
}

templates := []string{}
for _, s := range cluster.Spec.Services {
templates = append(templates, s.Template)
}

return templates
}

// release

// ReleaseVersionIndexKey indexer field name to extract release version from a Release object.
const ReleaseVersionIndexKey = ".spec.version"

func setupReleaseVersionIndexer(ctx context.Context, mgr ctrl.Manager) error {
return mgr.GetFieldIndexer().IndexField(ctx, &Release{}, ReleaseVersionIndexKey, extractReleaseVersion)
}

func extractReleaseVersion(rawObj client.Object) []string {
release, ok := rawObj.(*Release)
if !ok {
return nil
}
return []string{release.Spec.Version}
}

// ReleaseTemplatesIndexKey indexer field name to extract component template names from a Release object.
const ReleaseTemplatesIndexKey = "releaseTemplates"

func setupReleaseTemplatesIndexer(ctx context.Context, mgr ctrl.Manager) error {
return mgr.GetFieldIndexer().IndexField(ctx, &Release{}, ReleaseTemplatesIndexKey, extractReleaseTemplates)
}

func extractReleaseTemplates(rawObj client.Object) []string {
release, ok := rawObj.(*Release)
if !ok {
return nil
}

return release.Templates()
}

// template chains

// TemplateChainSupportedTemplatesIndexKey indexer field name to extract supported template names from an according TemplateChain object.
const TemplateChainSupportedTemplatesIndexKey = ".spec.supportedTemplates[].Name"

func setupClusterTemplateChainIndexer(ctx context.Context, mgr ctrl.Manager) error {
return mgr.GetFieldIndexer().IndexField(ctx, &ClusterTemplateChain{}, TemplateChainSupportedTemplatesIndexKey, extractSupportedTemplatesNames)
}

func setupServiceTemplateChainIndexer(ctx context.Context, mgr ctrl.Manager) error {
return mgr.GetFieldIndexer().IndexField(ctx, &ServiceTemplateChain{}, TemplateChainSupportedTemplatesIndexKey, extractSupportedTemplatesNames)
}

func extractSupportedTemplatesNames(rawObj client.Object) []string {
chainSpec := TemplateChainSpec{}
switch chain := rawObj.(type) {
case *ClusterTemplateChain:
chainSpec = chain.Spec
case *ServiceTemplateChain:
chainSpec = chain.Spec
default:
return nil
}

supportedTemplates := make([]string, 0, len(chainSpec.SupportedTemplates))
for _, t := range chainSpec.SupportedTemplates {
supportedTemplates = append(supportedTemplates, t.Name)
}

return supportedTemplates
}

// cluster template

// ClusterTemplateProvidersIndexKey indexer field name to extract provider names from a ClusterTemplate object.
const ClusterTemplateProvidersIndexKey = "clusterTemplateProviders"

func setupClusterTemplateProvidersIndexer(ctx context.Context, mgr ctrl.Manager) error {
return mgr.GetFieldIndexer().IndexField(ctx, &ClusterTemplate{}, ClusterTemplateProvidersIndexKey, ExtractProvidersFromClusterTemplate)
}

// ExtractProvidersFromClusterTemplate returns provider names from a ClusterTemplate object.
func ExtractProvidersFromClusterTemplate(o client.Object) []string {
ct, ok := o.(*ClusterTemplate)
if !ok {
return nil
}

return ct.Status.Providers
}

// multicluster service

// MultiClusterServiceTemplatesIndexKey indexer field name to extract service templates names from a MultiClusterService object.
const MultiClusterServiceTemplatesIndexKey = "serviceTemplates"

func setupMultiClusterServiceServicesIndexer(ctx context.Context, mgr ctrl.Manager) error {
return mgr.GetFieldIndexer().IndexField(ctx, &MultiClusterService{}, MultiClusterServiceTemplatesIndexKey, ExtractServiceTemplateNamesFromMultiClusterService)
}

// ExtractServiceTemplateNamesFromMultiClusterService returns a list of service templates names
// declared in a MultiClusterService object.
func ExtractServiceTemplateNamesFromMultiClusterService(rawObj client.Object) []string {
mcs, ok := rawObj.(*MultiClusterService)
if !ok {
return nil
}

templates := make([]string, len(mcs.Spec.Services))
for i, s := range mcs.Spec.Services {
templates[i] = s.Template
}

return templates
}
3 changes: 1 addition & 2 deletions api/v1alpha1/management_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,7 @@ type ManagementStatus struct {
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 supported contract versions, if specified in ProviderTemplates, on the Management cluster.
// AvailableProviders holds all available CAPI providers.
AvailableProviders Providers `json:"availableProviders,omitempty"`
// ObservedGeneration is the last observed generation.
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
Expand Down
Loading

0 comments on commit 23ace50

Please sign in to comment.