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