sigs.k8s.io/cluster-api@v1.7.1/controlplane/kubeadm/internal/cluster_test.go (about) 1 /* 2 Copyright 2020 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 internal 18 19 import ( 20 "context" 21 "crypto/rand" 22 "crypto/rsa" 23 "crypto/x509" 24 "crypto/x509/pkix" 25 "fmt" 26 "math/big" 27 "testing" 28 "time" 29 30 . "github.com/onsi/gomega" 31 appsv1 "k8s.io/api/apps/v1" 32 corev1 "k8s.io/api/core/v1" 33 rbacv1 "k8s.io/api/rbac/v1" 34 apierrors "k8s.io/apimachinery/pkg/api/errors" 35 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 36 "k8s.io/apimachinery/pkg/runtime/schema" 37 "sigs.k8s.io/controller-runtime/pkg/client" 38 "sigs.k8s.io/controller-runtime/pkg/log" 39 40 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 41 "sigs.k8s.io/cluster-api/controllers/remote" 42 "sigs.k8s.io/cluster-api/util/certs" 43 "sigs.k8s.io/cluster-api/util/collections" 44 "sigs.k8s.io/cluster-api/util/kubeconfig" 45 "sigs.k8s.io/cluster-api/util/secret" 46 ) 47 48 func TestGetMachinesForCluster(t *testing.T) { 49 g := NewWithT(t) 50 51 m := Management{Client: &fakeClient{ 52 list: machineListForTestGetMachinesForCluster(), 53 }} 54 cluster := &clusterv1.Cluster{ 55 ObjectMeta: metav1.ObjectMeta{ 56 Namespace: metav1.NamespaceDefault, 57 Name: "my-cluster", 58 }, 59 } 60 machines, err := m.GetMachinesForCluster(ctx, cluster) 61 g.Expect(err).ToNot(HaveOccurred()) 62 g.Expect(machines).To(HaveLen(3)) 63 64 // Test the ControlPlaneMachines works 65 machines, err = m.GetMachinesForCluster(ctx, cluster, collections.ControlPlaneMachines("my-cluster")) 66 g.Expect(err).ToNot(HaveOccurred()) 67 g.Expect(machines).To(HaveLen(1)) 68 69 // Test that the filters use AND logic instead of OR logic 70 nameFilter := func(cluster *clusterv1.Machine) bool { 71 return cluster.Name == "first-machine" 72 } 73 machines, err = m.GetMachinesForCluster(ctx, cluster, collections.ControlPlaneMachines("my-cluster"), nameFilter) 74 g.Expect(err).ToNot(HaveOccurred()) 75 g.Expect(machines).To(HaveLen(1)) 76 } 77 78 func TestGetWorkloadCluster(t *testing.T) { 79 g := NewWithT(t) 80 81 ns, err := env.CreateNamespace(ctx, "workload-cluster2") 82 g.Expect(err).ToNot(HaveOccurred()) 83 defer func() { 84 g.Expect(env.Cleanup(ctx, ns)).To(Succeed()) 85 }() 86 87 // Create an etcd secret with valid certs 88 key, err := certs.NewPrivateKey() 89 g.Expect(err).ToNot(HaveOccurred()) 90 cert, err := getTestCACert(key) 91 g.Expect(err).ToNot(HaveOccurred()) 92 etcdSecret := &corev1.Secret{ 93 ObjectMeta: metav1.ObjectMeta{ 94 Name: "my-cluster-etcd", 95 Namespace: ns.Name, 96 Labels: map[string]string{ 97 clusterv1.ClusterNameLabel: "my-cluster", 98 }, 99 }, 100 Data: map[string][]byte{ 101 secret.TLSCrtDataName: certs.EncodeCertPEM(cert), 102 secret.TLSKeyDataName: certs.EncodePrivateKeyPEM(key), 103 }, 104 } 105 emptyCrtEtcdSecret := etcdSecret.DeepCopy() 106 delete(emptyCrtEtcdSecret.Data, secret.TLSCrtDataName) 107 emptyKeyEtcdSecret := etcdSecret.DeepCopy() 108 delete(emptyKeyEtcdSecret.Data, secret.TLSKeyDataName) 109 badCrtEtcdSecret := etcdSecret.DeepCopy() 110 badCrtEtcdSecret.Data[secret.TLSCrtDataName] = []byte("bad cert") 111 112 // Create kubeconfig secret 113 // Store the envtest config as the contents of the kubeconfig secret. 114 // This way we are using the envtest environment as both the 115 // management and the workload cluster. 116 testEnvKubeconfig := kubeconfig.FromEnvTestConfig(env.GetConfig(), &clusterv1.Cluster{ 117 ObjectMeta: metav1.ObjectMeta{ 118 Name: "my-cluster", 119 Namespace: ns.Name, 120 }, 121 }) 122 kubeconfigSecret := &corev1.Secret{ 123 ObjectMeta: metav1.ObjectMeta{ 124 Name: "my-cluster-kubeconfig", 125 Namespace: ns.Name, 126 Labels: map[string]string{ 127 clusterv1.ClusterNameLabel: "my-cluster", 128 }, 129 }, 130 Data: map[string][]byte{ 131 secret.KubeconfigDataName: testEnvKubeconfig, 132 }, 133 } 134 clusterKey := client.ObjectKey{ 135 Name: "my-cluster", 136 Namespace: ns.Name, 137 } 138 139 tests := []struct { 140 name string 141 clusterKey client.ObjectKey 142 objs []client.Object 143 expectErr bool 144 }{ 145 { 146 name: "returns a workload cluster", 147 clusterKey: clusterKey, 148 objs: []client.Object{etcdSecret.DeepCopy(), kubeconfigSecret.DeepCopy()}, 149 expectErr: false, 150 }, 151 { 152 name: "returns error if cannot get rest.Config from kubeconfigSecret", 153 clusterKey: clusterKey, 154 objs: []client.Object{etcdSecret.DeepCopy()}, 155 expectErr: true, 156 }, 157 { 158 name: "returns error if unable to find the etcd secret", 159 clusterKey: clusterKey, 160 objs: []client.Object{kubeconfigSecret.DeepCopy()}, 161 expectErr: true, 162 }, 163 { 164 name: "returns error if unable to find the certificate in the etcd secret", 165 clusterKey: clusterKey, 166 objs: []client.Object{emptyCrtEtcdSecret.DeepCopy(), kubeconfigSecret.DeepCopy()}, 167 expectErr: true, 168 }, 169 { 170 name: "returns error if unable to find the key in the etcd secret", 171 clusterKey: clusterKey, 172 objs: []client.Object{emptyKeyEtcdSecret.DeepCopy(), kubeconfigSecret.DeepCopy()}, 173 expectErr: true, 174 }, 175 { 176 name: "returns error if unable to generate client cert", 177 clusterKey: clusterKey, 178 objs: []client.Object{badCrtEtcdSecret.DeepCopy(), kubeconfigSecret.DeepCopy()}, 179 expectErr: true, 180 }, 181 } 182 183 for _, tt := range tests { 184 t.Run(tt.name, func(t *testing.T) { 185 g := NewWithT(t) 186 187 for _, o := range tt.objs { 188 g.Expect(env.CreateAndWait(ctx, o)).To(Succeed()) 189 defer func(do client.Object) { 190 g.Expect(env.CleanupAndWait(ctx, do)).To(Succeed()) 191 }(o) 192 } 193 194 // We have to create a new ClusterCacheTracker for every test case otherwise 195 // it could still have a rest config from a previous run cached. 196 tracker, err := remote.NewClusterCacheTracker( 197 env.Manager, 198 remote.ClusterCacheTrackerOptions{ 199 Log: &log.Log, 200 }, 201 ) 202 g.Expect(err).ToNot(HaveOccurred()) 203 204 m := Management{ 205 Client: env.GetClient(), 206 SecretCachingClient: secretCachingClient, 207 Tracker: tracker, 208 } 209 210 workloadCluster, err := m.GetWorkloadCluster(ctx, tt.clusterKey) 211 if tt.expectErr { 212 g.Expect(err).To(HaveOccurred()) 213 g.Expect(workloadCluster).To(BeNil()) 214 return 215 } 216 g.Expect(err).ToNot(HaveOccurred()) 217 g.Expect(workloadCluster).ToNot(BeNil()) 218 }) 219 } 220 } 221 222 func getTestCACert(key *rsa.PrivateKey) (*x509.Certificate, error) { 223 cfg := certs.Config{ 224 CommonName: "kubernetes", 225 } 226 227 now := time.Now().UTC() 228 229 tmpl := x509.Certificate{ 230 SerialNumber: new(big.Int).SetInt64(0), 231 Subject: pkix.Name{ 232 CommonName: cfg.CommonName, 233 Organization: cfg.Organization, 234 }, 235 NotBefore: now.Add(time.Minute * -5), 236 NotAfter: now.Add(time.Hour * 24), // 1 day 237 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, 238 MaxPathLenZero: true, 239 BasicConstraintsValid: true, 240 MaxPathLen: 0, 241 IsCA: true, 242 } 243 244 b, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, key.Public(), key) 245 if err != nil { 246 return nil, err 247 } 248 249 c, err := x509.ParseCertificate(b) 250 return c, err 251 } 252 253 func machineListForTestGetMachinesForCluster() *clusterv1.MachineList { 254 owned := true 255 ownedRef := []metav1.OwnerReference{ 256 { 257 Kind: "KubeadmControlPlane", 258 Name: "my-control-plane", 259 Controller: &owned, 260 }, 261 } 262 machine := func(name string) clusterv1.Machine { 263 return clusterv1.Machine{ 264 TypeMeta: metav1.TypeMeta{}, 265 ObjectMeta: metav1.ObjectMeta{ 266 Name: name, 267 Namespace: metav1.NamespaceDefault, 268 Labels: map[string]string{ 269 clusterv1.ClusterNameLabel: "my-cluster", 270 }, 271 }, 272 } 273 } 274 controlPlaneMachine := machine("first-machine") 275 controlPlaneMachine.ObjectMeta.Labels[clusterv1.MachineControlPlaneLabel] = "" 276 controlPlaneMachine.OwnerReferences = ownedRef 277 278 return &clusterv1.MachineList{ 279 Items: []clusterv1.Machine{ 280 controlPlaneMachine, 281 machine("second-machine"), 282 machine("third-machine"), 283 }, 284 } 285 } 286 287 type fakeClient struct { 288 client.Client 289 list interface{} 290 291 createErr error 292 get map[string]interface{} 293 getCalled bool 294 updateCalled bool 295 getErr error 296 patchErr error 297 updateErr error 298 listErr error 299 } 300 301 func (f *fakeClient) Get(_ context.Context, key client.ObjectKey, obj client.Object, _ ...client.GetOption) error { 302 f.getCalled = true 303 if f.getErr != nil { 304 return f.getErr 305 } 306 item := f.get[key.String()] 307 switch l := item.(type) { 308 case *corev1.Pod: 309 l.DeepCopyInto(obj.(*corev1.Pod)) 310 case *rbacv1.RoleBinding: 311 l.DeepCopyInto(obj.(*rbacv1.RoleBinding)) 312 case *rbacv1.Role: 313 l.DeepCopyInto(obj.(*rbacv1.Role)) 314 case *appsv1.DaemonSet: 315 l.DeepCopyInto(obj.(*appsv1.DaemonSet)) 316 case *corev1.ConfigMap: 317 l.DeepCopyInto(obj.(*corev1.ConfigMap)) 318 case *corev1.Secret: 319 l.DeepCopyInto(obj.(*corev1.Secret)) 320 case nil: 321 return apierrors.NewNotFound(schema.GroupResource{}, key.Name) 322 default: 323 return fmt.Errorf("unknown type: %s", l) 324 } 325 return nil 326 } 327 328 func (f *fakeClient) List(_ context.Context, list client.ObjectList, _ ...client.ListOption) error { 329 if f.listErr != nil { 330 return f.listErr 331 } 332 switch l := f.list.(type) { 333 case *clusterv1.MachineList: 334 l.DeepCopyInto(list.(*clusterv1.MachineList)) 335 case *corev1.NodeList: 336 l.DeepCopyInto(list.(*corev1.NodeList)) 337 case *corev1.PodList: 338 l.DeepCopyInto(list.(*corev1.PodList)) 339 default: 340 return fmt.Errorf("unknown type: %s", l) 341 } 342 return nil 343 } 344 345 func (f *fakeClient) Create(_ context.Context, _ client.Object, _ ...client.CreateOption) error { 346 if f.createErr != nil { 347 return f.createErr 348 } 349 return nil 350 } 351 352 func (f *fakeClient) Patch(_ context.Context, _ client.Object, _ client.Patch, _ ...client.PatchOption) error { 353 if f.patchErr != nil { 354 return f.patchErr 355 } 356 return nil 357 } 358 359 func (f *fakeClient) Update(_ context.Context, _ client.Object, _ ...client.UpdateOption) error { 360 f.updateCalled = true 361 if f.updateErr != nil { 362 return f.updateErr 363 } 364 return nil 365 }