sigs.k8s.io/cluster-api@v1.7.1/controlplane/kubeadm/internal/workload_cluster_rbac_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 "errors" 21 "testing" 22 23 "github.com/blang/semver/v4" 24 . "github.com/onsi/gomega" 25 rbacv1 "k8s.io/api/rbac/v1" 26 apierrors "k8s.io/apimachinery/pkg/api/errors" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/runtime/schema" 29 ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" 30 "sigs.k8s.io/controller-runtime/pkg/client/fake" 31 ) 32 33 func TestCluster_ReconcileKubeletRBACBinding_NoError(t *testing.T) { 34 type wantRBAC struct { 35 role ctrlclient.ObjectKey 36 roleBinding ctrlclient.ObjectKey 37 } 38 tests := []struct { 39 name string 40 client ctrlclient.Client 41 version semver.Version 42 want *wantRBAC 43 }{ 44 { 45 name: "creates role and role binding for Kubernetes/kubeadm < v1.24", 46 client: fake.NewClientBuilder().Build(), 47 version: semver.MustParse("1.23.3"), 48 want: &wantRBAC{ 49 role: ctrlclient.ObjectKey{Namespace: metav1.NamespaceSystem, Name: "kubeadm:kubelet-config-1.23"}, 50 roleBinding: ctrlclient.ObjectKey{Namespace: metav1.NamespaceSystem, Name: "kubeadm:kubelet-config-1.23"}, 51 }, 52 }, 53 { 54 name: "tolerates existing role binding for Kubernetes/kubeadm < v1.24", 55 client: fake.NewClientBuilder().WithObjects( 56 &rbacv1.RoleBinding{ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "kubeadm:kubelet-config-1.23"}, RoleRef: rbacv1.RoleRef{ 57 Name: "kubeadm:kubelet-config-1.23", 58 }}, 59 &rbacv1.Role{ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "kubeadm:kubelet-config-1.23"}, Rules: []rbacv1.PolicyRule{{ 60 Verbs: []string{"get"}, 61 APIGroups: []string{""}, 62 Resources: []string{"configmaps"}, 63 ResourceNames: []string{"kubelet-config-1.23"}, 64 }}}, 65 ).Build(), 66 version: semver.MustParse("1.23.3"), 67 want: &wantRBAC{ 68 role: ctrlclient.ObjectKey{Namespace: metav1.NamespaceSystem, Name: "kubeadm:kubelet-config-1.23"}, 69 roleBinding: ctrlclient.ObjectKey{Namespace: metav1.NamespaceSystem, Name: "kubeadm:kubelet-config-1.23"}, 70 }, 71 }, 72 { 73 name: "creates role and role binding for Kubernetes/kubeadm >= v1.24", 74 client: fake.NewClientBuilder().Build(), 75 version: semver.MustParse("1.24.0"), 76 want: &wantRBAC{ 77 role: ctrlclient.ObjectKey{Namespace: metav1.NamespaceSystem, Name: "kubeadm:kubelet-config"}, 78 roleBinding: ctrlclient.ObjectKey{Namespace: metav1.NamespaceSystem, Name: "kubeadm:kubelet-config"}, 79 }, 80 }, 81 { 82 name: "creates role and role binding for Kubernetes/kubeadm >= v1.24 ignoring pre-release and build tags", 83 client: fake.NewClientBuilder().Build(), 84 version: semver.MustParse("1.24.0-alpha.1+xyz.1"), 85 want: &wantRBAC{ 86 role: ctrlclient.ObjectKey{Namespace: metav1.NamespaceSystem, Name: "kubeadm:kubelet-config"}, 87 roleBinding: ctrlclient.ObjectKey{Namespace: metav1.NamespaceSystem, Name: "kubeadm:kubelet-config"}, 88 }, 89 }, 90 { 91 name: "tolerates existing role binding for Kubernetes/kubeadm >= v1.24", 92 client: fake.NewClientBuilder().WithObjects( 93 &rbacv1.RoleBinding{ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "kubeadm:kubelet-config"}, RoleRef: rbacv1.RoleRef{ 94 Name: "kubeadm:kubelet-config", 95 }}, 96 &rbacv1.Role{ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "kubeadm:kubelet-config"}, Rules: []rbacv1.PolicyRule{{ 97 Verbs: []string{"get"}, 98 APIGroups: []string{""}, 99 Resources: []string{"configmaps"}, 100 ResourceNames: []string{"kubelet-config"}, 101 }}}, 102 ).Build(), 103 version: semver.MustParse("1.24.1"), 104 want: &wantRBAC{ 105 role: ctrlclient.ObjectKey{Namespace: metav1.NamespaceSystem, Name: "kubeadm:kubelet-config"}, 106 roleBinding: ctrlclient.ObjectKey{Namespace: metav1.NamespaceSystem, Name: "kubeadm:kubelet-config"}, 107 }, 108 }, 109 } 110 111 for _, tt := range tests { 112 t.Run(tt.name, func(t *testing.T) { 113 g := NewWithT(t) 114 115 c := &Workload{ 116 Client: tt.client, 117 } 118 g.Expect(c.ReconcileKubeletRBACBinding(ctx, tt.version)).To(Succeed()) 119 g.Expect(c.ReconcileKubeletRBACRole(ctx, tt.version)).To(Succeed()) 120 if tt.want != nil { 121 r := &rbacv1.Role{} 122 // Role exists 123 g.Expect(tt.client.Get(ctx, tt.want.role, r)).To(Succeed()) 124 // Role ensure grants for the KubeletConfig config map 125 g.Expect(r.Rules).To(BeComparableTo([]rbacv1.PolicyRule{ 126 { 127 Verbs: []string{"get"}, 128 APIGroups: []string{""}, 129 Resources: []string{"configmaps"}, 130 ResourceNames: []string{generateKubeletConfigName(tt.version)}, 131 }, 132 })) 133 // RoleBinding exists 134 b := &rbacv1.RoleBinding{} 135 // RoleBinding refers to the role 136 g.Expect(tt.client.Get(ctx, tt.want.roleBinding, b)).To(Succeed()) 137 g.Expect(b.RoleRef.Name).To(Equal(tt.want.role.Name)) 138 } 139 }) 140 } 141 } 142 143 func TestCluster_ReconcileKubeletRBACBinding_Error(t *testing.T) { 144 tests := []struct { 145 name string 146 client ctrlclient.Client 147 }{ 148 { 149 name: "client fails to update an expected error or the role binding/role", 150 client: &fakeClient{ 151 createErr: errors.New(""), 152 }, 153 }, 154 } 155 156 for _, tt := range tests { 157 t.Run(tt.name, func(t *testing.T) { 158 g := NewWithT(t) 159 160 c := &Workload{ 161 Client: tt.client, 162 } 163 g.Expect(c.ReconcileKubeletRBACBinding(ctx, semver.MustParse("1.12.3"))).NotTo(Succeed()) 164 g.Expect(c.ReconcileKubeletRBACRole(ctx, semver.MustParse("1.13.3"))).NotTo(Succeed()) 165 }) 166 } 167 } 168 169 func TestCluster_AllowBootstrapTokensToGetNodes_NoError(t *testing.T) { 170 tests := []struct { 171 name string 172 client ctrlclient.Client 173 }{ 174 { 175 name: "role binding and role already exist", 176 client: &fakeClient{ 177 get: map[string]interface{}{ 178 GetNodesClusterRoleName: &rbacv1.ClusterRoleBinding{}, 179 }, 180 }, 181 }, 182 { 183 name: "role binding and role don't exist", 184 client: &fakeClient{}, 185 }, 186 { 187 name: "create returns an already exists error", 188 client: &fakeClient{ 189 createErr: apierrors.NewAlreadyExists(schema.GroupResource{}, ""), 190 }, 191 }, 192 } 193 194 for _, tt := range tests { 195 t.Run(tt.name, func(t *testing.T) { 196 g := NewWithT(t) 197 198 c := &Workload{ 199 Client: tt.client, 200 } 201 g.Expect(c.AllowBootstrapTokensToGetNodes(ctx)).To(Succeed()) 202 }) 203 } 204 } 205 206 func TestCluster_AllowBootstrapTokensToGetNodes_Error(t *testing.T) { 207 tests := []struct { 208 name string 209 client ctrlclient.Client 210 }{ 211 { 212 name: "client fails to retrieve an expected error or the cluster role binding/role", 213 client: &fakeClient{ 214 createErr: errors.New(""), 215 }, 216 }, 217 } 218 219 for _, tt := range tests { 220 t.Run(tt.name, func(t *testing.T) { 221 g := NewWithT(t) 222 223 c := &Workload{ 224 Client: tt.client, 225 } 226 g.Expect(c.AllowBootstrapTokensToGetNodes(ctx)).NotTo(Succeed()) 227 }) 228 } 229 }