Skip to content

Commit

Permalink
feat: add ability to detect CAPI version and installed infra providers
Browse files Browse the repository at this point in the history
Make manager fetch existing CAPI state, so it should be possible to
manage clusters even if CAPI wasn't initialized in the same code path.

Signed-off-by: Artem Chernyshev <[email protected]>
  • Loading branch information
Unix4ever committed Sep 8, 2021
1 parent c20b1a8 commit 5e78193
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 19 deletions.
88 changes: 88 additions & 0 deletions pkg/capi/capi.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"k8s.io/client-go/rest"
clientcmd "k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client"
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"

Expand All @@ -33,6 +34,7 @@ type Manager struct {
clientset *kubernetes.Clientset
config *rest.Config
runtimeClient runtimeclient.Client
version string

options Options
}
Expand Down Expand Up @@ -92,6 +94,10 @@ func NewManager(ctx context.Context, options Options) (*Manager, error) {
return nil, err
}

if err = clusterAPI.FetchState(ctx); err != nil {
return nil, err
}

return clusterAPI, nil
}

Expand Down Expand Up @@ -199,6 +205,88 @@ func (clusterAPI *Manager) Install(ctx context.Context) error {
return nil
}

// FetchState fetches infra providers and installed CAPI version if any.
func (clusterAPI *Manager) FetchState(ctx context.Context) error {
resources, err := clusterAPI.clientset.ServerPreferredResources()
if err != nil {
return err
}

gv := schema.GroupVersion{}

for _, list := range resources {
for _, resource := range list.APIResources {
if resource.Kind == "Provider" {
gv, err = schema.ParseGroupVersion(list.GroupVersion)

if err != nil {
return err
}
}
}
}

// Assume CAPI not installed
if gv.Version == "" {
return nil
}

providers := &unstructured.UnstructuredList{}
providers.SetGroupVersionKind(schema.GroupVersionKind{
Kind: "Provider",
Group: gv.Group,
Version: gv.Version,
})

if err = clusterAPI.runtimeClient.List(ctx, providers); err != nil {
return err
}

var (
providerName string
providerVersion string
providerType string
ok bool
)

infrastructureProviders := []infrastructure.Provider{}

for _, provider := range providers.Items {
if providerType, ok, err = unstructured.NestedString(provider.Object, "type"); err != nil {
return err
} else if !ok {
return fieldNotFound("type")
}

if clusterctlv1.ProviderType(providerType) == clusterctlv1.InfrastructureProviderType {
if providerName, ok, err = unstructured.NestedString(provider.Object, "providerName"); err != nil {
return err
} else if !ok {
return fieldNotFound("providerName")
}

if providerVersion, ok, err = unstructured.NestedString(provider.Object, "version"); err != nil {
return err
} else if !ok {
return fieldNotFound("providerVersion")
}

provider, err := infrastructure.NewProvider(fmt.Sprintf("%s:%s", providerName, providerVersion))
// if we couldn't parse it then it's not supported
if err != nil {
continue
}

infrastructureProviders = append(infrastructureProviders, provider)
}
}

clusterAPI.options.InfrastructureProviders = infrastructureProviders
clusterAPI.version = gv.Version

return nil
}

