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  }