From 4ae891069587f331e4c2a73dd26065c874d38076 Mon Sep 17 00:00:00 2001 From: cjschaef Date: Wed, 26 Jun 2024 11:31:39 -0500 Subject: [PATCH] VPC: Create v2 path for new Infrastructure implementation 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 NetworkSpec field, to prevent breaking existing implementation. --- api/v1beta1/ibmvpc_conversion.go | 8 + api/v1beta1/zz_generated.conversion.go | 31 ++- api/v1beta2/conditions_consts.go | 4 + api/v1beta2/ibmvpccluster_types.go | 17 ++ api/v1beta2/zz_generated.deepcopy.go | 39 ++++ cloud/scope/util.go | 17 ++ cloud/scope/vpc_cluster.go | 182 ++++++++++++++++++ ...cture.cluster.x-k8s.io_ibmvpcclusters.yaml | 54 ++++++ ...uster.x-k8s.io_ibmvpcclustertemplates.yaml | 54 ++++++ controllers/ibmvpccluster_controller.go | 64 ++++++ 10 files changed, 450 insertions(+), 20 deletions(-) create mode 100644 cloud/scope/vpc_cluster.go diff --git a/api/v1beta1/ibmvpc_conversion.go b/api/v1beta1/ibmvpc_conversion.go index 3679f88b4..f049de2bf 100644 --- a/api/v1beta1/ibmvpc_conversion.go +++ b/api/v1beta1/ibmvpc_conversion.go @@ -223,3 +223,11 @@ func Convert_Slice_Pointer_v1beta2_IBMVPCResourceReference_To_Slice_Pointer_stri func Convert_v1beta2_VPCLoadBalancerSpec_To_v1beta1_VPCLoadBalancerSpec(in *infrav1beta2.VPCLoadBalancerSpec, out *VPCLoadBalancerSpec, s apiconversion.Scope) error { return autoConvert_v1beta2_VPCLoadBalancerSpec_To_v1beta1_VPCLoadBalancerSpec(in, out, s) } + +func Convert_v1beta2_IBMVPCClusterSpec_To_v1beta1_IBMVPCClusterSpec(in *infrav1beta2.IBMVPCClusterSpec, out *IBMVPCClusterSpec, s apiconversion.Scope) error { + return autoConvert_v1beta2_IBMVPCClusterSpec_To_v1beta1_IBMVPCClusterSpec(in, out, s) +} + +func Convert_v1beta2_IBMVPCClusterStatus_To_v1beta1_IBMVPCClusterStatus(in *infrav1beta2.IBMVPCClusterStatus, out *IBMVPCClusterStatus, s apiconversion.Scope) error { + return autoConvert_v1beta2_IBMVPCClusterStatus_To_v1beta1_IBMVPCClusterStatus(in, out, s) +} diff --git a/api/v1beta1/zz_generated.conversion.go b/api/v1beta1/zz_generated.conversion.go index 7eed081c5..411c522d6 100644 --- a/api/v1beta1/zz_generated.conversion.go +++ b/api/v1beta1/zz_generated.conversion.go @@ -259,21 +259,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.IBMVPCClusterSpec)(nil), (*IBMVPCClusterSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_IBMVPCClusterSpec_To_v1beta1_IBMVPCClusterSpec(a.(*v1beta2.IBMVPCClusterSpec), b.(*IBMVPCClusterSpec), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*IBMVPCClusterStatus)(nil), (*v1beta2.IBMVPCClusterStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_IBMVPCClusterStatus_To_v1beta2_IBMVPCClusterStatus(a.(*IBMVPCClusterStatus), b.(*v1beta2.IBMVPCClusterStatus), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.IBMVPCClusterStatus)(nil), (*IBMVPCClusterStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_IBMVPCClusterStatus_To_v1beta1_IBMVPCClusterStatus(a.(*v1beta2.IBMVPCClusterStatus), b.(*IBMVPCClusterStatus), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*IBMVPCMachine)(nil), (*v1beta2.IBMVPCMachine)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_IBMVPCMachine_To_v1beta2_IBMVPCMachine(a.(*IBMVPCMachine), b.(*v1beta2.IBMVPCMachine), scope) }); err != nil { @@ -444,6 +434,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1beta2.IBMVPCClusterSpec)(nil), (*IBMVPCClusterSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_IBMVPCClusterSpec_To_v1beta1_IBMVPCClusterSpec(a.(*v1beta2.IBMVPCClusterSpec), b.(*IBMVPCClusterSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.IBMVPCClusterStatus)(nil), (*IBMVPCClusterStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_IBMVPCClusterStatus_To_v1beta1_IBMVPCClusterStatus(a.(*v1beta2.IBMVPCClusterStatus), b.(*IBMVPCClusterStatus), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1beta2.IBMVPCMachineSpec)(nil), (*IBMVPCMachineSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_IBMVPCMachineSpec_To_v1beta1_IBMVPCMachineSpec(a.(*v1beta2.IBMVPCMachineSpec), b.(*IBMVPCMachineSpec), scope) }); err != nil { @@ -1269,14 +1269,10 @@ func autoConvert_v1beta2_IBMVPCClusterSpec_To_v1beta1_IBMVPCClusterSpec(in *v1be } else { out.ControlPlaneLoadBalancer = nil } + // WARNING: in.NetworkSpec requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta2_IBMVPCClusterSpec_To_v1beta1_IBMVPCClusterSpec is an autogenerated conversion function. -func Convert_v1beta2_IBMVPCClusterSpec_To_v1beta1_IBMVPCClusterSpec(in *v1beta2.IBMVPCClusterSpec, out *IBMVPCClusterSpec, s conversion.Scope) error { - return autoConvert_v1beta2_IBMVPCClusterSpec_To_v1beta1_IBMVPCClusterSpec(in, out, s) -} - func autoConvert_v1beta1_IBMVPCClusterStatus_To_v1beta2_IBMVPCClusterStatus(in *IBMVPCClusterStatus, out *v1beta2.IBMVPCClusterStatus, s conversion.Scope) error { if err := Convert_v1beta1_VPC_To_v1beta2_VPC(&in.VPC, &out.VPC, s); err != nil { return err @@ -1314,11 +1310,6 @@ func autoConvert_v1beta2_IBMVPCClusterStatus_To_v1beta1_IBMVPCClusterStatus(in * return nil } -// Convert_v1beta2_IBMVPCClusterStatus_To_v1beta1_IBMVPCClusterStatus is an autogenerated conversion function. -func Convert_v1beta2_IBMVPCClusterStatus_To_v1beta1_IBMVPCClusterStatus(in *v1beta2.IBMVPCClusterStatus, out *IBMVPCClusterStatus, s conversion.Scope) error { - return autoConvert_v1beta2_IBMVPCClusterStatus_To_v1beta1_IBMVPCClusterStatus(in, out, s) -} - func autoConvert_v1beta1_IBMVPCMachine_To_v1beta2_IBMVPCMachine(in *IBMVPCMachine, out *v1beta2.IBMVPCMachine, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta if err := Convert_v1beta1_IBMVPCMachineSpec_To_v1beta2_IBMVPCMachineSpec(&in.Spec, &out.Spec, s); err != nil { diff --git a/api/v1beta2/conditions_consts.go b/api/v1beta2/conditions_consts.go index a3cda5597..d8b72d296 100644 --- a/api/v1beta2/conditions_consts.go +++ b/api/v1beta2/conditions_consts.go @@ -120,4 +120,8 @@ const ( // CreateInfrastructureAnnotation is the name of an annotation that indicates if // Power VS infrastructure should be created as a part of cluster creation. CreateInfrastructureAnnotation = "powervs.cluster.x-k8s.io/create-infra" + + // CreateVPCInfrastructureAnnotation is the name of an annotation that indicates if + // VPC infrastructure should be created as part of cluster creation. + CreateVPCInfrastructureAnnotation = "vpc.cluster.x-k8s.io/create-infra" ) diff --git a/api/v1beta2/ibmvpccluster_types.go b/api/v1beta2/ibmvpccluster_types.go index 507b21f97..16c401485 100644 --- a/api/v1beta2/ibmvpccluster_types.go +++ b/api/v1beta2/ibmvpccluster_types.go @@ -54,6 +54,10 @@ type IBMVPCClusterSpec struct { // ControlPlaneLoadBalancer is optional configuration for customizing control plane behavior. // +optional ControlPlaneLoadBalancer *VPCLoadBalancerSpec `json:"controlPlaneLoadBalancer,omitempty"` + + // networkSpec represents the VPC network to use for the cluster. + // +optional + NetworkSpec *VPCNetworkSpec `json:"networkSpec,omitempty"` } // VPCLoadBalancerSpec defines the desired state of an VPC load balancer. @@ -94,6 +98,19 @@ type AdditionalListenerSpec struct { Port int64 `json:"port"` } +// VPCNetworkSpec defines the desired state of the network resources for the cluster. +type VPCNetworkSpec struct { + // computeSubnetsSpec is a set of Subnet's which define the Compute subnets. + ComputeSubnetsSpec []Subnet `json:"computeSubnetsSpec,omitempty"` + // controlPlaneSubnetsSpec is a set of Subnet's which define the Control Plane subnets. + ControlPlaneSubnetsSpec []Subnet `json:"controlPlaneSubentsSpec,omitempty"` + // resourceGroup is the name of the Resource Group containing all of the newtork resources. + // This can be different than the Resource Group containing the remaining cluster resources. + ResourceGroup *string `json:"resourceGroup,omitempty"` + + // TODO(cjschaef): Complete spec definition (SecurityGroups, VPC) +} + // VPCSecurityGroupStatus defines a vpc security group resource status with its id and respective rule's ids. type VPCSecurityGroupStatus struct { // id represents the id of the resource. diff --git a/api/v1beta2/zz_generated.deepcopy.go b/api/v1beta2/zz_generated.deepcopy.go index 06aac4cc0..bad86686c 100644 --- a/api/v1beta2/zz_generated.deepcopy.go +++ b/api/v1beta2/zz_generated.deepcopy.go @@ -873,6 +873,11 @@ func (in *IBMVPCClusterSpec) DeepCopyInto(out *IBMVPCClusterSpec) { *out = new(VPCLoadBalancerSpec) (*in).DeepCopyInto(*out) } + if in.NetworkSpec != nil { + in, out := &in.NetworkSpec, &out.NetworkSpec + *out = new(VPCNetworkSpec) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IBMVPCClusterSpec. @@ -1485,6 +1490,40 @@ func (in *VPCLoadBalancerStatus) DeepCopy() *VPCLoadBalancerStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VPCNetworkSpec) DeepCopyInto(out *VPCNetworkSpec) { + *out = *in + if in.ComputeSubnetsSpec != nil { + in, out := &in.ComputeSubnetsSpec, &out.ComputeSubnetsSpec + *out = make([]Subnet, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ControlPlaneSubnetsSpec != nil { + in, out := &in.ControlPlaneSubnetsSpec, &out.ControlPlaneSubnetsSpec + *out = make([]Subnet, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ResourceGroup != nil { + in, out := &in.ResourceGroup, &out.ResourceGroup + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VPCNetworkSpec. +func (in *VPCNetworkSpec) DeepCopy() *VPCNetworkSpec { + if in == nil { + return nil + } + out := new(VPCNetworkSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VPCResourceReference) DeepCopyInto(out *VPCResourceReference) { *out = *in diff --git a/cloud/scope/util.go b/cloud/scope/util.go index 7559d1d64..5b64eadd1 100644 --- a/cloud/scope/util.go +++ b/cloud/scope/util.go @@ -57,3 +57,20 @@ func CheckCreateInfraAnnotation(cluster infrav1beta2.IBMPowerVSCluster) bool { } return createInfra } + +// CheckCreateVPCInfraAnnotation checks for annotations set on IBMVPCCluster object to determine cluster creation workflow. +func CheckCreateVPCInfraAnnotation(cluster infrav1beta2.IBMVPCCluster) bool { + annotations := cluster.GetAnnotations() + if len(annotations) == 0 { + return false + } + value, found := annotations[infrav1beta2.CreateVPCInfrastructureAnnotation] + if !found { + return false + } + createInfra, err := strconv.ParseBool(value) + if err != nil { + return false + } + return createInfra +} diff --git a/cloud/scope/vpc_cluster.go b/cloud/scope/vpc_cluster.go new file mode 100644 index 000000000..2371ad35a --- /dev/null +++ b/cloud/scope/vpc_cluster.go @@ -0,0 +1,182 @@ +/* +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 vpc.cluster.x-k8s.io/create-infra=true annotation is not set, only need vpc client. + if !CheckCreateVPCInfraAnnotation(*params.IBMVPCCluster) { + return &VPCClusterScope{ + Logger: params.Logger, + Client: params.Client, + patchHelper: helper, + Cluster: params.Cluster, + IBMVPCCluster: params.IBMVPCCluster, + ServiceEndpoint: params.ServiceEndpoint, + VPCClient: vpcClient, + }, nil + } + + // if vpc.cluster.x-k8s.io/create-infra=true annotation is set, create necessary clients. + if params.IBMVPCCluster.Spec.NetworkSpec == nil || params.IBMVPCCluster.Spec.Region == "" { + return nil, fmt.Errorf("error failed to generate vpc client as NetworkSpec info 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 +} diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_ibmvpcclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_ibmvpcclusters.yaml index 9713d8506..838576e95 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_ibmvpcclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_ibmvpcclusters.yaml @@ -290,6 +290,60 @@ spec: private type: boolean type: object + networkSpec: + description: networkSpec represents the VPC network to use for the + cluster. + properties: + computeSubnetsSpec: + description: computeSubnetsSpec is a set of Subnet's which define + the Compute subnets. + items: + description: Subnet describes a subnet. + properties: + cidr: + type: string + id: + maxLength: 64 + minLength: 1 + pattern: ^[-0-9a-z_]+$ + type: string + name: + maxLength: 63 + minLength: 1 + pattern: ^([a-z]|[a-z][-a-z0-9]*[a-z0-9])$ + type: string + zone: + type: string + type: object + type: array + controlPlaneSubentsSpec: + description: controlPlaneSubnetsSpec is a set of Subnet's which + define the Control Plane subnets. + items: + description: Subnet describes a subnet. + properties: + cidr: + type: string + id: + maxLength: 64 + minLength: 1 + pattern: ^[-0-9a-z_]+$ + type: string + name: + maxLength: 63 + minLength: 1 + pattern: ^([a-z]|[a-z][-a-z0-9]*[a-z0-9])$ + type: string + zone: + type: string + type: object + type: array + resourceGroup: + description: |- + resourceGroup is the name of the Resource Group containing all of the newtork resources. + This can be different than the Resource Group containing the remaining cluster resources. + type: string + type: object region: description: The IBM Cloud Region the cluster lives in. type: string diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_ibmvpcclustertemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_ibmvpcclustertemplates.yaml index 9b8a360d9..acc76d66f 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_ibmvpcclustertemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_ibmvpcclustertemplates.yaml @@ -139,6 +139,60 @@ spec: or private type: boolean type: object + networkSpec: + description: networkSpec represents the VPC network to use + for the cluster. + properties: + computeSubnetsSpec: + description: computeSubnetsSpec is a set of Subnet's which + define the Compute subnets. + items: + description: Subnet describes a subnet. + properties: + cidr: + type: string + id: + maxLength: 64 + minLength: 1 + pattern: ^[-0-9a-z_]+$ + type: string + name: + maxLength: 63 + minLength: 1 + pattern: ^([a-z]|[a-z][-a-z0-9]*[a-z0-9])$ + type: string + zone: + type: string + type: object + type: array + controlPlaneSubentsSpec: + description: controlPlaneSubnetsSpec is a set of Subnet's + which define the Control Plane subnets. + items: + description: Subnet describes a subnet. + properties: + cidr: + type: string + id: + maxLength: 64 + minLength: 1 + pattern: ^[-0-9a-z_]+$ + type: string + name: + maxLength: 63 + minLength: 1 + pattern: ^([a-z]|[a-z][-a-z0-9]*[a-z0-9])$ + type: string + zone: + type: string + type: object + type: array + resourceGroup: + description: |- + resourceGroup is the name of the Resource Group containing all of the newtork resources. + This can be different than the Resource Group containing the remaining cluster resources. + type: string + type: object region: description: The IBM Cloud Region the cluster lives in. type: string diff --git a/controllers/ibmvpccluster_controller.go b/controllers/ibmvpccluster_controller.go index 066736d57..56141ad64 100644 --- a/controllers/ibmvpccluster_controller.go +++ b/controllers/ibmvpccluster_controller.go @@ -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.NetworkSpec != nil { + return r.reconcileV2(ctx, req) + } + // Fetch the Cluster. cluster, err := util.GetOwnerCluster(ctx, r.Client, ibmCluster.ObjectMeta) if err != nil { @@ -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) { @@ -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{ @@ -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