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  }