sigs.k8s.io/cluster-api@v1.7.1/internal/controllers/machinedeployment/suite_test.go (about) 1 /* 2 Copyright 2022 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 machinedeployment 18 19 import ( 20 "context" 21 "fmt" 22 "os" 23 "testing" 24 "time" 25 26 . "github.com/onsi/gomega" 27 corev1 "k8s.io/api/core/v1" 28 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 31 "k8s.io/apimachinery/pkg/runtime" 32 "k8s.io/apimachinery/pkg/util/intstr" 33 "k8s.io/apimachinery/pkg/util/uuid" 34 clientgoscheme "k8s.io/client-go/kubernetes/scheme" 35 "k8s.io/utils/ptr" 36 ctrl "sigs.k8s.io/controller-runtime" 37 "sigs.k8s.io/controller-runtime/pkg/client" 38 "sigs.k8s.io/controller-runtime/pkg/controller" 39 40 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 41 "sigs.k8s.io/cluster-api/api/v1beta1/index" 42 "sigs.k8s.io/cluster-api/controllers/remote" 43 machinecontroller "sigs.k8s.io/cluster-api/internal/controllers/machine" 44 machinesetcontroller "sigs.k8s.io/cluster-api/internal/controllers/machineset" 45 "sigs.k8s.io/cluster-api/internal/test/envtest" 46 ) 47 48 const ( 49 timeout = time.Second * 30 50 ) 51 52 var ( 53 env *envtest.Environment 54 ctx = ctrl.SetupSignalHandler() 55 fakeScheme = runtime.NewScheme() 56 ) 57 58 func init() { 59 _ = clientgoscheme.AddToScheme(fakeScheme) 60 _ = clusterv1.AddToScheme(fakeScheme) 61 _ = apiextensionsv1.AddToScheme(fakeScheme) 62 } 63 64 func TestMain(m *testing.M) { 65 setupIndexes := func(ctx context.Context, mgr ctrl.Manager) { 66 if err := index.AddDefaultIndexes(ctx, mgr); err != nil { 67 panic(fmt.Sprintf("unable to setup index: %v", err)) 68 } 69 } 70 71 setupReconcilers := func(ctx context.Context, mgr ctrl.Manager) { 72 // Set up a ClusterCacheTracker and ClusterCacheReconciler to provide to controllers 73 // requiring a connection to a remote cluster 74 tracker, err := remote.NewClusterCacheTracker( 75 mgr, 76 remote.ClusterCacheTrackerOptions{ 77 Log: &ctrl.Log, 78 Indexes: []remote.Index{remote.NodeProviderIDIndex}, 79 }, 80 ) 81 if err != nil { 82 panic(fmt.Sprintf("unable to create cluster cache tracker: %v", err)) 83 } 84 if err := (&remote.ClusterCacheReconciler{ 85 Client: mgr.GetClient(), 86 Tracker: tracker, 87 }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: 1}); err != nil { 88 panic(fmt.Sprintf("Failed to start ClusterCacheReconciler: %v", err)) 89 } 90 91 unstructuredCachingClient, err := client.New(mgr.GetConfig(), client.Options{ 92 HTTPClient: mgr.GetHTTPClient(), 93 Cache: &client.CacheOptions{ 94 Reader: mgr.GetCache(), 95 Unstructured: true, 96 }, 97 }) 98 if err != nil { 99 panic(fmt.Sprintf("unable to create unstructuredCachineClient: %v", err)) 100 } 101 102 if err := (&machinecontroller.Reconciler{ 103 Client: mgr.GetClient(), 104 UnstructuredCachingClient: unstructuredCachingClient, 105 APIReader: mgr.GetAPIReader(), 106 Tracker: tracker, 107 }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: 1}); err != nil { 108 panic(fmt.Sprintf("Failed to start MachineReconciler: %v", err)) 109 } 110 if err := (&machinesetcontroller.Reconciler{ 111 Client: mgr.GetClient(), 112 UnstructuredCachingClient: unstructuredCachingClient, 113 APIReader: mgr.GetAPIReader(), 114 Tracker: tracker, 115 }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: 1}); err != nil { 116 panic(fmt.Sprintf("Failed to start MachineSetReconciler: %v", err)) 117 } 118 if err := (&Reconciler{ 119 Client: mgr.GetClient(), 120 UnstructuredCachingClient: unstructuredCachingClient, 121 APIReader: mgr.GetAPIReader(), 122 }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: 1}); err != nil { 123 panic(fmt.Sprintf("Failed to start MachineDeploymentReconciler: %v", err)) 124 } 125 } 126 127 SetDefaultEventuallyPollingInterval(100 * time.Millisecond) 128 SetDefaultEventuallyTimeout(timeout) 129 130 os.Exit(envtest.Run(ctx, envtest.RunInput{ 131 M: m, 132 SetupEnv: func(e *envtest.Environment) { env = e }, 133 SetupIndexes: setupIndexes, 134 SetupReconcilers: setupReconcilers, 135 })) 136 } 137 138 func intOrStrPtr(i int32) *intstr.IntOrString { 139 // FromInt takes an int that must not be greater than int32... 140 res := intstr.FromInt(int(i)) 141 return &res 142 } 143 144 func fakeInfrastructureRefReady(ref corev1.ObjectReference, base map[string]interface{}, g *WithT) string { 145 iref := (&unstructured.Unstructured{Object: base}).DeepCopy() 146 g.Eventually(func() error { 147 return env.Get(ctx, client.ObjectKey{Name: ref.Name, Namespace: ref.Namespace}, iref) 148 }).Should(Succeed()) 149 150 irefPatch := client.MergeFrom(iref.DeepCopy()) 151 providerID := fmt.Sprintf("test:////%v", uuid.NewUUID()) 152 g.Expect(unstructured.SetNestedField(iref.Object, providerID, "spec", "providerID")).To(Succeed()) 153 g.Expect(env.Patch(ctx, iref, irefPatch)).To(Succeed()) 154 155 irefPatch = client.MergeFrom(iref.DeepCopy()) 156 g.Expect(unstructured.SetNestedField(iref.Object, true, "status", "ready")).To(Succeed()) 157 g.Expect(env.Status().Patch(ctx, iref, irefPatch)).To(Succeed()) 158 return providerID 159 } 160 161 func fakeMachineNodeRef(m *clusterv1.Machine, pid string, g *WithT) { 162 g.Eventually(func() error { 163 key := client.ObjectKey{Name: m.Name, Namespace: m.Namespace} 164 return env.Get(ctx, key, &clusterv1.Machine{}) 165 }).Should(Succeed()) 166 167 if m.Status.NodeRef != nil { 168 return 169 } 170 171 // Create a new fake Node. 172 node := &corev1.Node{ 173 ObjectMeta: metav1.ObjectMeta{ 174 GenerateName: m.Name + "-", 175 }, 176 Spec: corev1.NodeSpec{ 177 ProviderID: pid, 178 }, 179 } 180 g.Expect(env.Create(ctx, node)).To(Succeed()) 181 182 g.Eventually(func() error { 183 key := client.ObjectKey{Name: node.Name, Namespace: node.Namespace} 184 return env.Get(ctx, key, &corev1.Node{}) 185 }).Should(Succeed()) 186 187 // Patch the node and make it look like ready. 188 patchNode := client.MergeFrom(node.DeepCopy()) 189 node.Status.Conditions = append(node.Status.Conditions, corev1.NodeCondition{Type: corev1.NodeReady, Status: corev1.ConditionTrue}) 190 g.Expect(env.Status().Patch(ctx, node, patchNode)).To(Succeed()) 191 192 // Patch the Machine. 193 patchMachine := client.MergeFrom(m.DeepCopy()) 194 m.Spec.ProviderID = ptr.To(pid) 195 g.Expect(env.Patch(ctx, m, patchMachine)).To(Succeed()) 196 197 patchMachine = client.MergeFrom(m.DeepCopy()) 198 m.Status.NodeRef = &corev1.ObjectReference{ 199 APIVersion: corev1.SchemeGroupVersion.String(), 200 Kind: "Node", 201 Name: node.Name, 202 } 203 g.Expect(env.Status().Patch(ctx, m, patchMachine)).To(Succeed()) 204 }