Skip to content

Commit

Permalink
updated lan creation logic
Browse files Browse the repository at this point in the history
  • Loading branch information
lubedacht committed Jan 10, 2024
1 parent 0e924e5 commit 09be88b
Show file tree
Hide file tree
Showing 14 changed files with 420 additions and 213 deletions.
2 changes: 1 addition & 1 deletion api/v1alpha1/ionoscloudcluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (

const (
// ClusterFinalizer allows cleanup of resources, which are
// associated with the IonosCloudCluster before removing it from the apiserver.
// associated with the IonosCloudCluster before removing it from the API server.
ClusterFinalizer = "ionoscloudcluster.infrastructure.cluster.x-k8s.io"

// IonosCloudClusterReady is the condition for the IonosCloudCluster, which indicates that the cluster is ready.
Expand Down
18 changes: 18 additions & 0 deletions api/v1alpha1/ionoscloudmachine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,24 @@ import (
const (
// IonosCloudMachineType is the named type for the API object.
IonosCloudMachineType = "IonosCloudMachine"

// MachineFinalizer is the finalizer for the IonosCloudMachine resources.
// It will prevent the deletion of the resource until it was removed by the controller
// to ensure that related cloud resources will be deleted before the IonosCloudMachine resource
// will be removed from the API server.
MachineFinalizer = "ionoscloudmachine.infrastructure.cluster.x-k8s.io"

// MachineProvisionedCondition documents the status of the provisioning of a IonosCloudMachine and
// the underlying enterprise VM.
MachineProvisionedCondition clusterv1.ConditionType = "MachineProvisioned"

// WaitingForClusterInfrastructureReason (Severity=Info) indicates, that the IonosCloudMachine is currently
// waiting for the cluster infrastructure to become ready.
WaitingForClusterInfrastructureReason = "WaitingForClusterInfrastructure"

// WaitingForBootstrapDataReason (Severity=Info) indicates, that the bootstrap provider has not yet finished
// creating the bootstrap data secret and store it in the Cluster API Machine.
WaitingForBootstrapDataReason = "WaitingForBootstrapData"
)

// VolumeDiskType specifies the type of hard disk.
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ require (
k8s.io/apimachinery v0.28.4
k8s.io/client-go v0.28.4
k8s.io/klog/v2 v2.110.1
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2
k8s.io/utils v0.0.0-20240102154912-e7106e64919e
sigs.k8s.io/cluster-api v1.6.0
sigs.k8s.io/controller-runtime v0.16.3
)
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5Ohx
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM=
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk=
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ=
k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/cluster-api v1.6.0 h1:2bhVSnUbtWI8taCjd9lGiHExsRUpKf7Z1fXqi/IwYx4=
sigs.k8s.io/cluster-api v1.6.0/go.mod h1:LB7u/WxiWj4/bbpHNOa1oQ8nq0MQ5iYlD0pGfRSBGLI=
sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4=
Expand Down
8 changes: 1 addition & 7 deletions internal/controller/ionoscloudcluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,7 @@ func (r *IonosCloudClusterReconciler) Reconcile(ctx context.Context, req ctrl.Re
// Make sure to persist the changes to the cluster before exiting the function.
defer func() {
if err := clusterScope.Finalize(); err != nil {
if retErr != nil {
retErr = errors.Join(err, retErr)
return
}
retErr = err
retErr = errors.Join(err, retErr)
}
}()

Expand All @@ -121,9 +117,7 @@ func (r *IonosCloudClusterReconciler) Reconcile(ctx context.Context, req ctrl.Re

//nolint:unparam
func (r *IonosCloudClusterReconciler) reconcileNormal(_ context.Context, clusterScope *scope.ClusterScope) (ctrl.Result, error) {
// TODO(lubedacht): setup cloud resources which are required before we create the machines
controllerutil.AddFinalizer(clusterScope.IonosCluster, infrav1.ClusterFinalizer)

conditions.MarkTrue(clusterScope.IonosCluster, infrav1.IonosCloudClusterReady)
clusterScope.IonosCluster.Status.Ready = true

Expand Down
106 changes: 85 additions & 21 deletions internal/controller/ionoscloudmachine_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ package controller

import (
"context"
"errors"
"fmt"
"github.com/ionos-cloud/cluster-api-provider-ionoscloud/internal/service/cloud"
"sigs.k8s.io/cluster-api/util/conditions"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"time"

"github.com/go-logr/logr"
apierrors "k8s.io/apimachinery/pkg/api/errors"
Expand All @@ -33,7 +38,6 @@ import (

infrav1 "github.com/ionos-cloud/cluster-api-provider-ionoscloud/api/v1alpha1"
"github.com/ionos-cloud/cluster-api-provider-ionoscloud/internal/ionoscloud"
"github.com/ionos-cloud/cluster-api-provider-ionoscloud/internal/service"
"github.com/ionos-cloud/cluster-api-provider-ionoscloud/scope"
)

Expand Down Expand Up @@ -94,11 +98,11 @@ func (r *IonosCloudMachineReconciler) Reconcile(ctx context.Context, req ctrl.Re

logger = logger.WithValues("cluster", klog.KObj(cluster))

infraCluster, err := r.getInfraCluster(ctx, &logger, cluster, ionosCloudMachine)
clusterScope, err := r.getClusterScope(ctx, &logger, cluster, ionosCloudMachine)
if err != nil {
return ctrl.Result{}, fmt.Errorf("error getting infra provider cluster or control plane object: %w", err)
}
if infraCluster == nil {
if clusterScope == nil {
logger.Info("ionos cloud machine is not ready yet")
return ctrl.Result{}, nil
}
Expand All @@ -108,7 +112,7 @@ func (r *IonosCloudMachineReconciler) Reconcile(ctx context.Context, req ctrl.Re
Client: r.Client,
Cluster: cluster,
Machine: machine,
InfraCluster: infraCluster,
InfraCluster: clusterScope,
IonosCloudMachine: ionosCloudMachine,
Logger: &logger,
})
Expand All @@ -117,37 +121,97 @@ func (r *IonosCloudMachineReconciler) Reconcile(ctx context.Context, req ctrl.Re
return ctrl.Result{}, err
}

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

machineService, err := service.NewMachineService(ctx, machineScope)
cloudService, err := cloud.NewService(ctx, machineScope)
if err != nil {
return ctrl.Result{}, fmt.Errorf("could not create machine service")
}
if !ionosCloudMachine.ObjectMeta.DeletionTimestamp.IsZero() {
return r.reconcileDelete(machineService)
return r.reconcileDelete(cloudService)
}

return r.reconcileNormal(machineService)
return r.reconcileNormal(machineScope, clusterScope, cloudService)
}

func (r *IonosCloudMachineReconciler) reconcileNormal(machineService *service.MachineService) (ctrl.Result, error) {
lan, err := machineService.GetLAN()
if err != nil {
return ctrl.Result{}, fmt.Errorf("could not reconcile LAN: %w", err)
func (r *IonosCloudMachineReconciler) isInfrastructureReady(machineScope *scope.MachineScope) bool {
// Make sure the infrastructure is ready.
if !machineScope.Cluster.Status.InfrastructureReady {
machineScope.Info("Cluster infrastructure is not ready yet")
conditions.MarkFalse(
machineScope.IonosCloudMachine,
infrav1.MachineProvisionedCondition,
infrav1.WaitingForClusterInfrastructureReason,
clusterv1.ConditionSeverityInfo, "")

return false
}
if lan == nil {
return ctrl.Result{Requeue: true}, nil

// Make sure to wait until the data secret was created
if machineScope.Machine.Spec.Bootstrap.DataSecretName == nil {
machineScope.Info("Boostrap data secret is not available yet")

Check failure on line 156 in internal/controller/ionoscloudmachine_controller.go

View workflow job for this annotation

GitHub Actions / codespell

Boostrap ==> Bootstrap
conditions.MarkFalse(
machineScope.IonosCloudMachine,
infrav1.MachineProvisionedCondition,
infrav1.WaitingForBootstrapDataReason,
clusterv1.ConditionSeverityInfo, "",
)

return false
}

return true
}

func (r *IonosCloudMachineReconciler) reconcileNormal(machineScope *scope.MachineScope, _ *scope.ClusterScope, cloudService *cloud.Service) (ctrl.Result, error) {
machineScope.V(4).Info("Reconciling IonosCloudMachine")

if machineScope.HasFailed() {
machineScope.Info("Error state detected, skipping reconciliation")
return ctrl.Result{}, nil
}

if !r.isInfrastructureReady(machineScope) {
return ctrl.Result{}, nil
}

if controllerutil.AddFinalizer(machineScope.IonosCloudMachine, infrav1.MachineFinalizer) {
if err := machineScope.PatchObject(); err != nil {
machineScope.Error(err, "unable to update finalizer on object")
return ctrl.Result{}, err
}
}

// TODO(lubedacht) Check before starting reconciliation if there is any pending request in the Ionos cluster or machine spec
// If there is, query for the request and check the status
// Status:
// * Done = Clear request from the status and continue reconciliation
// * Queued, Running => Requeue the current request
// * Failed => We need to discuss this, log error and continue (retry last request in the corresponding reconcile function)

// Ensure that a lan is created in the datacenter
if requeue, err := cloudService.ReconcileLAN(); err != nil || requeue {
if requeue {
return ctrl.Result{RequeueAfter: time.Second * 30}, err
}
return ctrl.Result{}, fmt.Errorf("could not reconcile LAN %w", err)
}

//if err != nil {
// return ctrl.Result{}, fmt.Errorf("could not reconcile LAN: %w", err)
//}
//if lan == nil {
// return ctrl.Result{Requeue: true}, nil
//}

return ctrl.Result{}, nil
}

func (r *IonosCloudMachineReconciler) reconcileDelete(machineService *service.MachineService) (ctrl.Result, error) {
func (r *IonosCloudMachineReconciler) reconcileDelete(machineService *cloud.Service) (ctrl.Result, error) {
isLANGone, err := machineService.DeleteLAN("placeholder for LAN ID")

Check failure on line 215 in internal/controller/ionoscloudmachine_controller.go

View workflow job for this annotation

GitHub Actions / go_test

machineService.DeleteLAN undefined (type *cloud.Service has no field or method DeleteLAN)

Check failure on line 215 in internal/controller/ionoscloudmachine_controller.go

View workflow job for this annotation

GitHub Actions / lint

machineService.DeleteLAN undefined (type *cloud.Service has no field or method DeleteLAN)) (typecheck)

Check failure on line 215 in internal/controller/ionoscloudmachine_controller.go

View workflow job for this annotation

GitHub Actions / lint

machineService.DeleteLAN undefined (type *cloud.Service has no field or method DeleteLAN) (typecheck)
if err != nil {
return ctrl.Result{}, fmt.Errorf("could not delete LAN: %w", err)
Expand All @@ -169,7 +233,7 @@ func (r *IonosCloudMachineReconciler) SetupWithManager(mgr ctrl.Manager) error {
Complete(r)
}

func (r *IonosCloudMachineReconciler) getInfraCluster(
func (r *IonosCloudMachineReconciler) getClusterScope(
ctx context.Context, logger *logr.Logger, cluster *clusterv1.Cluster, ionosCloudMachine *infrav1.IonosCloudMachine,
) (*scope.ClusterScope, error) {
var clusterScope *scope.ClusterScope
Expand Down
7 changes: 5 additions & 2 deletions internal/ionoscloud/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,11 +304,13 @@ func (c *IonosCloudClient) GetRequests(ctx context.Context, method, path string)
if method == "" {
return nil, errors.New("method needs to be provided")
}
yesterday := time.Now().Add(-24 * time.Hour).Format(time.DateTime)

lookback := time.Now().Add(-24 * time.Hour).Format(time.DateTime)
reqs, _, err := c.API.RequestsApi.RequestsGet(ctx).
Depth(3).
FilterMethod(method).
FilterUrl(path).
FilterCreatedAfter(yesterday).
FilterCreatedAfter(lookback).
Execute()
if err != nil {
return nil, fmt.Errorf("failed to get requests: %w", err)
Expand All @@ -318,6 +320,7 @@ func (c *IonosCloudClient) GetRequests(ctx context.Context, method, path string)
// We invert the value to sort in descending order
return -a.Metadata.CreatedDate.Compare(b.Metadata.CreatedDate.Time)
})

return &items, nil
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package service
package cloud

// DataCenterID is a shortcut for getting the data center ID used by the IONOS Cloud machine.
func (s *MachineService) DataCenterID() string {
func (s *Service) DataCenterID() string {
return s.scope.IonosCloudMachine.Spec.DatacenterID
}
Loading

0 comments on commit 09be88b

Please sign in to comment.