Skip to content

Commit

Permalink
clusterctl cleanup inventory
Browse files Browse the repository at this point in the history
  • Loading branch information
fabriziopandini committed Feb 6, 2020
1 parent 4b7ca5a commit 8aa5b6a
Show file tree
Hide file tree
Showing 19 changed files with 283 additions and 254 deletions.
14 changes: 7 additions & 7 deletions cmd/clusterctl/api/v1alpha3/metadata_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,29 +32,29 @@ type Metadata struct {
ReleaseSeries []ReleaseSeries `json:"releaseSeries"`
}

// ReleaseSeries maps a provider release series (major/minor) with a ClusterAPIVersion
// ReleaseSeries maps a provider release series (major/minor) with a API Version of Cluster API (contract).
type ReleaseSeries struct {
// Major version of the release series
Major uint `json:"major,omitempty"`

// Minor version of the release series
Minor uint `json:"minor,omitempty"`

// ClusterAPIVersion indicates the cluster API supported version.
ClusterAPIVersion string `json:"clusterAPIVersion,omitempty"`
// Contract defines the API Version of Cluster API (contract) supported by the ReleaseSeries.
Contract string `json:"contract,omitempty"`
}

func init() {
SchemeBuilder.Register(&Metadata{})
}

// HasReleaseSeriesForVersion return true if the given version is in one of the ReleaseSeries.
func (m *Metadata) HasReleaseSeriesForVersion(version *version.Version) bool {
// GetReleaseSeriesForVersion return the release series for a given version.
func (m *Metadata) GetReleaseSeriesForVersion(version *version.Version) *ReleaseSeries {
for _, releaseSeries := range m.ReleaseSeries {
if version.Major() == releaseSeries.Major && version.Minor() == releaseSeries.Minor {
return true
return &releaseSeries
}
}

return false
return nil
}
35 changes: 22 additions & 13 deletions cmd/clusterctl/api/v1alpha3/provider_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package v1alpha3

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

// +kubebuilder:resource:path=providers,scope=Namespaced,categories=cluster-api
Expand Down Expand Up @@ -47,19 +48,6 @@ type Provider struct {
WatchedNamespace string `json:"watchedNamespace,omitempty"`
}

// +kubebuilder:object:root=true

// ProviderList contains a list of Provider
type ProviderList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Provider `json:"items"`
}

func init() {
SchemeBuilder.Register(&Provider{}, &ProviderList{})
}

// ProviderType is a string representation of a TaskGroup create policy.
type ProviderType string

Expand Down Expand Up @@ -95,11 +83,19 @@ func (p *Provider) GetProviderType() ProviderType {
}
}

// InstanceName return the instance name for the provider, that is composed by the provider name and the namespace
// where the provider is installed (nb. clusterctl does not support multiple instances of the same provider to be
// installed in the same namespace)
func (p *Provider) InstanceName() string {
return types.NamespacedName{Namespace: p.Namespace, Name: p.Name}.String()
}

// HasWatchingOverlapWith returns true if the provider has an overlapping watching namespace with another provider.
func (p *Provider) HasWatchingOverlapWith(other Provider) bool {
return p.WatchedNamespace == "" || p.WatchedNamespace == other.WatchedNamespace || other.WatchedNamespace == ""
}

// Equals returns true if two providers are exactly the same.
func (p *Provider) Equals(other Provider) bool {
return p.Name == other.Name &&
p.Namespace == other.Namespace &&
Expand All @@ -108,6 +104,15 @@ func (p *Provider) Equals(other Provider) bool {
p.Version == other.Version
}

// +kubebuilder:object:root=true

// ProviderList contains a list of Provider
type ProviderList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Provider `json:"items"`
}

