@@ -17,6 +17,8 @@ limitations under the License.
17
17
package machine
18
18
19
19
import (
20
+ "context"
21
+ "fmt"
20
22
"testing"
21
23
"time"
22
24
@@ -27,9 +29,11 @@ import (
27
29
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28
30
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
29
31
"k8s.io/client-go/kubernetes/scheme"
32
+ "k8s.io/client-go/tools/record"
30
33
"k8s.io/utils/pointer"
31
34
ctrl "sigs.k8s.io/controller-runtime"
32
35
"sigs.k8s.io/controller-runtime/pkg/client"
36
+ "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
33
37
"sigs.k8s.io/controller-runtime/pkg/client/fake"
34
38
"sigs.k8s.io/controller-runtime/pkg/log"
35
39
"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -1856,6 +1860,193 @@ func TestNodeToMachine(t *testing.T) {
1856
1860
}
1857
1861
}
1858
1862
1863
+ type fakeClientWithNodeDeletionErr struct {
1864
+ client.Client
1865
+ }
1866
+
1867
+ func (fc fakeClientWithNodeDeletionErr ) Delete (ctx context.Context , obj client.Object , opts ... client.DeleteOption ) error {
1868
+ gvk , err := apiutil .GVKForObject (obj , fakeScheme )
1869
+ if err == nil && gvk .Kind == "Node" {
1870
+ return fmt .Errorf ("fake error" )
1871
+ }
1872
+ return fc .Client .Delete (ctx , obj , opts ... )
1873
+ }
1874
+
1875
+ func TestNodeDeletion (t * testing.T ) {
1876
+ g := NewWithT (t )
1877
+
1878
+ deletionTime := metav1 .Now ().Add (- 1 * time .Second )
1879
+
1880
+ testCluster := clusterv1.Cluster {
1881
+ ObjectMeta : metav1.ObjectMeta {
1882
+ Name : "test-cluster" ,
1883
+ Namespace : metav1 .NamespaceDefault ,
1884
+ },
1885
+ }
1886
+
1887
+ node := & corev1.Node {
1888
+ ObjectMeta : metav1.ObjectMeta {
1889
+ Name : "test" ,
1890
+ },
1891
+ Spec : corev1.NodeSpec {ProviderID : "test://id-1" },
1892
+ }
1893
+
1894
+ testMachine := clusterv1.Machine {
1895
+ ObjectMeta : metav1.ObjectMeta {
1896
+ Name : "test" ,
1897
+ Namespace : metav1 .NamespaceDefault ,
1898
+ Labels : map [string ]string {
1899
+ clusterv1 .MachineControlPlaneLabelName : "" ,
1900
+ },
1901
+ Annotations : map [string ]string {
1902
+ "machine.cluster.x-k8s.io/exclude-node-draining" : "" ,
1903
+ },
1904
+ Finalizers : []string {clusterv1 .MachineFinalizer },
1905
+ DeletionTimestamp : & metav1.Time {Time : deletionTime },
1906
+ },
1907
+ Spec : clusterv1.MachineSpec {
1908
+ ClusterName : "test-cluster" ,
1909
+ InfrastructureRef : corev1.ObjectReference {
1910
+ APIVersion : "infrastructure.cluster.x-k8s.io/v1beta1" ,
1911
+ Kind : "GenericInfrastructureMachine" ,
1912
+ Name : "infra-config1" ,
1913
+ },
1914
+ Bootstrap : clusterv1.Bootstrap {DataSecretName : pointer .StringPtr ("data" )},
1915
+ },
1916
+ Status : clusterv1.MachineStatus {
1917
+ NodeRef : & corev1.ObjectReference {
1918
+ Name : "test" ,
1919
+ },
1920
+ },
1921
+ }
1922
+
1923
+ cpmachine1 := & clusterv1.Machine {
1924
+ ObjectMeta : metav1.ObjectMeta {
1925
+ Name : "cp1" ,
1926
+ Namespace : metav1 .NamespaceDefault ,
1927
+ Labels : map [string ]string {
1928
+ clusterv1 .ClusterLabelName : "test-cluster" ,
1929
+ clusterv1 .MachineControlPlaneLabelName : "" ,
1930
+ },
1931
+ Finalizers : []string {clusterv1 .MachineFinalizer },
1932
+ },
1933
+ Spec : clusterv1.MachineSpec {
1934
+ ClusterName : "test-cluster" ,
1935
+ InfrastructureRef : corev1.ObjectReference {},
1936
+ Bootstrap : clusterv1.Bootstrap {DataSecretName : pointer .StringPtr ("data" )},
1937
+ },
1938
+ Status : clusterv1.MachineStatus {
1939
+ NodeRef : & corev1.ObjectReference {
1940
+ Name : "cp1" ,
1941
+ },
1942
+ },
1943
+ }
1944
+
1945
+ testCases := []struct {
1946
+ name string
1947
+ deletionTimeout * metav1.Duration
1948
+ resultErr bool
1949
+ clusterDeleted bool
1950
+ expectNodeDeletion bool
1951
+ createFakeClient func (... client.Object ) client.Client
1952
+ }{
1953
+ {
1954
+ name : "should return no error when deletion is successful" ,
1955
+ deletionTimeout : & metav1.Duration {Duration : time .Second },
1956
+ resultErr : false ,
1957
+ expectNodeDeletion : true ,
1958
+ createFakeClient : func (initObjs ... client.Object ) client.Client {
1959
+ return fake .NewClientBuilder ().
1960
+ WithObjects (initObjs ... ).
1961
+ Build ()
1962
+ },
1963
+ },
1964
+ {
1965
+ name : "should return an error when timeout is not expired and node deletion fails" ,
1966
+ deletionTimeout : & metav1.Duration {Duration : time .Hour },
1967
+ resultErr : true ,
1968
+ expectNodeDeletion : false ,
1969
+ createFakeClient : func (initObjs ... client.Object ) client.Client {
1970
+ fc := fake .NewClientBuilder ().
1971
+ WithObjects (initObjs ... ).
1972
+ Build ()
1973
+ return fakeClientWithNodeDeletionErr {fc }
1974
+ },
1975
+ },
1976
+ {
1977
+ name : "should return an error when timeout is infinite and node deletion fails" ,
1978
+ deletionTimeout : & metav1.Duration {Duration : 0 }, // should lead to infinite timeout
1979
+ resultErr : true ,
1980
+ expectNodeDeletion : false ,
1981
+ createFakeClient : func (initObjs ... client.Object ) client.Client {
1982
+ fc := fake .NewClientBuilder ().
1983
+ WithObjects (initObjs ... ).
1984
+ Build ()
1985
+ return fakeClientWithNodeDeletionErr {fc }
1986
+ },
1987
+ },
1988
+ {
1989
+ name : "should not return an error when timeout is expired and node deletion fails" ,
1990
+ deletionTimeout : & metav1.Duration {Duration : time .Millisecond },
1991
+ resultErr : false ,
1992
+ expectNodeDeletion : false ,
1993
+ createFakeClient : func (initObjs ... client.Object ) client.Client {
1994
+ fc := fake .NewClientBuilder ().
1995
+ WithObjects (initObjs ... ).
1996
+ Build ()
1997
+ return fakeClientWithNodeDeletionErr {fc }
1998
+ },
1999
+ },
2000
+ {
2001
+ name : "should not delete the node or return an error when the cluster is marked for deletion" ,
2002
+ deletionTimeout : nil , // should lead to infinite timeout
2003
+ resultErr : false ,
2004
+ clusterDeleted : true ,
2005
+ expectNodeDeletion : false ,
2006
+ createFakeClient : func (initObjs ... client.Object ) client.Client {
2007
+ fc := fake .NewClientBuilder ().
2008
+ WithObjects (initObjs ... ).
2009
+ Build ()
2010
+ return fakeClientWithNodeDeletionErr {fc }
2011
+ },
2012
+ },
2013
+ }
2014
+
2015
+ for _ , tc := range testCases {
2016
+ t .Run (tc .name , func (t * testing.T ) {
2017
+ m := testMachine .DeepCopy ()
2018
+ m .Spec .NodeDeletionTimeout = tc .deletionTimeout
2019
+
2020
+ fakeClient := tc .createFakeClient (node , m , cpmachine1 )
2021
+ tracker := remote .NewTestClusterCacheTracker (ctrl .Log , fakeClient , fakeScheme , client .ObjectKeyFromObject (& testCluster ))
2022
+
2023
+ r := & Reconciler {
2024
+ Client : fakeClient ,
2025
+ Tracker : tracker ,
2026
+ recorder : record .NewFakeRecorder (10 ),
2027
+ nodeDeletionRetryTimeout : 10 * time .Millisecond ,
2028
+ }
2029
+
2030
+ cluster := testCluster .DeepCopy ()
2031
+ if tc .clusterDeleted {
2032
+ cluster .DeletionTimestamp = & metav1.Time {Time : deletionTime .Add (time .Hour )}
2033
+ }
2034
+
2035
+ _ , err := r .reconcileDelete (context .Background (), cluster , m )
2036
+
2037
+ if tc .resultErr {
2038
+ g .Expect (err ).To (HaveOccurred ())
2039
+ } else {
2040
+ g .Expect (err ).NotTo (HaveOccurred ())
2041
+ if tc .expectNodeDeletion {
2042
+ n := & corev1.Node {}
2043
+ g .Expect (fakeClient .Get (context .Background (), client .ObjectKeyFromObject (node ), n )).NotTo (Succeed ())
2044
+ }
2045
+ }
2046
+ })
2047
+ }
2048
+ }
2049
+
1859
2050
// adds a condition list to an external object.
1860
2051
func addConditionsToExternal (u * unstructured.Unstructured , newConditions clusterv1.Conditions ) {
1861
2052
existingConditions := clusterv1.Conditions {}
0 commit comments