Skip to content

Commit

Permalink
VPC: Create v2 path for new Infrastructure implementation (#1858)
Browse files Browse the repository at this point in the history
Create a v2 path that will be used for the new Infrastructure
implementation for VPC Clusters. All new functionality will
be placed in these new v2 paths, based on the new Network
field, to prevent breaking existing implementation.
  • Loading branch information
cjschaef authored Jul 15, 2024
1 parent af008bd commit 5b01057
Show file tree
Hide file tree
Showing 2 changed files with 232 additions and 0 deletions.
168 changes: 168 additions & 0 deletions cloud/scope/vpc_cluster.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
Copyright 2024 The Kubernetes Authors.
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 scope

import (
"context"
"errors"
"fmt"

"github.com/go-logr/logr"

"github.com/IBM/go-sdk-core/v5/core"
"github.com/IBM/platform-services-go-sdk/resourcecontrollerv2"

"k8s.io/klog/v2/textlogger"

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

capiv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1"
"sigs.k8s.io/cluster-api/util/patch"

infrav1beta2 "sigs.k8s.io/cluster-api-provider-ibmcloud/api/v1beta2"
"sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/authenticator"
"sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/cos"
"sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/resourcecontroller"
"sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/resourcemanager"
"sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/vpc"
"sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/endpoints"
)

const (
// LOGDEBUGLEVEL indicates the debug level of the logs.
LOGDEBUGLEVEL = 5
)

// VPCClusterScopeParams defines the input parameters used to create a new VPCClusterScope.
type VPCClusterScopeParams struct {
Client client.Client
Cluster *capiv1beta1.Cluster
IBMVPCCluster *infrav1beta2.IBMVPCCluster
Logger logr.Logger
ServiceEndpoint []endpoints.ServiceEndpoint

IBMVPCClient vpc.Vpc
}

// VPCClusterScope defines a scope defined around a VPC Cluster.
type VPCClusterScope struct {
logr.Logger
Client client.Client
patchHelper *patch.Helper

COSClient cos.Cos
ResourceControllerClient resourcecontroller.ResourceController
ResourceManagerClient resourcemanager.ResourceManager
VPCClient vpc.Vpc

Cluster *capiv1beta1.Cluster
IBMVPCCluster *infrav1beta2.IBMVPCCluster
ServiceEndpoint []endpoints.ServiceEndpoint
}

// NewVPCClusterScope creates a new VPCClusterScope from the supplied parameters.
func NewVPCClusterScope(params VPCClusterScopeParams) (*VPCClusterScope, error) {
if params.Client == nil {
err := errors.New("error failed to generate new scope from nil Client")
return nil, err
}
if params.Cluster == nil {
err := errors.New("error failed to generate new scope from nil Cluster")
return nil, err
}
if params.IBMVPCCluster == nil {
err := errors.New("error failed to generate new scope from nil IBMVPCCluster")
return nil, err
}
if params.Logger == (logr.Logger{}) {
params.Logger = textlogger.NewLogger(textlogger.NewConfig())
}

helper, err := patch.NewHelper(params.IBMVPCCluster, params.Client)
if err != nil {
return nil, fmt.Errorf("error failed to init patch helper: %w", err)
}

vpcEndpoint := endpoints.FetchVPCEndpoint(params.IBMVPCCluster.Spec.Region, params.ServiceEndpoint)
vpcClient, err := vpc.NewService(vpcEndpoint)
if err != nil {
return nil, fmt.Errorf("error failed to create IBM VPC client: %w", err)
}

if params.IBMVPCCluster.Spec.Network == nil || params.IBMVPCCluster.Spec.Region == "" {
return nil, fmt.Errorf("error failed to generate vpc client as Network or Region is nil")
}

if params.Logger.V(LOGDEBUGLEVEL).Enabled() {
core.SetLoggingLevel(core.LevelDebug)
}

auth, err := authenticator.GetAuthenticator()
if err != nil {
return nil, fmt.Errorf("error failed to create authenticator: %w", err)
}

// Create Global Tagging client.
// TODO(cjschaef): need service support.

// Create Resource Controller client.
rcOptions := resourcecontroller.ServiceOptions{
ResourceControllerV2Options: &resourcecontrollerv2.ResourceControllerV2Options{
Authenticator: auth,
},
}
// Fetch the resource controller endpoint.
rcEndpoint := endpoints.FetchEndpoints(string(endpoints.RC), params.ServiceEndpoint)
if rcEndpoint != "" {
rcOptions.URL = rcEndpoint
params.Logger.V(3).Info("Overriding the default resource controller endpoint", "ResourceControllerEndpoint", rcEndpoint)
}
resourceControllerClient, err := resourcecontroller.NewService(rcOptions)
if err != nil {
return nil, fmt.Errorf("error failed to create resource controller client: %w", err)
}

// Create Resource Manager client.
// TODO(cjschaef): Need to extend ResourceManager service and endpoint support to add properly.

clusterScope := &VPCClusterScope{
Logger: params.Logger,
Client: params.Client,
patchHelper: helper,
Cluster: params.Cluster,
IBMVPCCluster: params.IBMVPCCluster,
ServiceEndpoint: params.ServiceEndpoint,
ResourceControllerClient: resourceControllerClient,
VPCClient: vpcClient,
}
return clusterScope, nil
}

// PatchObject persists the cluster configuration and status.
func (s *VPCClusterScope) PatchObject() error {
return s.patchHelper.Patch(context.TODO(), s.IBMVPCCluster)
}

// Close closes the current scope persisting the cluster configuration and status.
func (s *VPCClusterScope) Close() error {
return s.PatchObject()
}

// Name returns the CAPI cluster name.
func (s *VPCClusterScope) Name() string {
return s.Cluster.Name
}
64 changes: 64 additions & 0 deletions controllers/ibmvpccluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ func (r *IBMVPCClusterReconciler) Reconcile(ctx context.Context, req ctrl.Reques
return ctrl.Result{}, err
}

// Determine whether the Cluster is designed for extended Infrastructure support, implemented in a separate path.
if ibmCluster.Spec.Network != nil {
return r.reconcileV2(ctx, req)
}

// Fetch the Cluster.
cluster, err := util.GetOwnerCluster(ctx, r.Client, ibmCluster.ObjectMeta)
if err != nil {
Expand Down Expand Up @@ -109,6 +114,57 @@ func (r *IBMVPCClusterReconciler) Reconcile(ctx context.Context, req ctrl.Reques
return r.reconcile(clusterScope)
}

func (r *IBMVPCClusterReconciler) reconcileV2(ctx context.Context, req ctrl.Request) (_ ctrl.Result, reterr error) {
log := r.Log.WithValues("ibmvpccluster", req.NamespacedName)

// Fetch the IBMVPCCluster instance.
ibmCluster := &infrav1beta2.IBMVPCCluster{}
err := r.Get(ctx, req.NamespacedName, ibmCluster)
if err != nil {
if apierrors.IsNotFound(err) {
return ctrl.Result{}, nil
}
return ctrl.Result{}, err
}

// Fetch the Cluster.
cluster, err := util.GetOwnerCluster(ctx, r.Client, ibmCluster.ObjectMeta)
if err != nil {
return ctrl.Result{}, err
}
if cluster == nil {
log.Info("Cluster Controller has not yet set OwnerRef")
return ctrl.Result{}, nil
}

clusterScope, err := scope.NewVPCClusterScope(scope.VPCClusterScopeParams{
Client: r.Client,
Logger: log,
Cluster: cluster,
IBMVPCCluster: ibmCluster,
ServiceEndpoint: r.ServiceEndpoint,
})

// Always close the scope when exiting this function so we can persist any IBMVPCCluster changes.
defer func() {
if clusterScope != nil {
if err := clusterScope.Close(); err != nil && reterr == nil {
reterr = err
}
}
}()

// Handle deleted clusters.
if !ibmCluster.DeletionTimestamp.IsZero() {
return r.reconcileDeleteV2(clusterScope)
}

if err != nil {
return reconcile.Result{}, fmt.Errorf("failed to create scope: %w", err)
}
return r.reconcileCluster(clusterScope)
}

func (r *IBMVPCClusterReconciler) reconcile(clusterScope *scope.ClusterScope) (ctrl.Result, error) {
// If the IBMVPCCluster doesn't have our finalizer, add it.
if controllerutil.AddFinalizer(clusterScope.IBMVPCCluster, infrav1beta2.ClusterFinalizer) {
Expand Down Expand Up @@ -173,6 +229,10 @@ func (r *IBMVPCClusterReconciler) reconcile(clusterScope *scope.ClusterScope) (c
return ctrl.Result{}, nil
}

func (r *IBMVPCClusterReconciler) reconcileCluster(_ *scope.VPCClusterScope) (ctrl.Result, error) {
return ctrl.Result{}, fmt.Errorf("not implemented")
}

func (r *IBMVPCClusterReconciler) reconcileDelete(clusterScope *scope.ClusterScope) (ctrl.Result, error) {
// check if still have existing VSIs.
listVSIOpts := &vpcv1.ListInstancesOptions{
Expand Down Expand Up @@ -227,6 +287,10 @@ func (r *IBMVPCClusterReconciler) reconcileDelete(clusterScope *scope.ClusterSco
return handleFinalizerRemoval(clusterScope)
}

func (r *IBMVPCClusterReconciler) reconcileDeleteV2(_ *scope.VPCClusterScope) (ctrl.Result, error) {
return ctrl.Result{}, fmt.Errorf("not implemented")
}

func (r *IBMVPCClusterReconciler) getOrCreate(clusterScope *scope.ClusterScope) (*vpcv1.LoadBalancer, error) {
loadBalancer, err := clusterScope.CreateLoadBalancer()
return loadBalancer, err
Expand Down

0 comments on commit 5b01057

Please sign in to comment.