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 }