Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 98 additions & 78 deletions internal/controller/metalstackcluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ import (
metalgo "github.com/metal-stack/metal-go"
)

var (
errProviderIPNotFound = errors.New("provider ip not found")
errProviderIPTooManyFound = errors.New("multiple provider ips found")
)

// MetalStackClusterReconciler reconciles a MetalStackCluster object
type MetalStackClusterReconciler struct {
MetalClient metalgo.Client
Expand Down Expand Up @@ -235,29 +240,21 @@ func (r *clusterReconciler) delete() error {
return fmt.Errorf("unable to delete firewall deployment: %w", err)
}

r.log.Info("deleted firewall deployment")

err = r.deleteControlPlaneIP()
if err != nil {
return fmt.Errorf("unable to delete control plane ip: %w", err)
}

r.log.Info("deleted control plane ip")

err = r.deleteNodeNetwork()
if err != nil {
return fmt.Errorf("unable to delete node network: %w", err)
}

r.log.Info("deleted node network")

err = r.deleteSshKeyPair(r.ctx)
if err != nil {
return fmt.Errorf("unable to delete ssh key pair: %w", err)
}

r.log.Info("deleted ssh key pair")

return err
}

Expand Down Expand Up @@ -325,6 +322,8 @@ func (r *clusterReconciler) deleteSshKeyPair(ctx context.Context) error {
return err
}

r.log.Info("deleted ssh key pair")

return nil
}

Expand Down Expand Up @@ -362,6 +361,11 @@ func (r *clusterReconciler) ensureNodeNetwork() (string, error) {
}

