Skip to content

Commit 937aa93

Browse files
committed
Infra: Create Dedicated Hosts for cluster if necessary
Add support to create a Dedicated Host for a Infrastructure cluster, if defined and it doesn't already exist.
1 parent c106673 commit 937aa93

File tree

5 files changed

+164
-0
lines changed

5 files changed

+164
-0
lines changed

cloud/scope/vpc_cluster.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,15 @@ func (s *VPCClusterScope) SetResourceStatus(resourceType infrav1beta2.ResourceTy
562562
return
563563
}
564564
s.IBMVPCCluster.Status.Image.Set(*resource)
565+
case infrav1beta2.ResourceTypeDedicatedHost:
566+
if s.IBMVPCCluster.Status.DedicatedHosts == nil {
567+
s.IBMVPCCluster.Status.DedicatedHosts = make(map[string]*infrav1beta2.ResourceStatus)
568+
}
569+
if dHost, ok := s.IBMVPCCluster.Status.DedicatedHosts[*resource.Name]; ok {
570+
dHost.Set(*resource)
571+
} else {
572+
s.IBMVPCCluster.Status.DedicatedHosts[*resource.Name] = resource
573+
}
565574
case infrav1beta2.ResourceTypeControlPlaneSubnet:
566575
if s.NetworkStatus() == nil {
567576
s.IBMVPCCluster.Status.Network = &infrav1beta2.VPCNetworkStatus{}
@@ -907,6 +916,104 @@ func (s *VPCClusterScope) buildCOSObjectHRef() (*string, error) {
907916
return ptr.To(href), nil
908917
}
909918

919+
// ReconcileDedicatedHosts reconciles the VPC Dedicated Hosts, if defined.
920+
func (s *VPCClusterScope) ReconcileDedicatedHosts() (bool, error) {
921+
if len(s.IBMVPCCluster.Spec.DedicatedHosts) == 0 {
922+
s.V(3).Info("no dedicated hosts to reconcile")
923+
return false, nil
924+
}
925+
926+
requeue := false
927+
for _, dHost := range s.IBMVPCCluster.Spec.DedicatedHosts {
928+
var dHostDetails *vpcv1.DedicatedHost
929+
var err error
930+
switch {
931+
// Attempt lookup of Dedicated Host by ID, if provided. A defined ID expects the Dedicated Host to exist.
932+
case dHost.ID != nil:
933+
dHostDetails, _, err = s.VPCClient.GetDedicatedHost(&vpcv1.GetDedicatedHostOptions{
934+
ID: dHost.ID,
935+
})
936+
if err != nil {
937+
return false, fmt.Errorf("error looking up existing dedicated host by id %s: %w", *dHost.ID, err)
938+
} else if dHostDetails == nil {
939+
return false, fmt.Errorf("error unable to retrieve existing dedicated host by id %s", *dHost.ID)
940+
}
941+
s.V(3).Info("found dedicated host by id", "id", *dHost.ID)
942+
// Attempt lookup of Dedicated Host by Name, if provided. A defined name could be for an existing Dedicated Host, or if not found and a profile provided, create a new Dedicated Host.
943+
case dHost.Name != nil:
944+
dHostDetails, err = s.VPCClient.GetDedicatedHostByName(*dHost.Name)
945+
switch {
946+
case err != nil:
947+
return false, fmt.Errorf("error looking up dedicated host by name %s: %w", *dHost.Name, err)
948+
case dHostDetails == nil && dHost.Profile != nil:
949+
// If Dedicated Host not found by name and a profile is defined, try to create a new Dedicated Host.
950+
dHostDetails, _, err = s.createDedicatedHost(dHost)
951+
if err != nil {
952+
return false, fmt.Errorf("error creating dedicated host %s: %w", *dHost.Name, err)
953+
} else if dHostDetails == nil {
954+
return false, fmt.Errorf("error failed to create dedicated host %s", *dHost.Name)
955+
}
956+
s.V(3).Info("created dedicated host", "name", *dHost.Name)
957+
// Flag for requeue after creating a new Dedicated Host.
958+
requeue = true
959+
// Update Status with the new Dedicated Host now and move on to next Dedicated Host to reconcile.
960+
s.SetResourceStatus(infrav1beta2.ResourceTypeDedicatedHost, &infrav1beta2.ResourceStatus{
961+
ID: *dHostDetails.ID,
962+
Name: dHost.Name,
963+
Ready: false,
964+
})
965+
continue
966+
case dHostDetails == nil:
967+
return false, fmt.Errorf("error unable to retrieve existing dedicated host by name %s", *dHost.Name)
968+
default:
969+
s.V(3).Info("found dedicated host by name", "name", *dHost.Name)
970+
}
971+
default:
972+
return false, fmt.Errorf("error cannot reconcile dedicated host without id and name")
973+
}
974+
// Determine whether the Dedicated Host is ready and available for provisioning VSI's.
975+
dHostReady := *dHostDetails.State == vpcv1.DedicatedHostStateAvailableConst && *dHostDetails.Provisionable
976+
// Update Dedicated Host Status.
977+
s.SetResourceStatus(infrav1beta2.ResourceTypeDedicatedHost, &infrav1beta2.ResourceStatus{
978+
ID: *dHostDetails.ID,
979+
Name: dHostDetails.Name,
980+
Ready: dHostReady,
981+
})
982+
// If the Dedicated Host is not ready, flag for requeue.
983+
if !dHostReady {
984+
requeue = true
985+
}
986+
}
987+
988+
return requeue, nil
989+
}
990+
991+
// createDedicatedHost will create a new Dedicated Host as defined by the VPCDedicatedHost.
992+
func (s *VPCClusterScope) createDedicatedHost(dHost infrav1beta2.VPCDedicatedHost) (*vpcv1.DedicatedHost, *core.DetailedResponse, error) {
993+
// Collect Resource Group ID for Dedicated Host creation options.
994+
resourceGroupID, err := s.GetResourceGroupID()
995+
if err != nil {
996+
return nil, nil, fmt.Errorf("error retrieving resource group id for dedicated host creation %s: %w", *dHost.Name, err)
997+
}
998+
dHostOptions := &vpcv1.CreateDedicatedHostOptions{
999+
// NOTE(cjschaef): Currently only support creating a new Dedicated Host by Zone, not by Dedicated Host Group.
1000+
DedicatedHostPrototype: &vpcv1.DedicatedHostPrototypeDedicatedHostByZone{
1001+
Name: dHost.Name,
1002+
Profile: &vpcv1.DedicatedHostProfileIdentityByName{
1003+
Name: dHost.Profile,
1004+
},
1005+
ResourceGroup: &vpcv1.ResourceGroupIdentityByID{
1006+
ID: ptr.To(resourceGroupID),
1007+
},
1008+
Zone: &vpcv1.ZoneIdentityByName{
1009+
Name: ptr.To(dHost.Zone),
1010+
},
1011+
},
1012+
}
1013+
1014+
return s.VPCClient.CreateDedicatedHost(dHostOptions)
1015+
}
1016+
9101017
// ReconcileSubnets reconciles the VPC Subnet(s).
9111018
// For Subnets, we collect all of the required subnets, for each Plane, and reconcile them individually. Requeing if one is missing or just created. Reconciliation is attempted on all subnets each loop, to prevent single subnet creation per reconciliation loop.
9121019
func (s *VPCClusterScope) ReconcileSubnets() (bool, error) {

controllers/ibmvpccluster_controller.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,19 @@ func (r *IBMVPCClusterReconciler) reconcileCluster(clusterScope *scope.VPCCluste
261261
clusterScope.Info("Reconciliation of VPC Custom Image complete")
262262
conditions.MarkTrue(clusterScope.IBMVPCCluster, infrav1beta2.ImageReadyCondition)
263263

264+
// Reconcile the cluster's Dedicated Hosts, if requested.
265+
clusterScope.Info("Reconciling VPC Dedicated Hosts")
266+
if requeue, err := clusterScope.ReconcileDedicatedHosts(); err != nil {
267+
clusterScope.Error(err, "failed to reconcile dedicated hosts")
268+
conditions.MarkFalse(clusterScope.IBMVPCCluster, infrav1beta2.VPCDedicatedHostReadyCondition, infrav1beta2.VPCDedicatedHostReconciliationFailedReason, capiv1beta1.ConditionSeverityError, "%s", err.Error())
269+
return reconcile.Result{}, err
270+
} else if requeue {
271+
clusterScope.Info("VPC Dedicated Hosts creation is pending, requeueing")
272+
return reconcile.Result{RequeueAfter: 15 * time.Second}, nil
273+
}
274+
clusterScope.Info("Reconciliation of VPC Dedicated Hosts complete")
275+
conditions.MarkTrue(clusterScope.IBMVPCCluster, infrav1beta2.VPCDedicatedHostReadyCondition)
276+
264277
// Reconcile the cluster's VPC Subnets.
265278
clusterScope.Info("Reconciling VPC Subnets")
266279
if requeue, err := clusterScope.ReconcileSubnets(); err != nil {

pkg/cloud/services/vpc/mock/vpc_generated.go

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/cloud/services/vpc/service.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ func (s *Service) ListInstances(options *vpcv1.ListInstancesOptions) (*vpcv1.Ins
6060
return s.vpcService.ListInstances(options)
6161
}
6262

63+
// CreateDedicatedHost creates a new Dedicated Host.
64+
func (s *Service) CreateDedicatedHost(options *vpcv1.CreateDedicatedHostOptions) (*vpcv1.DedicatedHost, *core.DetailedResponse, error) {
65+
return s.vpcService.CreateDedicatedHost(options)
66+
}
67+
68+
// GetDedicatedHost returns a Dedicated Host.
69+
func (s *Service) GetDedicatedHost(options *vpcv1.GetDedicatedHostOptions) (*vpcv1.DedicatedHost, *core.DetailedResponse, error) {
70+
return s.vpcService.GetDedicatedHost(options)
71+
}
72+
6373
// GetDedicatedHostByName returns Dedicated Host with given name. If not found, returns nil.
6474
func (s *Service) GetDedicatedHostByName(dHostName string) (*vpcv1.DedicatedHost, error) {
6575
var dHost *vpcv1.DedicatedHost

pkg/cloud/services/vpc/vpc.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ type Vpc interface {
3030
DeleteInstance(options *vpcv1.DeleteInstanceOptions) (*core.DetailedResponse, error)
3131
GetInstance(options *vpcv1.GetInstanceOptions) (*vpcv1.Instance, *core.DetailedResponse, error)
3232
ListInstances(options *vpcv1.ListInstancesOptions) (*vpcv1.InstanceCollection, *core.DetailedResponse, error)
33+
CreateDedicatedHost(options *vpcv1.CreateDedicatedHostOptions) (*vpcv1.DedicatedHost, *core.DetailedResponse, error)
34+
GetDedicatedHost(options *vpcv1.GetDedicatedHostOptions) (*vpcv1.DedicatedHost, *core.DetailedResponse, error)
3335
GetDedicatedHostByName(dHostName string) (*vpcv1.DedicatedHost, error)
3436
CreateVPC(options *vpcv1.CreateVPCOptions) (*vpcv1.VPC, *core.DetailedResponse, error)
3537
DeleteVPC(options *vpcv1.DeleteVPCOptions) (response *core.DetailedResponse, err error)

0 commit comments

Comments
 (0)