type ref struct {
types.NamespacedName
gvk schema.GroupVersionKind
Expand Down
6 changes: 3 additions & 3 deletions pkg/capi/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ import (

// CheckClusterReady verifies that cluster ready from the CAPI point of view.
//nolint:cyclop,gocyclo,gocognit
func CheckClusterReady(ctx context.Context, metalClient client.Client, clusterName, version string) error {
func (clusterAPI *Manager) CheckClusterReady(ctx context.Context, metalClient client.Client, clusterName string) error {
var cluster unstructured.Unstructured

cluster.SetGroupVersionKind(
schema.GroupVersionKind{
Version: version,
Version: clusterAPI.version,
Group: "cluster.x-k8s.io",
Kind: "Cluster",
},
Expand Down Expand Up @@ -122,7 +122,7 @@ func CheckClusterReady(ctx context.Context, metalClient client.Client, clusterNa

machineDeployments.SetGroupVersionKind(
schema.GroupVersionKind{
Version: version,
Version: clusterAPI.version,
Group: "cluster.x-k8s.io",
Kind: "MachineDeployment",
},
Expand Down
8 changes: 4 additions & 4 deletions pkg/capi/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type Cluster struct {

// NewCluster fetches cluster info from the CAPI state.
//nolint:gocyclo,cyclop
func (clusterAPI *Manager) NewCluster(ctx context.Context, clusterName, version string) (*Cluster, error) {
func (clusterAPI *Manager) NewCluster(ctx context.Context, clusterName string) (*Cluster, error) {
var (
cluster unstructured.Unstructured
controlPlane unstructured.Unstructured
Expand All @@ -55,15 +55,15 @@ func (clusterAPI *Manager) NewCluster(ctx context.Context, clusterName, version

cluster.SetGroupVersionKind(
schema.GroupVersionKind{
Version: version,
Version: clusterAPI.version,
Group: "cluster.x-k8s.io",
Kind: "Cluster",
},
)

machines.SetGroupVersionKind(
schema.GroupVersionKind{
Version: version,
Version: clusterAPI.version,
Group: "cluster.x-k8s.io",
Kind: "Machine",
},
Expand Down Expand Up @@ -204,7 +204,7 @@ func (clusterAPI *Manager) NewCluster(ctx context.Context, clusterName, version
controlPlaneNodes: controlPlaneNodes,
workerNodes: workerNodes,
client: talosClient,
capiVersion: version,
capiVersion: clusterAPI.version,
}, nil
}

Expand Down
17 changes: 5 additions & 12 deletions pkg/capi/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"log"
"os"
"strconv"
"strings"
"time"

"github.com/talos-systems/go-retry/retry"
Expand Down Expand Up @@ -153,7 +152,7 @@ func WithProviderOptions(val interface{}) DeployOption {
}

// DeployCluster creates a new cluster.
//nolint:gocognit,gocyclo,cyclop
//nolint:gocognit
func (clusterAPI *Manager) DeployCluster(ctx context.Context, clusterName string, setters ...DeployOption) (*Cluster, error) {
if len(clusterAPI.options.InfrastructureProviders) == 0 {
return nil, fmt.Errorf("no infrastructure providers are installed")
Expand Down Expand Up @@ -235,25 +234,19 @@ func (clusterAPI *Manager) DeployCluster(ctx context.Context, clusterName string
return nil, err
}

var version string

for _, obj := range template.Objs() {
if err = clusterAPI.runtimeClient.Create(ctx, &obj); err != nil {
return nil, err
}

if version == "" {
version = strings.Split(obj.GetAPIVersion(), "/")[1]
}
}

if err = retry.Constant(30*time.Minute, retry.WithUnits(10*time.Second), retry.WithErrorLogging(true)).Retry(func() error {
return CheckClusterReady(ctx, clusterAPI.runtimeClient, clusterName, version)
return clusterAPI.CheckClusterReady(ctx, clusterAPI.runtimeClient, clusterName)
}); err != nil {
return nil, err
}

deployedCluster, err := clusterAPI.NewCluster(ctx, options.ClusterName, version)
deployedCluster, err := clusterAPI.NewCluster(ctx, options.ClusterName)
if err != nil {
return nil, err
}
Expand All @@ -262,14 +255,14 @@ func (clusterAPI *Manager) DeployCluster(ctx context.Context, clusterName string
}

// DestroyCluster deletes cluster.
func (clusterAPI *Manager) DestroyCluster(ctx context.Context, name, namespace, version string) error {
func (clusterAPI *Manager) DestroyCluster(ctx context.Context, name, namespace string) error {
cluster := &unstructured.Unstructured{}
cluster.SetName(name)
cluster.SetNamespace(namespace)
cluster.SetGroupVersionKind(schema.GroupVersionKind{
Group: "cluster.x-k8s.io",
Kind: "Cluster",
Version: version,
Version: clusterAPI.version,
})

if err := clusterAPI.runtimeClient.Delete(ctx, cluster); err != nil {
Expand Down

0 comments on commit 5e78193

Please sign in to comment.