func (r *clusterReconciler) deleteNodeNetwork() error {
if r.infraCluster.Spec.NodeNetworkID != nil {
r.log.Info("skip deletion of node network")
return nil
}

nws, err := r.findNodeNetwork()
if err != nil {
return err
Expand All @@ -381,6 +385,7 @@ func (r *clusterReconciler) deleteNodeNetwork() error {
if err != nil {
return err
}
r.log.Info("deleted node network")

return nil
default:
Expand Down Expand Up @@ -411,84 +416,90 @@ func (r *clusterReconciler) findNodeNetwork() ([]*models.V1NetworkResponse, erro
}

func (r *clusterReconciler) ensureControlPlaneIP() (*models.V1IPResponse, error) {
ips, err := r.findControlPlaneIP()
if err != nil {
ip, err := r.findControlPlaneIP()
if ip != nil {
return ip, nil
}
if errors.Is(err, errProviderIPTooManyFound) {
return nil, fmt.Errorf("more than a single control plane ip exists for this cluster, operator investigation is required")
}
if err != nil && !errors.Is(err, errProviderIPNotFound) {
return nil, err
}

switch len(ips) {
case 0:
nwResp, err := r.metalClient.Network().FindNetworks(network.NewFindNetworksParams().WithBody(&models.V1NetworkFindRequest{
Labels: map[string]string{
tag.NetworkDefault: "",
},
}).WithContext(r.ctx), nil)
if err != nil {
return nil, fmt.Errorf("error finding default network: %w", err)
}

if len(nwResp.Payload) != 1 {
return nil, fmt.Errorf("no distinct default network configured in the metal-api")
}
nwResp, err := r.metalClient.Network().FindNetworks(network.NewFindNetworksParams().WithBody(&models.V1NetworkFindRequest{
Labels: map[string]string{
tag.NetworkDefault: "",
},
}).WithContext(r.ctx), nil)
if err != nil {
return nil, fmt.Errorf("error finding default network: %w", err)
}

resp, err := r.metalClient.IP().AllocateIP(ipmodels.NewAllocateIPParams().WithBody(&models.V1IPAllocateRequest{
Description: fmt.Sprintf("%s/%s control plane ip", r.infraCluster.GetNamespace(), r.infraCluster.GetName()),
Name: r.infraCluster.GetName() + "-control-plane",
Networkid: nwResp.Payload[0].ID,
Projectid: &r.infraCluster.Spec.ProjectID,
Tags: []string{
tag.New(tag.ClusterID, string(r.infraCluster.GetUID())),
v1alpha1.TagControlPlanePurpose,
},
Type: ptr.To(models.V1IPBaseTypeStatic),
}).WithContext(r.ctx), nil)
if err != nil {
return nil, fmt.Errorf("error creating ip: %w", err)
}
if len(nwResp.Payload) != 1 {
return nil, fmt.Errorf("no distinct default network configured in the metal-api")
}

return resp.Payload, nil
case 1:
return ips[0], nil
default:
return nil, fmt.Errorf("more than a single control plane ip exists for this cluster, operator investigation is required")
resp, err := r.metalClient.IP().AllocateIP(ipmodels.NewAllocateIPParams().WithBody(&models.V1IPAllocateRequest{
Description: fmt.Sprintf("%s/%s control plane ip", r.infraCluster.GetNamespace(), r.infraCluster.GetName()),
Name: r.infraCluster.GetName() + "-control-plane",
Networkid: nwResp.Payload[0].ID,
Projectid: &r.infraCluster.Spec.ProjectID,
Tags: []string{
tag.New(tag.ClusterID, string(r.infraCluster.GetUID())),
v1alpha1.TagControlPlanePurpose,
},
Type: ptr.To(models.V1IPBaseTypeEphemeral),
}).WithContext(r.ctx), nil)
if err != nil {
return nil, fmt.Errorf("error creating ip: %w", err)
}

return resp.Payload, nil
}

func (r *clusterReconciler) deleteControlPlaneIP() error {
ips, err := r.findControlPlaneIP()
if r.infraCluster.Spec.ControlPlaneIP != nil {
r.log.Info("skip deletion of provided control plane ip")
return nil
}
ip, err := r.findControlPlaneIP()
if err != nil && errors.Is(err, errProviderIPNotFound) {
return nil
}
if err != nil {
return err
return fmt.Errorf("unable to delete control plane ip: %w", err)
}

switch len(ips) {
case 0:
if ip.Type != nil && *ip.Type == models.V1IPBaseTypeStatic {
r.log.Info("skip deletion of static control plane ip")
return nil
case 1:
ip := ips[0]

if ip.Ipaddress == nil {
return fmt.Errorf("control plane ip address not set")
}
}

_, err := r.metalClient.IP().FreeIP(ipmodels.NewFreeIPParams().WithID(*ip.Ipaddress).WithContext(r.ctx), nil)
if err != nil {
return err
}
if ip.Ipaddress == nil {
return fmt.Errorf("control plane ip address not set")
}

if ip.Type != nil && *ip.Type == models.V1IPAllocateRequestTypeStatic {
r.log.Info("skipping deletion of static control plane ip", "ip", *ip.Ipaddress)
return nil
default:
return fmt.Errorf("more than a single control plane ip exists for this cluster, operator investigation is required")
}
_, err = r.metalClient.IP().FreeIP(ipmodels.NewFreeIPParams().WithID(*ip.Ipaddress).WithContext(r.ctx), nil)
if err != nil {
return err
}
r.log.Info("deleted control plane ip", "address", *ip.Ipaddress)

return nil
}

func (r *clusterReconciler) findControlPlaneIP() ([]*models.V1IPResponse, error) {
func (r *clusterReconciler) findControlPlaneIP() (*models.V1IPResponse, error) {
if r.infraCluster.Spec.ControlPlaneIP != nil {
resp, err := r.metalClient.IP().FindIP(ipmodels.NewFindIPParams().WithID(*r.infraCluster.Spec.ControlPlaneIP).WithContext(r.ctx), nil)
if err != nil {
return nil, err
}

return []*models.V1IPResponse{resp.Payload}, nil
return resp.Payload, nil
}

resp, err := r.metalClient.IP().FindIPs(ipmodels.NewFindIPsParams().WithBody(&models.V1IPFindRequest{
Expand All @@ -502,7 +513,14 @@ func (r *clusterReconciler) findControlPlaneIP() ([]*models.V1IPResponse, error)
return nil, err
}

return resp.Payload, nil
switch len(resp.Payload) {
case 0:
return nil, errProviderIPNotFound
case 1:
return resp.Payload[0], nil
default:
return nil, errProviderIPTooManyFound
}
}

func (r *clusterReconciler) ensureFirewallDeployment(nodeNetworkID, sshPubKey string) (*fcmv2.FirewallDeployment, error) {
Expand Down Expand Up @@ -649,6 +667,8 @@ func (r *clusterReconciler) deleteFirewallDeployment() error {
return fmt.Errorf("error deleting firewall deployment: %w", err)
}

r.log.Info("deleting firewall deployment")

return errors.New("firewall deployment was deleted, process is still ongoing")
}

Expand Down Expand Up @@ -702,30 +722,30 @@ func (r *clusterReconciler) status() error {
})

g.Go(func() error {
ips, err := r.findControlPlaneIP()
ip, err := r.findControlPlaneIP()

conditionUpdates <- func() {
if err != nil {
conditions.MarkFalse(r.infraCluster, v1alpha1.ClusterControlPlaneEndpointEnsured, "InternalError", clusterv1.ConditionSeverityError, "%s", err.Error())
return
}

switch len(ips) {
case 0:
if errors.Is(err, errProviderIPNotFound) {
if r.infraCluster.Spec.ControlPlaneEndpoint.Host != "" {
conditions.MarkTrue(r.infraCluster, v1alpha1.ClusterControlPlaneEndpointEnsured)
} else {
conditions.MarkFalse(r.infraCluster, v1alpha1.ClusterControlPlaneEndpointEnsured, "NotCreated", clusterv1.ConditionSeverityError, "control plane ip was not yet created")
}
case 1:
if r.infraCluster.Spec.ControlPlaneEndpoint.Host == *ips[0].Ipaddress {
conditions.MarkTrue(r.infraCluster, v1alpha1.ClusterControlPlaneEndpointEnsured)
} else {
conditions.MarkFalse(r.infraCluster, v1alpha1.ClusterControlPlaneEndpointEnsured, "NotSet", clusterv1.ConditionSeverityWarning, "control plane ip was not yet patched into the cluster's spec")
}
default:

}
if errors.Is(err, errProviderIPTooManyFound) {
conditions.MarkFalse(r.infraCluster, v1alpha1.ClusterControlPlaneEndpointEnsured, "InternalError", clusterv1.ConditionSeverityError, "more than a single control plane ip exists for this cluster, operator investigation is required")
}
if err != nil {
conditions.MarkFalse(r.infraCluster, v1alpha1.ClusterControlPlaneEndpointEnsured, "InternalError", clusterv1.ConditionSeverityError, "%s", err.Error())
return
}

if r.infraCluster.Spec.ControlPlaneEndpoint.Host == *ip.Ipaddress {
conditions.MarkTrue(r.infraCluster, v1alpha1.ClusterControlPlaneEndpointEnsured)
} else {
conditions.MarkFalse(r.infraCluster, v1alpha1.ClusterControlPlaneEndpointEnsured, "NotSet", clusterv1.ConditionSeverityWarning, "control plane ip was not yet patched into the cluster's spec")
}
}

return err
Expand Down
3 changes: 2 additions & 1 deletion internal/controller/metalstackcluster_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ var _ = Describe("MetalStackCluster Controller", func() {
Description: resource.Namespace + "/" + resource.Name + " control plane ip",
Networkid: ptr.To("internet"),
Projectid: ptr.To("test-project"),
Type: ptr.To("static"),
Type: ptr.To("ephemeral"),
})), nil).Run(func(args mock.Arguments) {
findIPResponse.Payload = []*models.V1IPResponse{
{
Expand Down Expand Up @@ -342,6 +342,7 @@ var _ = Describe("MetalStackCluster Controller", func() {
"cluster.metal-stack.io/id=" + string(resource.UID),
"metal-stack.infrastructure.cluster.x-k8s.io/purpose=control-plane",
},
Type: ptr.To(models.V1IPBaseTypeStatic),
},
}, nil)
},
Expand Down
Loading