sigs.k8s.io/cluster-api@v1.7.1/exp/runtime/internal/controllers/warmup_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 controllers
    18  
    19  import (
    20  	"fmt"
    21  	"testing"
    22  	"time"
    23  
    24  	. "github.com/onsi/gomega"
    25  	"github.com/pkg/errors"
    26  	corev1 "k8s.io/api/core/v1"
    27  	"k8s.io/apiserver/pkg/admission/plugin/webhook/testcerts"
    28  	utilfeature "k8s.io/component-base/featuregate/testing"
    29  
    30  	runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1"
    31  	runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog"
    32  	runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
    33  	"sigs.k8s.io/cluster-api/feature"
    34  	runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client"
    35  	runtimeregistry "sigs.k8s.io/cluster-api/internal/runtime/registry"
    36  	fakev1alpha1 "sigs.k8s.io/cluster-api/internal/runtime/test/v1alpha1"
    37  )
    38  
    39  func Test_warmupRunnable_Start(t *testing.T) {
    40  	g := NewWithT(t)
    41  	defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.ClusterTopology, true)()
    42  	defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.RuntimeSDK, true)()
    43  
    44  	t.Run("succeed to warm up registry on Start", func(t *testing.T) {
    45  		ns, err := env.CreateNamespace(ctx, "test-runtime-extension")
    46  		g.Expect(err).ToNot(HaveOccurred())
    47  
    48  		caCertSecret := fakeCASecret(ns.Name, "ext1-webhook", testcerts.CACert)
    49  		// Create the secret which contains the fake ca certificate.
    50  		g.Expect(env.CreateAndWait(ctx, caCertSecret)).To(Succeed())
    51  		defer func() {
    52  			g.Expect(env.CleanupAndWait(ctx, caCertSecret)).To(Succeed())
    53  		}()
    54  
    55  		cat := runtimecatalog.New()
    56  		g.Expect(fakev1alpha1.AddToCatalog(cat)).To(Succeed())
    57  
    58  		registry := runtimeregistry.New()
    59  		g.Expect(runtimehooksv1.AddToCatalog(cat)).To(Succeed())
    60  
    61  		for _, name := range []string{"ext1", "ext2", "ext3"} {
    62  			server, err := fakeSecureExtensionServer(discoveryHandler("first", "second", "third"))
    63  			g.Expect(err).ToNot(HaveOccurred())
    64  			defer server.Close()
    65  			extensionConfig := fakeExtensionConfigForURL(ns.Name, name, server.URL)
    66  			extensionConfig.Annotations[runtimev1.InjectCAFromSecretAnnotation] = caCertSecret.GetNamespace() + "/" + caCertSecret.GetName()
    67  
    68  			// Create the ExtensionConfig.
    69  			g.Expect(env.CreateAndWait(ctx, extensionConfig)).To(Succeed())
    70  			defer func(namespace, name, url string) {
    71  				g.Expect(env.CleanupAndWait(ctx, fakeExtensionConfigForURL(namespace, name, url))).To(Succeed())
    72  			}(ns.Name, name, server.URL)
    73  		}
    74  
    75  		r := &warmupRunnable{
    76  			Client:    env.GetClient(),
    77  			APIReader: env.GetAPIReader(),
    78  			RuntimeClient: runtimeclient.New(runtimeclient.Options{
    79  				Catalog:  cat,
    80  				Registry: registry,
    81  			}),
    82  		}
    83  
    84  		if err := r.Start(ctx); err != nil {
    85  			t.Error(err)
    86  		}
    87  		list := &runtimev1.ExtensionConfigList{}
    88  		g.Expect(env.GetAPIReader().List(ctx, list)).To(Succeed())
    89  		g.Expect(list.Items).To(HaveLen(3))
    90  		for i, config := range list.Items {
    91  			// Expect three handlers for each extension and expect the name to be the handler name plus the extension name.
    92  			handlers := config.Status.Handlers
    93  			g.Expect(handlers).To(HaveLen(3))
    94  			g.Expect(handlers[0].Name).To(Equal(fmt.Sprintf("first.ext%d", i+1)))
    95  			g.Expect(handlers[1].Name).To(Equal(fmt.Sprintf("second.ext%d", i+1)))
    96  			g.Expect(handlers[2].Name).To(Equal(fmt.Sprintf("third.ext%d", i+1)))
    97  
    98  			conditions := config.GetConditions()
    99  			g.Expect(conditions).To(HaveLen(1))
   100  			g.Expect(conditions[0].Status).To(Equal(corev1.ConditionTrue))
   101  			g.Expect(conditions[0].Type).To(Equal(runtimev1.RuntimeExtensionDiscoveredCondition))
   102  		}
   103  	})
   104  
   105  	t.Run("fail to warm up registry on Start with broken extension", func(t *testing.T) {
   106  		// This test should time out and throw a failure.
   107  		ns, err := env.CreateNamespace(ctx, "test-runtime-extension")
   108  		g.Expect(err).ToNot(HaveOccurred())
   109  
   110  		caCertSecret := fakeCASecret(ns.Name, "ext1-webhook", testcerts.CACert)
   111  		// Create the secret which contains the ca certificate.
   112  		g.Expect(env.CreateAndWait(ctx, caCertSecret)).To(Succeed())
   113  		defer func() {
   114  			g.Expect(env.CleanupAndWait(ctx, caCertSecret)).To(Succeed())
   115  		}()
   116  
   117  		cat := runtimecatalog.New()
   118  		g.Expect(fakev1alpha1.AddToCatalog(cat)).To(Succeed())
   119  		registry := runtimeregistry.New()
   120  		g.Expect(runtimehooksv1.AddToCatalog(cat)).To(Succeed())
   121  
   122  		// Do not create an extension server for an extension with this name, but do create an extensionconfig.
   123  		brokenExtension := "ext2"
   124  		for _, name := range []string{"ext1", "ext2", "ext3"} {
   125  			if name == brokenExtension {
   126  				g.Expect(env.CreateAndWait(ctx, fakeExtensionConfigForURL(ns.Name, name, "https://localhost:1234"))).To(Succeed())
   127  				continue
   128  			}
   129  			server, err := fakeSecureExtensionServer(discoveryHandler("first", "second", "third"))
   130  			g.Expect(err).ToNot(HaveOccurred())
   131  			defer server.Close()
   132  
   133  			extensionConfig := fakeExtensionConfigForURL(ns.Name, name, server.URL)
   134  			extensionConfig.Annotations[runtimev1.InjectCAFromSecretAnnotation] = caCertSecret.GetNamespace() + "/" + caCertSecret.GetName()
   135  
   136  			// Create the ExtensionConfig.
   137  			g.Expect(env.CreateAndWait(ctx, extensionConfig)).To(Succeed())
   138  		}
   139  
   140  		r := &warmupRunnable{
   141  			Client:    env.GetClient(),
   142  			APIReader: env.GetAPIReader(),
   143  			RuntimeClient: runtimeclient.New(runtimeclient.Options{
   144  				Catalog:  cat,
   145  				Registry: registry,
   146  			}),
   147  			warmupInterval: 500 * time.Millisecond,
   148  			warmupTimeout:  5 * time.Second,
   149  		}
   150  
   151  		if err := r.Start(ctx); err == nil {
   152  			t.Error(errors.New("expected error on start up"))
   153  		}
   154  		list := &runtimev1.ExtensionConfigList{}
   155  		g.Expect(env.GetAPIReader().List(ctx, list)).To(Succeed())
   156  		g.Expect(list.Items).To(HaveLen(3))
   157  
   158  		for i, config := range list.Items {
   159  			handlers := config.Status.Handlers
   160  			conditions := config.GetConditions()
   161  
   162  			// Expect no handlers and a failed condition for the broken extension.
   163  			if config.Name == brokenExtension {
   164  				g.Expect(conditions).To(HaveLen(1))
   165  				g.Expect(conditions[0].Status).To(Equal(corev1.ConditionFalse))
   166  				g.Expect(conditions[0].Type).To(Equal(runtimev1.RuntimeExtensionDiscoveredCondition))
   167  				g.Expect(handlers).To(BeEmpty())
   168  
   169  				continue
   170  			}
   171  
   172  			// For other extensions expect handler name plus the extension name, and expect the condition to be True.
   173  			g.Expect(handlers).To(HaveLen(3))
   174  			g.Expect(handlers[0].Name).To(Equal(fmt.Sprintf("first.ext%d", i+1)))
   175  			g.Expect(handlers[1].Name).To(Equal(fmt.Sprintf("second.ext%d", i+1)))
   176  			g.Expect(handlers[2].Name).To(Equal(fmt.Sprintf("third.ext%d", i+1)))
   177  
   178  			g.Expect(conditions).To(HaveLen(1))
   179  			g.Expect(conditions[0].Status).To(Equal(corev1.ConditionTrue))
   180  			g.Expect(conditions[0].Type).To(Equal(runtimev1.RuntimeExtensionDiscoveredCondition))
   181  		}
   182  	})
   183  }