Skip to content

Commit

Permalink
Merge pull request #31 from GDATASoftwareAG/rework-to-lan-spec
Browse files Browse the repository at this point in the history
Rework to lan spec
  • Loading branch information
farodin91 authored Nov 13, 2023
2 parents cecdb2e + 553084c commit 4b5e1f1
Show file tree
Hide file tree
Showing 13 changed files with 228 additions and 215 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ The API itself is shared across multiple cloud providers allowing for IONOS Clou
* Choice of Linux distribution between Ubuntu 22.04 and other cloud init distribution using Server Templates based on raw images from [image builder](image_builder).
* Using cloud init for bootstrapping nodes.
* Installs only the minimal components to bootstrap a control plane and workers.
* multi ipv4 lan's (private and public lan)

# Roadmap

Expand All @@ -28,7 +29,7 @@ The API itself is shared across multiple cloud providers allowing for IONOS Clou
* failuredomains for control planes
* failuredomains for machinedeployment
* autoscaler integrations example
* multi lan (private and public lan)
* external managed datacenter

---

Expand Down
28 changes: 6 additions & 22 deletions api/v1alpha1/ionoscloudcluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,26 +51,12 @@ const (
// issues with the creation of the datacenter.
DataCenterCreationFailedReason = "DataCenterCreationFailed"

// PublicLanCreatedCondition documents the creation of the Lan
PublicLanCreatedCondition clusterv1.ConditionType = "PublicLanCreated"
// LanCreatedCondition documents the creation of the Lan
LanCreatedCondition clusterv1.ConditionType = "LanCreated"

// PublicLanCreationFailedReason (Severity=Error) documents a controller detecting
// LanCreationFailedReason (Severity=Error) documents a controller detecting
// issues with the creation of the Lan.
PublicLanCreationFailedReason = "PublicLanCreationFailed"

// PrivateLanCreatedCondition documents the creation of the Lan
PrivateLanCreatedCondition clusterv1.ConditionType = "PrivateLanCreated"

// PrivateLanCreationFailedReason (Severity=Error) documents a controller detecting
// issues with the creation of the Lan.
PrivateLanCreationFailedReason = "PrivateLanCreationFailed"

// InternetLanCreatedCondition documents the creation of the Lan
InternetLanCreatedCondition clusterv1.ConditionType = "InternetLanCreated"

// InternetLanCreationFailedReason (Severity=Error) documents a controller detecting
// issues with the creation of the Lan.
InternetLanCreationFailedReason = "InternetLanCreationFailed"
LanCreationFailedReason = "LanCreationFailed"

// LoadBalancerForwardingRuleCreatedCondition documents the creation of the ForwardingRule
LoadBalancerForwardingRuleCreatedCondition clusterv1.ConditionType = "LoadBalancerForwardingRuleCreated"
Expand Down Expand Up @@ -102,12 +88,10 @@ type IONOSCloudClusterSpec struct {
IdentityName string `json:"identityName"`
// +optional
ControlPlaneEndpoint clusterv1.APIEndpoint `json:"controlPlaneEndpoint"`
// +optional
// +listType=map
// +listMapKey=name
Lans []IONOSLanSpec `json:"lans,omitempty"`
// +optional
LoadBalancer *IONOSLoadBalancerSpec `json:"loadBalancer,omitempty"`
Lans []IONOSLanSpec `json:"lans"`
LoadBalancer IONOSLoadBalancerSpec `json:"loadBalancer"`

// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="DataCenterID is immutable"
DataCenterID string `json:"dataCenterID,omitempty"`
Expand Down
12 changes: 12 additions & 0 deletions api/v1alpha1/ionoscloudmachine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,15 @@ func (c *IONOSCloudMachine) EnsureNic(spec IONOSNicSpec) {
}
c.Spec.Nics = append(c.Spec.Nics, spec)
}

func (c *IONOSCloudMachine) NicByLan(name string) *IONOSNicSpec {
if name == "" {
return nil
}
for i := range c.Spec.Nics {
if c.Spec.Nics[i].LanRef.Name == name {
return &c.Spec.Nics[i]
}
}
return nil
}
6 changes: 1 addition & 5 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ spec:
rule: self == oldSelf
required:
- identityName
- lans
- loadBalancer
- location
type: object
x-kubernetes-validations:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ spec:
- public
type: object
type: array
x-kubernetes-list-map-keys:
- name
x-kubernetes-list-type: map
loadBalancer:
properties:
id:
Expand Down Expand Up @@ -140,6 +143,8 @@ spec:
rule: self == oldSelf
required:
- identityName
- lans
- loadBalancer
- location
type: object
x-kubernetes-validations:
Expand Down
12 changes: 12 additions & 0 deletions config/samples/cluster_template_ionoscloudcluster.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,15 @@ spec:
controlPlaneEndpoint:
host: "85.215.201.63"
port: 6443
lans:
- name: private
public: false
- name: public
public: true
- name: internet
public: true
loadBalancer:
listenerLanRef:
name: public
targetLanRef:
name: private
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ spec:
image: "a9152e16-2482-11ee-9eb0-82cf6aa9b8c3"
type: "HDD"
size: "25"
nics:
- lanRef:
name: internet
- lanRef:
name: private
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ spec:
image: "a9152e16-2482-11ee-9eb0-82cf6aa9b8c3"
type: "HDD"
size: "25"
nics:
- lanRef:
name: internet
- lanRef:
name: private
148 changes: 35 additions & 113 deletions internal/controller/ionoscloudcluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,8 @@ func (r *IONOSCloudClusterReconciler) reconcileNormal(ctx *context.ClusterContex
return *result, err
}

if result, err := r.reconcilePrivateLan(ctx); err != nil {
conditions.MarkFalse(ctx.IONOSCloudCluster, v1alpha1.PrivateLanCreatedCondition, v1alpha1.PrivateLanCreationFailedReason, clusterv1.ConditionSeverityError, err.Error())
return *result, err
}

if result, err := r.reconcilePublicLan(ctx); err != nil {
conditions.MarkFalse(ctx.IONOSCloudCluster, v1alpha1.PublicLanCreatedCondition, v1alpha1.PublicLanCreationFailedReason, clusterv1.ConditionSeverityError, err.Error())
if result, err := r.reconcileLan(ctx); err != nil {
conditions.MarkFalse(ctx.IONOSCloudCluster, v1alpha1.LanCreatedCondition, v1alpha1.LanCreationFailedReason, clusterv1.ConditionSeverityError, err.Error())
return *result, err
}

Expand All @@ -173,11 +168,6 @@ func (r *IONOSCloudClusterReconciler) reconcileNormal(ctx *context.ClusterContex
return *result, err
}

if result, err := r.reconcileInternet(ctx); err != nil {
conditions.MarkFalse(ctx.IONOSCloudCluster, v1alpha1.InternetLanCreatedCondition, v1alpha1.InternetLanCreationFailedReason, clusterv1.ConditionSeverityError, err.Error())
return *result, err
}

ctx.IONOSCloudCluster.Status.Ready = true

return reconcile.Result{}, nil
Expand Down Expand Up @@ -211,107 +201,49 @@ func (r *IONOSCloudClusterReconciler) reconcileDataCenter(ctx *context.ClusterCo
return nil, nil
}

func (r *IONOSCloudClusterReconciler) reconcilePrivateLan(ctx *context.ClusterContext) (*reconcile.Result, error) {
ctx.Logger.Info("Reconciling private Lan")
if ctx.IONOSCloudCluster.Spec.PrivateLanID == nil {
lanID, err := createLan(ctx, false)
if err != nil {
return &reconcile.Result{}, errors.Wrap(err, "error creating private Lan")
func (r *IONOSCloudClusterReconciler) reconcileLan(ctx *context.ClusterContext) (*reconcile.Result, error) {
for i := range ctx.IONOSCloudCluster.Spec.Lans {
lanSpec := &ctx.IONOSCloudCluster.Spec.Lans[i]
ctx.Logger.Info(fmt.Sprintf("Reconciling %s Lan", lanSpec.Name))
if lanSpec.LanID == nil {
lanID, err := createLan(ctx, lanSpec.Public)
if err != nil {
return &reconcile.Result{}, err
}
lanSpec.LanID = lanID
}
ctx.IONOSCloudCluster.Spec.PrivateLanID = lanID
}

ctx.IONOSCloudCluster.EnsureLan(v1alpha1.IONOSLanSpec{
Name: "private",
LanID: ctx.IONOSCloudCluster.Spec.PrivateLanID,
Public: false,
})

// check status
lanId := fmt.Sprint(*ctx.IONOSCloudCluster.Spec.PrivateLanID)
lan, resp, err := ctx.IONOSClient.GetLan(ctx, ctx.IONOSCloudCluster.Spec.DataCenterID, lanId)
// check status
lanId := fmt.Sprint(*lanSpec.LanID)
lan, resp, err := ctx.IONOSClient.GetLan(ctx, ctx.IONOSCloudCluster.Spec.DataCenterID, lanId)

if err != nil && resp.StatusCode != http.StatusNotFound {
return &reconcile.Result{}, errors.Wrap(err, "error getting private Lan")
}

if resp.StatusCode == http.StatusNotFound || *lan.Metadata.State == STATE_BUSY {
return &reconcile.Result{RequeueAfter: defaultRetryIntervalOnBusy}, errors.New("private Lan not available (yet)")
}

conditions.MarkTrue(ctx.IONOSCloudCluster, v1alpha1.PrivateLanCreatedCondition)

return nil, nil
}

func (r *IONOSCloudClusterReconciler) reconcilePublicLan(ctx *context.ClusterContext) (*reconcile.Result, error) {
ctx.Logger.Info("Reconciling public Lan")
if ctx.IONOSCloudCluster.Spec.PublicLanID == nil {
lanID, err := createLan(ctx, true)
if err != nil {
return &reconcile.Result{}, err
if err != nil && resp.StatusCode != http.StatusNotFound {
return &reconcile.Result{}, errors.Wrap(err, fmt.Sprintf("error getting %s Lan", lanSpec.Name))
}
ctx.IONOSCloudCluster.Spec.PublicLanID = lanID
}

ctx.IONOSCloudCluster.EnsureLan(v1alpha1.IONOSLanSpec{
Name: "public",
LanID: ctx.IONOSCloudCluster.Spec.PublicLanID,
Public: true,
})

// check status
lanId := fmt.Sprint(*ctx.IONOSCloudCluster.Spec.PublicLanID)
lan, resp, err := ctx.IONOSClient.GetLan(ctx, ctx.IONOSCloudCluster.Spec.DataCenterID, lanId)

if err != nil && resp.StatusCode != http.StatusNotFound {
return &reconcile.Result{}, errors.Wrap(err, "error getting public Lan")
}

if resp.StatusCode == http.StatusNotFound || *lan.Metadata.State == STATE_BUSY {
return &reconcile.Result{RequeueAfter: defaultRetryIntervalOnBusy}, errors.New("public Lan not available (yet)")
if resp.StatusCode == http.StatusNotFound || *lan.Metadata.State == STATE_BUSY {
return &reconcile.Result{RequeueAfter: defaultRetryIntervalOnBusy}, errors.New(fmt.Sprintf("%s Lan not available (yet)", lanSpec.Name))
}
}

conditions.MarkTrue(ctx.IONOSCloudCluster, v1alpha1.PublicLanCreatedCondition)
conditions.MarkTrue(ctx.IONOSCloudCluster, v1alpha1.LanCreatedCondition)
return nil, nil
}

func (r *IONOSCloudClusterReconciler) reconcileInternet(ctx *context.ClusterContext) (*reconcile.Result, error) {
ctx.Logger.Info("Reconciling internet")
if ctx.IONOSCloudCluster.Spec.InternetLanID == nil {
lanID, err := createLan(ctx, true)
if err != nil {
return &reconcile.Result{}, err
}
ctx.IONOSCloudCluster.Spec.InternetLanID = lanID
}

ctx.IONOSCloudCluster.EnsureLan(v1alpha1.IONOSLanSpec{
Name: "internet",
LanID: ctx.IONOSCloudCluster.Spec.InternetLanID,
Public: true,
})

// check status
lanId := fmt.Sprint(*ctx.IONOSCloudCluster.Spec.InternetLanID)
lan, resp, err := ctx.IONOSClient.GetLan(ctx, ctx.IONOSCloudCluster.Spec.DataCenterID, lanId)
func (r *IONOSCloudClusterReconciler) reconcileLoadBalancer(ctx *context.ClusterContext) (*reconcile.Result, error) {
ctx.Logger.Info("Reconciling LoadBalancer")
lbSpec := &ctx.IONOSCloudCluster.Spec.LoadBalancer
listenerLan := ctx.IONOSCloudCluster.Lan(lbSpec.ListenerLanRef.Name)
targetLan := ctx.IONOSCloudCluster.Lan(lbSpec.TargetLanRef.Name)

if err != nil && resp.StatusCode != http.StatusNotFound {
return &reconcile.Result{}, errors.New("error getting internet Lan")
if listenerLan == nil {
return &reconcile.Result{RequeueAfter: defaultRetryIntervalOnBusy}, errors.New(fmt.Sprintf("listener lb %s Lan not available (yet)", lbSpec.ListenerLanRef.Name))
}

if resp.StatusCode == http.StatusNotFound || *lan.Metadata.State == STATE_BUSY {
return &reconcile.Result{RequeueAfter: defaultRetryIntervalOnBusy}, errors.New("internet Lan not available (yet)")
if targetLan == nil {
return &reconcile.Result{RequeueAfter: defaultRetryIntervalOnBusy}, errors.New(fmt.Sprintf("target lb %s Lan not available (yet)", lbSpec.TargetLanRef.Name))
}

conditions.MarkTrue(ctx.IONOSCloudCluster, v1alpha1.InternetLanCreatedCondition)

return nil, nil
}

func (r *IONOSCloudClusterReconciler) reconcileLoadBalancer(ctx *context.ClusterContext) (*reconcile.Result, error) {
ctx.Logger.Info("Reconciling LoadBalancer")
if ctx.IONOSCloudCluster.Spec.LoadBalancerID == "" {
if lbSpec.ID == "" {
loadBalancerName := fmt.Sprintf("lb-%s", ctx.Cluster.Name)
loadBalancer := ionoscloud.NetworkLoadBalancer{
Entities: &ionoscloud.NetworkLoadBalancerEntities{
Expand All @@ -330,31 +262,21 @@ func (r *IONOSCloudClusterReconciler) reconcileLoadBalancer(ctx *context.Cluster
},
},
Properties: &ionoscloud.NetworkLoadBalancerProperties{
ListenerLan: ctx.IONOSCloudCluster.Spec.PublicLanID,
ListenerLan: listenerLan.LanID,
Name: &loadBalancerName,
TargetLan: ctx.IONOSCloudCluster.Spec.PrivateLanID,
TargetLan: targetLan.LanID,
Ips: &[]string{ctx.IONOSCloudCluster.Spec.ControlPlaneEndpoint.Host},
},
}
loadBalancer, _, err := ctx.IONOSClient.CreateLoadBalancer(ctx, ctx.IONOSCloudCluster.Spec.DataCenterID, loadBalancer)
if err != nil {
return &reconcile.Result{}, err
}
ctx.IONOSCloudCluster.Spec.LoadBalancerID = *loadBalancer.Id
}

ctx.IONOSCloudCluster.Spec.LoadBalancer = &v1alpha1.IONOSLoadBalancerSpec{
ID: ctx.IONOSCloudCluster.Spec.LoadBalancerID,
ListenerLanRef: v1alpha1.IONOSLanRefSpec{
Name: "public",
},
TargetLanRef: v1alpha1.IONOSLanRefSpec{
Name: "private",
},
lbSpec.ID = *loadBalancer.Id
}

// check status
loadBalancer, resp, err := ctx.IONOSClient.GetLoadBalancer(ctx, ctx.IONOSCloudCluster.Spec.DataCenterID, ctx.IONOSCloudCluster.Spec.LoadBalancerID)
loadBalancer, resp, err := ctx.IONOSClient.GetLoadBalancer(ctx, ctx.IONOSCloudCluster.Spec.DataCenterID, lbSpec.ID)
if err != nil && resp.StatusCode != http.StatusNotFound {
return &reconcile.Result{}, errors.Wrap(err, "error getting loadbalancer")
}
Expand Down
22 changes: 22 additions & 0 deletions internal/controller/ionoscloudcluster_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,28 @@ var _ = Describe("IONOSCloudCluster controller", func() {
Port: 6443,
},
IdentityName: CAPICClusterIdentityName,
LoadBalancer: v1alpha1.IONOSLoadBalancerSpec{
ListenerLanRef: v1alpha1.IONOSLanRefSpec{
Name: "public",
},
TargetLanRef: v1alpha1.IONOSLanRefSpec{
Name: "private",
},
},
Lans: []v1alpha1.IONOSLanSpec{
{
Name: "public",
Public: true,
},
{
Name: "private",
Public: false,
},
{
Name: "internet",
Public: true,
},
},
},
}
identitySecret = &v1.Secret{
Expand Down
Loading

0 comments on commit 4b5e1f1

Please sign in to comment.