func (l *ProviderList) FilterByName(name string) []Provider {
return l.filterBy(func(p Provider) bool {
return p.Name == name
Expand Down Expand Up @@ -147,3 +152,7 @@ func (l *ProviderList) filterBy(predicate func(p Provider) bool) []Provider {
}
return ret
}

func init() {
SchemeBuilder.Register(&Provider{}, &ProviderList{})
}
16 changes: 8 additions & 8 deletions cmd/clusterctl/cmd/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ var upgradePlanCmd = &cobra.Command{
Long: LongDesc(`
The upgrade plan command provides a list of recommended target versions for upgrading Cluster API providers in a management cluster.
The providers are grouped into management groups, each one defining a set of providers that should be supporting the same cluster API version
in order to guarantee the proper functioning of the management cluster.
The providers are grouped into management groups, each one defining a set of providers that should be supporting
the same API Version of Cluster API (contract) in order to guarantee the proper functioning of the management cluster.
Then, for each provider in a management group, the following upgrade options are provided:
- The latest patch release for the current Cluster API version.
- The latest patch release for the next Cluster API version, if available.`),
- The latest patch release for the current API Version of Cluster API (contract).
- The latest patch release for the next API Version of Cluster API (contract), if available.`),

Example: Examples(`
# Gets the recommended target versions for upgrading Cluster API providers.
Expand Down Expand Up @@ -83,7 +83,7 @@ func runUpgradePlan() error {
return err
}

// ensure upgrade plans are sorted consistently (by CoreProvider.Namespace, ClusterAPIVersion).
// ensure upgrade plans are sorted consistently (by CoreProvider.Namespace, Contract).
sortUpgradePlans(upgradePlans)

if len(upgradePlans) == 0 {
Expand All @@ -98,7 +98,7 @@ func runUpgradePlan() error {
upgradeAvailable := false

fmt.Println("")
fmt.Printf("Management group: %s/%s, latest release available for the %s Cluster API version:\n", plan.CoreProvider.Namespace, plan.CoreProvider.Name, plan.ClusterAPIVersion)
fmt.Printf("Management group: %s, latest release available for the %s API Version of Cluster API (contract):\n", plan.CoreProvider.InstanceName(), plan.Contract)
fmt.Println("")
w := tabwriter.NewWriter(os.Stdout, 10, 4, 3, ' ', 0)
fmt.Fprintln(w, "NAME\tNAMESPACE\tTYPE\tCURRENT VERSION\tNEXT VERSION")
Expand All @@ -114,7 +114,7 @@ func runUpgradePlan() error {
if upgradeAvailable {
fmt.Println("You can now apply the upgrade by executing the following command:")
fmt.Println("")
fmt.Println(fmt.Sprintf(" upgrade apply --management-group %s/%s --cluster-api-version %s", plan.CoreProvider.Namespace, plan.CoreProvider.Name, plan.ClusterAPIVersion))
fmt.Println(fmt.Sprintf(" upgrade apply --management-group %s --cluster-api-version %s", plan.CoreProvider.InstanceName(), plan.Contract))
} else {
fmt.Println("You are already up to date!")
}
Expand All @@ -136,7 +136,7 @@ func sortUpgradeItems(plan client.UpgradePlan) {
func sortUpgradePlans(upgradePlans []client.UpgradePlan) {
sort.Slice(upgradePlans, func(i, j int) bool {
return upgradePlans[i].CoreProvider.Namespace < upgradePlans[j].CoreProvider.Namespace ||
(upgradePlans[i].CoreProvider.Namespace == upgradePlans[j].CoreProvider.Namespace && upgradePlans[i].ClusterAPIVersion < upgradePlans[j].ClusterAPIVersion)
(upgradePlans[i].CoreProvider.Namespace == upgradePlans[j].CoreProvider.Namespace && upgradePlans[i].Contract < upgradePlans[j].Contract)
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ spec:
releaseSeries:
items:
description: ReleaseSeries maps a provider release series (major/minor)
with a ClusterAPIVersion
with a API Version of Cluster API (contract).
properties:
clusterAPIVersion:
description: ClusterAPIVersion indicates the cluster API supported
version.
contract:
description: Contract defines the API Version of Cluster API (contract)
supported by the ReleaseSeries.
type: string
major:
description: Major version of the release series
Expand Down
2 changes: 1 addition & 1 deletion cmd/clusterctl/pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ type Client interface {

// PlanUpgrade returns a set of suggested Upgrade plans for the cluster, and more specifically:
// - Each management group gets separated upgrade plans.
// - For each management group, an upgrade plan is generated for each ClusterAPIVersion available, e.g.
// - For each management group, an upgrade plan is generated for each API Version of Cluster API (contract) available, e.g.
// - Upgrade to the latest version in the the v1alpha2 series: ....
// - Upgrade to the latest version in the the v1alpha3 series: ....
PlanUpgrade(options PlanUpgradeOptions) ([]UpgradePlan, error)
Expand Down
30 changes: 7 additions & 23 deletions cmd/clusterctl/pkg/client/cluster/inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ type InventoryClient interface {
Create(clusterctlv1.Provider) error

// List returns the inventory items for all the provider instances installed in the cluster.
List() ([]clusterctlv1.Provider, error)
List() (*clusterctlv1.ProviderList, error)

// GetDefaultProviderName returns the default provider for a given ProviderType.
// In case there is only a single provider for a given type, e.g. only the AWS infrastructure Provider, it returns
Expand All @@ -71,15 +71,7 @@ type InventoryClient interface {
GetDefaultProviderNamespace(provider string) (string, error)

// GetManagementGroups returns the list of management groups defined in the management cluster.
GetManagementGroups() ([]ManagementGroup, error)
}

// ManagementGroup is a group of providers composed by a CoreProvider and a set of Bootstrap/ControlPlane/Infrastructure providers
// watching objects in the same namespace. For example, a management group can be used for upgrades, in order to ensure all the providers
// in a management group support the same ClusterAPI version.
type ManagementGroup struct {
CoreProvider clusterctlv1.Provider
Providers []clusterctlv1.Provider
GetManagementGroups() (ManagementGroupList, error)
}

// inventoryClient implements InventoryClient.
Expand Down Expand Up @@ -178,7 +170,7 @@ func (p *inventoryClient) EnsureCustomResourceDefinitions() error {
}

func (p *inventoryClient) Validate(m clusterctlv1.Provider) error {
providerList, err := p.list()
providerList, err := p.List()
if err != nil {
return err
}
Expand Down Expand Up @@ -242,15 +234,7 @@ func (p *inventoryClient) Create(m clusterctlv1.Provider) error {
return nil
}

func (p *inventoryClient) List() ([]clusterctlv1.Provider, error) {
providerList, err := p.list()
if err != nil {
return nil, err
}
return providerList.Items, nil
}

func (p *inventoryClient) list() (*clusterctlv1.ProviderList, error) {
func (p *inventoryClient) List() (*clusterctlv1.ProviderList, error) {
cl, err := p.proxy.NewClient()
if err != nil {
return nil, err
Expand All @@ -264,7 +248,7 @@ func (p *inventoryClient) list() (*clusterctlv1.ProviderList, error) {
}

func (p *inventoryClient) GetDefaultProviderName(providerType clusterctlv1.ProviderType) (string, error) {
providerList, err := p.list()
providerList, err := p.List()
if err != nil {
return "", err
}
Expand All @@ -285,7 +269,7 @@ func (p *inventoryClient) GetDefaultProviderName(providerType clusterctlv1.Provi
}

func (p *inventoryClient) GetDefaultProviderVersion(provider string) (string, error) {
providerList, err := p.list()
providerList, err := p.List()
if err != nil {
return "", err
}
Expand All @@ -305,7 +289,7 @@ func (p *inventoryClient) GetDefaultProviderVersion(provider string) (string, er
}

func (p *inventoryClient) GetDefaultProviderNamespace(provider string) (string, error) {
providerList, err := p.list()
providerList, err := p.List()
if err != nil {
return "", err
}
Expand Down
68 changes: 54 additions & 14 deletions cmd/clusterctl/pkg/client/cluster/inventory_managementgroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,50 @@ limitations under the License.
package cluster

import (
"fmt"
"strings"

"github.com/pkg/errors"
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
)

func (p *inventoryClient) GetManagementGroups() ([]ManagementGroup, error) {
providerList, err := p.list()
if err != nil {
return nil, err
// ManagementGroup is a group of providers composed by a CoreProvider and a set of Bootstrap/ControlPlane/Infrastructure providers
// watching objects in the same namespace. For example, a management group can be used for upgrades, in order to ensure all the providers
// in a management group support the same API Version of Cluster API (contract).
type ManagementGroup struct {
CoreProvider clusterctlv1.Provider
Providers []clusterctlv1.Provider
}

// Equals return true if two management groups have the same core provider.
func (mg *ManagementGroup) Equals(other *ManagementGroup) bool {
return mg.CoreProvider.Equals(other.CoreProvider)
}

// GetProviderByInstanceName returns a specific provider instance.
func (mg *ManagementGroup) GetProviderByInstanceName(instanceName string) *clusterctlv1.Provider {
for _, provider := range mg.Providers {
if provider.InstanceName() == instanceName {
return &provider
}
}
return nil
}

// ManagementGroupList defines a list of management groups
type ManagementGroupList []ManagementGroup

// FindManagementGroupByProviderInstanceName return the management group that hosts a given provider.
func (ml *ManagementGroupList) FindManagementGroupByProviderInstanceName(instanceName string) *ManagementGroup {
for _, managementGroup := range *ml {
if p := managementGroup.GetProviderByInstanceName(instanceName); p != nil {
return &managementGroup
}
}
return nil
}

// deriveManagementGroups derives the management groups from a list of providers.
func deriveManagementGroups(providerList *clusterctlv1.ProviderList) (ManagementGroupList, error) {
// If any of the core providers watch the same namespace, we cannot define the management group.
if err := checkOverlappingCoreProviders(providerList); err != nil {
return nil, err
Expand All @@ -43,7 +74,7 @@ func (p *inventoryClient) GetManagementGroups() ([]ManagementGroup, error) {
}

// Composes the management group
managementGroups := []ManagementGroup{}
managementGroups := ManagementGroupList{}
for _, coreProvider := range providerList.FilterCore() {
group := ManagementGroup{CoreProvider: coreProvider}
for _, provider := range providerList.Items {
Expand All @@ -70,9 +101,9 @@ func checkOverlappingCoreProviders(providerList *clusterctlv1.ProviderList) erro

// check for overlapping namespaces
if provider.HasWatchingOverlapWith(other) {
return errors.Errorf("Unable to identify management groups: core providers %s/%s and %s/%s have overlapping watching namespaces",
provider.Namespace, provider.Name,
other.Namespace, other.Name,
return errors.Errorf("Unable to identify management groups: core providers %s and %s have overlapping watching namespaces",
provider.InstanceName(),
other.InstanceName(),
)
}
}
Expand All @@ -91,24 +122,33 @@ func checkOverlappingProviders(providerList *clusterctlv1.ProviderList) error {
var overlappingCoreProviders []string
for _, coreProvider := range providerList.FilterCore() {
if provider.HasWatchingOverlapWith(coreProvider) {
overlappingCoreProviders = append(overlappingCoreProviders, fmt.Sprintf("%s/%s", coreProvider.Namespace, coreProvider.Name))
overlappingCoreProviders = append(overlappingCoreProviders, coreProvider.InstanceName())
}
}

// if the provider does not overlap with any core provider, return error (it will not be part of any management group)
if len(overlappingCoreProviders) == 0 {
return errors.Errorf("Unable to identify management groups: provider %s/%s can't be combined with any core provider",
provider.Namespace, provider.Name,
return errors.Errorf("Unable to identify management groups: provider %s can't be combined with any core provider",
provider.InstanceName(),
)
}

// if the provider overlaps with more than one core provider, return error (it is part of two management groups --> e.g. there could be potential upgrade conflicts)
if len(overlappingCoreProviders) > 1 {
return errors.Errorf("Unable to identify management groupss: provider %s/%s is watching for objects in namespaces controlled by more than one core provider (%s)",
provider.Namespace, provider.Name,
return errors.Errorf("Unable to identify management groupss: provider %s is watching for objects in namespaces controlled by more than one core provider (%s)",
provider.InstanceName(),
strings.Join(overlappingCoreProviders, " ,"),
)
}
}
return nil
}

func (p *inventoryClient) GetManagementGroups() (ManagementGroupList, error) {
providerList, err := p.List()
if err != nil {
return nil, err
}

return deriveManagementGroups(providerList)
}
Loading

0 comments on commit 8aa5b6a

Please sign in to comment.