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  }