sigs.k8s.io/cluster-api@v1.7.1/controlplane/kubeadm/internal/control_plane_test.go (about)

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package internal
    18  
    19  import (
    20  	"testing"
    21  
    22  	. "github.com/onsi/gomega"
    23  	corev1 "k8s.io/api/core/v1"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  
    26  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    27  	controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
    28  	"sigs.k8s.io/cluster-api/util/collections"
    29  	"sigs.k8s.io/cluster-api/util/conditions"
    30  )
    31  
    32  func TestControlPlane(t *testing.T) {
    33  	g := NewWithT(t)
    34  
    35  	t.Run("Failure domains", func(t *testing.T) {
    36  		controlPlane := &ControlPlane{
    37  			KCP: &controlplanev1.KubeadmControlPlane{},
    38  			Cluster: &clusterv1.Cluster{
    39  				Status: clusterv1.ClusterStatus{
    40  					FailureDomains: clusterv1.FailureDomains{
    41  						"one":   failureDomain(true),
    42  						"two":   failureDomain(true),
    43  						"three": failureDomain(true),
    44  						"four":  failureDomain(false),
    45  					},
    46  				},
    47  			},
    48  			Machines: collections.Machines{
    49  				"machine-1": machine("machine-1", withFailureDomain("one")),
    50  				"machine-2": machine("machine-2", withFailureDomain("two")),
    51  				"machine-3": machine("machine-3", withFailureDomain("two")),
    52  			},
    53  		}
    54  
    55  		t.Run("With all machines in known failure domain, should return the FD with most number of machines", func(*testing.T) {
    56  			g.Expect(*controlPlane.FailureDomainWithMostMachines(ctx, controlPlane.Machines)).To(Equal("two"))
    57  		})
    58  
    59  		t.Run("With some machines in non defined failure domains", func(*testing.T) {
    60  			controlPlane.Machines.Insert(machine("machine-5", withFailureDomain("unknown")))
    61  			g.Expect(*controlPlane.FailureDomainWithMostMachines(ctx, controlPlane.Machines)).To(Equal("unknown"))
    62  		})
    63  	})
    64  }
    65  
    66  func TestHasUnhealthyMachine(t *testing.T) {
    67  	// healthy machine (without MachineHealthCheckSucceded condition)
    68  	healthyMachine1 := &clusterv1.Machine{ObjectMeta: metav1.ObjectMeta{Name: "healthyMachine1"}}
    69  	// healthy machine (with MachineHealthCheckSucceded == true)
    70  	healthyMachine2 := &clusterv1.Machine{ObjectMeta: metav1.ObjectMeta{Name: "healthyMachine2"}}
    71  	conditions.MarkTrue(healthyMachine2, clusterv1.MachineHealthCheckSucceededCondition)
    72  	// unhealthy machine NOT eligible for KCP remediation (with MachineHealthCheckSucceded == False, but without MachineOwnerRemediated condition)
    73  	unhealthyMachineNOTOwnerRemediated := &clusterv1.Machine{ObjectMeta: metav1.ObjectMeta{Name: "unhealthyMachineNOTOwnerRemediated"}}
    74  	conditions.MarkFalse(unhealthyMachineNOTOwnerRemediated, clusterv1.MachineHealthCheckSucceededCondition, clusterv1.MachineHasFailureReason, clusterv1.ConditionSeverityWarning, "Something is wrong")
    75  	// unhealthy machine eligible for KCP remediation (with MachineHealthCheckSucceded == False, with MachineOwnerRemediated condition)
    76  	unhealthyMachineOwnerRemediated := &clusterv1.Machine{ObjectMeta: metav1.ObjectMeta{Name: "unhealthyMachineOwnerRemediated"}}
    77  	conditions.MarkFalse(unhealthyMachineOwnerRemediated, clusterv1.MachineHealthCheckSucceededCondition, clusterv1.MachineHasFailureReason, clusterv1.ConditionSeverityWarning, "Something is wrong")
    78  	conditions.MarkFalse(unhealthyMachineOwnerRemediated, clusterv1.MachineOwnerRemediatedCondition, clusterv1.WaitingForRemediationReason, clusterv1.ConditionSeverityWarning, "KCP should remediate this issue")
    79  
    80  	t.Run("One unhealthy machine to be remediated by KCP", func(t *testing.T) {
    81  		c := ControlPlane{
    82  			Machines: collections.FromMachines(
    83  				healthyMachine1,                    // healthy machine, should be ignored
    84  				healthyMachine2,                    // healthy machine, should be ignored (the MachineHealthCheckSucceededCondition is true)
    85  				unhealthyMachineNOTOwnerRemediated, // unhealthy machine, but KCP should not remediate it, should be ignored.
    86  				unhealthyMachineOwnerRemediated,
    87  			),
    88  		}
    89  
    90  		g := NewWithT(t)
    91  		g.Expect(c.HasUnhealthyMachineByMachineHealthCheck()).To(BeTrue())
    92  	})
    93  
    94  	t.Run("No unhealthy machine to be remediated by KCP", func(t *testing.T) {
    95  		c := ControlPlane{
    96  			Machines: collections.FromMachines(
    97  				healthyMachine1,                    // healthy machine, should be ignored
    98  				healthyMachine2,                    // healthy machine, should be ignored (the MachineHealthCheckSucceededCondition is true)
    99  				unhealthyMachineNOTOwnerRemediated, // unhealthy machine, but KCP should not remediate it, should be ignored.
   100  			),
   101  		}
   102  
   103  		g := NewWithT(t)
   104  		g.Expect(c.HasUnhealthyMachineByMachineHealthCheck()).To(BeFalse())
   105  	})
   106  }
   107  
   108  func TestHasHealthyMachineStillProvisioning(t *testing.T) {
   109  	// healthy machine (without MachineHealthCheckSucceded condition) still provisioning (without NodeRef)
   110  	healthyMachineStillProvisioning1 := &clusterv1.Machine{ObjectMeta: metav1.ObjectMeta{Name: "healthyMachineStillProvisioning1"}}
   111  
   112  	// healthy machine (without MachineHealthCheckSucceded condition) provisioned (with NodeRef)
   113  	healthyMachineProvisioned1 := &clusterv1.Machine{ObjectMeta: metav1.ObjectMeta{Name: "healthyMachineProvisioned1"}}
   114  	healthyMachineProvisioned1.Status.NodeRef = &corev1.ObjectReference{}
   115  
   116  	// unhealthy machine (with MachineHealthCheckSucceded condition) still provisioning (without NodeRef)
   117  	unhealthyMachineStillProvisioning1 := &clusterv1.Machine{ObjectMeta: metav1.ObjectMeta{Name: "unhealthyMachineStillProvisioning1"}}
   118  	conditions.MarkFalse(unhealthyMachineStillProvisioning1, clusterv1.MachineHealthCheckSucceededCondition, clusterv1.MachineHasFailureReason, clusterv1.ConditionSeverityWarning, "Something is wrong")
   119  	conditions.MarkFalse(unhealthyMachineStillProvisioning1, clusterv1.MachineOwnerRemediatedCondition, clusterv1.WaitingForRemediationReason, clusterv1.ConditionSeverityWarning, "KCP should remediate this issue")
   120  
   121  	// unhealthy machine (with MachineHealthCheckSucceded condition) provisioned (with NodeRef)
   122  	unhealthyMachineProvisioned1 := &clusterv1.Machine{ObjectMeta: metav1.ObjectMeta{Name: "unhealthyMachineProvisioned1"}}
   123  	unhealthyMachineProvisioned1.Status.NodeRef = &corev1.ObjectReference{}
   124  	conditions.MarkFalse(unhealthyMachineProvisioned1, clusterv1.MachineHealthCheckSucceededCondition, clusterv1.MachineHasFailureReason, clusterv1.ConditionSeverityWarning, "Something is wrong")
   125  	conditions.MarkFalse(unhealthyMachineProvisioned1, clusterv1.MachineOwnerRemediatedCondition, clusterv1.WaitingForRemediationReason, clusterv1.ConditionSeverityWarning, "KCP should remediate this issue")
   126  
   127  	t.Run("Healthy machine still provisioning", func(t *testing.T) {
   128  		c := ControlPlane{
   129  			Machines: collections.FromMachines(
   130  				healthyMachineStillProvisioning1,
   131  				unhealthyMachineStillProvisioning1, // unhealthy, should be ignored
   132  				healthyMachineProvisioned1,         // already provisioned, should be ignored
   133  				unhealthyMachineProvisioned1,       // unhealthy and already provisioned, should be ignored
   134  			),
   135  		}
   136  
   137  		g := NewWithT(t)
   138  		g.Expect(c.HasHealthyMachineStillProvisioning()).To(BeTrue())
   139  	})
   140  	t.Run("No machines still provisioning", func(t *testing.T) {
   141  		c := ControlPlane{
   142  			Machines: collections.FromMachines(
   143  				unhealthyMachineStillProvisioning1, // unhealthy, should be ignored
   144  				healthyMachineProvisioned1,         // already provisioned, should be ignored
   145  				unhealthyMachineProvisioned1,       // unhealthy and already provisioned, should be ignored
   146  			),
   147  		}
   148  
   149  		g := NewWithT(t)
   150  		g.Expect(c.HasHealthyMachineStillProvisioning()).To(BeFalse())
   151  	})
   152  }
   153  
   154  type machineOpt func(*clusterv1.Machine)
   155  
   156  func failureDomain(controlPlane bool) clusterv1.FailureDomainSpec {
   157  	return clusterv1.FailureDomainSpec{
   158  		ControlPlane: controlPlane,
   159  	}
   160  }
   161  
   162  func withFailureDomain(fd string) machineOpt {
   163  	return func(m *clusterv1.Machine) {
   164  		m.Spec.FailureDomain = &fd
   165  	}
   166  }
   167  
   168  func machine(name string, opts ...machineOpt) *clusterv1.Machine {
   169  	m := &clusterv1.Machine{
   170  		ObjectMeta: metav1.ObjectMeta{
   171  			Name: name,
   172  		},
   173  	}
   174  	for _, opt := range opts {
   175  		opt(m)
   176  	}
   177  	return m
   178  }