github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/caas/kubernetes/clientconfig/plugins_test.go (about) 1 // Copyright 2019 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package clientconfig_test 5 6 import ( 7 "reflect" 8 9 "github.com/golang/mock/gomock" 10 jc "github.com/juju/testing/checkers" 11 gc "gopkg.in/check.v1" 12 core "k8s.io/api/core/v1" 13 rbacv1 "k8s.io/api/rbac/v1" 14 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 16 "github.com/juju/juju/caas/kubernetes/clientconfig" 17 ) 18 19 type k8sRawClientSuite struct { 20 BaseSuite 21 } 22 23 var _ = gc.Suite(&k8sRawClientSuite{}) 24 25 func (s *k8sRawClientSuite) SetUpSuite(c *gc.C) { 26 s.BaseSuite.SetUpSuite(c) 27 s.namespace = "kube-system" 28 } 29 30 func (s *k8sRawClientSuite) TestEnsureJujuAdminServiceAccount(c *gc.C) { 31 ctrl := s.setupBroker(c) 32 defer ctrl.Finish() 33 34 cfg := newClientConfig() 35 contextName := reflect.ValueOf(cfg.Contexts).MapKeys()[0].Interface().(string) 36 37 secret := &core.Secret{ 38 ObjectMeta: metav1.ObjectMeta{ 39 Name: "juju-sa-secret", 40 Namespace: s.namespace, 41 }, 42 Data: map[string][]byte{ 43 "ca.crt": []byte("a base64 encoded cert"), 44 "namespace": []byte("base64 encoded namespace"), 45 "token": []byte("a base64 encoded bearer token"), 46 }, 47 } 48 49 saName := "juju-service-account" 50 newSa := &core.ServiceAccount{ 51 ObjectMeta: metav1.ObjectMeta{ 52 Name: saName, 53 Namespace: s.namespace, 54 }, 55 } 56 saWithSecret := &core.ServiceAccount{ 57 ObjectMeta: metav1.ObjectMeta{ 58 Name: saName, 59 Namespace: s.namespace, 60 }, 61 Secrets: []core.ObjectReference{ 62 { 63 Kind: "Secret", 64 Name: secret.Name, 65 }, 66 }, 67 } 68 69 cr := &rbacv1.ClusterRole{ 70 ObjectMeta: metav1.ObjectMeta{ 71 Name: "cluster-admin", 72 Namespace: s.namespace, 73 }, 74 Rules: []rbacv1.PolicyRule{ 75 { 76 APIGroups: []string{rbacv1.APIGroupAll}, 77 Resources: []string{rbacv1.ResourceAll}, 78 Verbs: []string{rbacv1.VerbAll}, 79 }, 80 { 81 NonResourceURLs: []string{rbacv1.NonResourceAll}, 82 Verbs: []string{rbacv1.VerbAll}, 83 }, 84 }, 85 } 86 87 clusterRoleBinding := &rbacv1.ClusterRoleBinding{ 88 ObjectMeta: metav1.ObjectMeta{ 89 Name: "juju-cluster-role-binding", 90 }, 91 RoleRef: rbacv1.RoleRef{ 92 Kind: "ClusterRole", 93 Name: cr.Name, 94 }, 95 Subjects: []rbacv1.Subject{ 96 { 97 Kind: rbacv1.ServiceAccountKind, 98 Name: saName, 99 Namespace: s.namespace, 100 }, 101 }, 102 } 103 104 // 1st call of ensuring related resources - CREATE. 105 gomock.InOrder( 106 s.mockClusterRoles.EXPECT().Get(cr.Name, metav1.GetOptions{}).Times(1). 107 Return(nil, s.k8sNotFoundError()), 108 s.mockClusterRoles.EXPECT().Create(cr).Times(1). 109 Return(cr, nil), 110 s.mockServiceAccounts.EXPECT().Create(newSa).Times(1). 111 Return(newSa, nil), 112 s.mockServiceAccounts.EXPECT().Get(newSa.Name, metav1.GetOptions{}).Times(1). 113 Return(newSa, nil), 114 s.mockClusterRoleBinding.EXPECT().Create(clusterRoleBinding).Times(1). 115 Return(clusterRoleBinding, nil), 116 s.mockServiceAccounts.EXPECT().Get(newSa.Name, metav1.GetOptions{}).Times(1). 117 Return(saWithSecret, nil), 118 s.mockSecrets.EXPECT().Get(saWithSecret.Secrets[0].Name, metav1.GetOptions{}).Times(1). 119 Return(secret, nil), 120 ) 121 cfgOut, err := clientconfig.EnsureJujuAdminServiceAccount(s.k8sClient, cfg, contextName) 122 c.Assert(err, jc.ErrorIsNil) 123 authName := cfg.Contexts[contextName].AuthInfo 124 updatedAuthInfo := cfgOut.AuthInfos[authName] 125 c.Assert(updatedAuthInfo.AuthProvider, gc.IsNil) 126 c.Assert(updatedAuthInfo.ClientCertificateData, gc.DeepEquals, secret.Data[core.ServiceAccountRootCAKey]) 127 c.Assert(updatedAuthInfo.Token, gc.Equals, string(secret.Data[core.ServiceAccountTokenKey])) 128 129 } 130 131 func (s *k8sRawClientSuite) TestEnsureJujuServiceAdminAccountIdempotent(c *gc.C) { 132 ctrl := s.setupBroker(c) 133 defer ctrl.Finish() 134 135 cfg := newClientConfig() 136 contextName := reflect.ValueOf(cfg.Contexts).MapKeys()[0].Interface().(string) 137 138 secret := &core.Secret{ 139 ObjectMeta: metav1.ObjectMeta{ 140 Name: "juju-sa-secret", 141 Namespace: s.namespace, 142 }, 143 Data: map[string][]byte{ 144 "ca.crt": []byte("a base64 encoded cert"), 145 "namespace": []byte("base64 encoded namespace"), 146 "token": []byte("a base64 encoded bearer token"), 147 }, 148 } 149 150 saName := "juju-service-account" 151 newSa := &core.ServiceAccount{ 152 ObjectMeta: metav1.ObjectMeta{ 153 Name: saName, 154 Namespace: s.namespace, 155 }, 156 } 157 saWithSecret := &core.ServiceAccount{ 158 ObjectMeta: metav1.ObjectMeta{ 159 Name: saName, 160 Namespace: s.namespace, 161 }, 162 Secrets: []core.ObjectReference{ 163 { 164 Kind: "Secret", 165 Name: secret.Name, 166 }, 167 }, 168 } 169 170 cr := &rbacv1.ClusterRole{ 171 ObjectMeta: metav1.ObjectMeta{ 172 Name: "cluster-admin", 173 Namespace: s.namespace, 174 }, 175 Rules: []rbacv1.PolicyRule{ 176 { 177 APIGroups: []string{rbacv1.APIGroupAll}, 178 Resources: []string{rbacv1.ResourceAll}, 179 Verbs: []string{rbacv1.VerbAll}, 180 }, 181 { 182 NonResourceURLs: []string{rbacv1.NonResourceAll}, 183 Verbs: []string{rbacv1.VerbAll}, 184 }, 185 }, 186 } 187 188 clusterRoleBinding := &rbacv1.ClusterRoleBinding{ 189 ObjectMeta: metav1.ObjectMeta{ 190 Name: "juju-cluster-role-binding", 191 }, 192 RoleRef: rbacv1.RoleRef{ 193 Kind: "ClusterRole", 194 Name: cr.Name, 195 }, 196 Subjects: []rbacv1.Subject{ 197 { 198 Kind: rbacv1.ServiceAccountKind, 199 Name: saName, 200 Namespace: s.namespace, 201 }, 202 }, 203 } 204 205 // 2nd call of ensuring related resources - GET. 206 gomock.InOrder( 207 s.mockClusterRoles.EXPECT().Get(cr.Name, metav1.GetOptions{}).Times(1). 208 Return(cr, nil), 209 s.mockServiceAccounts.EXPECT().Create(newSa).Times(1). 210 Return(newSa, nil), 211 s.mockServiceAccounts.EXPECT().Get(newSa.Name, metav1.GetOptions{}).Times(1). 212 Return(newSa, nil), 213 s.mockClusterRoleBinding.EXPECT().Create(clusterRoleBinding).Times(1). 214 Return(clusterRoleBinding, nil), 215 s.mockServiceAccounts.EXPECT().Get(newSa.Name, metav1.GetOptions{}).Times(1). 216 Return(saWithSecret, nil), 217 s.mockSecrets.EXPECT().Get(saWithSecret.Secrets[0].Name, metav1.GetOptions{}).Times(1). 218 Return(secret, nil), 219 ) 220 cfgOut, err := clientconfig.EnsureJujuAdminServiceAccount(s.k8sClient, cfg, contextName) 221 c.Assert(err, jc.ErrorIsNil) 222 authName := cfg.Contexts[contextName].AuthInfo 223 updatedAuthInfo := cfgOut.AuthInfos[authName] 224 c.Assert(updatedAuthInfo.AuthProvider, gc.IsNil) 225 c.Assert(updatedAuthInfo.ClientCertificateData, gc.DeepEquals, secret.Data[core.ServiceAccountRootCAKey]) 226 c.Assert(updatedAuthInfo.Token, gc.Equals, string(secret.Data[core.ServiceAccountTokenKey])) 227 228 } 229 230 func (s *k8sRawClientSuite) TestEnsureClusterRole(c *gc.C) { 231 ctrl := s.setupBroker(c) 232 defer ctrl.Finish() 233 234 cr := &rbacv1.ClusterRole{ 235 ObjectMeta: metav1.ObjectMeta{ 236 Name: "juju-admin-cluster-role", 237 Namespace: s.namespace, 238 }, 239 Rules: []rbacv1.PolicyRule{ 240 { 241 APIGroups: []string{rbacv1.APIGroupAll}, 242 Resources: []string{rbacv1.ResourceAll}, 243 Verbs: []string{rbacv1.VerbAll}, 244 }, 245 { 246 NonResourceURLs: []string{rbacv1.NonResourceAll}, 247 Verbs: []string{rbacv1.VerbAll}, 248 }, 249 }, 250 } 251 252 gomock.InOrder( 253 s.mockClusterRoles.EXPECT().Get(cr.Name, metav1.GetOptions{}).Times(1). 254 Return(cr, nil), 255 ) 256 crOut, err := clientconfig.EnsureClusterRole(s.k8sClient, cr.Name, s.namespace) 257 c.Assert(err, jc.ErrorIsNil) 258 c.Assert(crOut, jc.DeepEquals, cr) 259 260 gomock.InOrder( 261 s.mockClusterRoles.EXPECT().Get(cr.Name, metav1.GetOptions{}).Times(1). 262 Return(nil, s.k8sNotFoundError()), 263 s.mockClusterRoles.EXPECT().Create(cr).Times(1). 264 Return(cr, nil), 265 ) 266 crOut, err = clientconfig.EnsureClusterRole(s.k8sClient, cr.Name, s.namespace) 267 c.Assert(err, jc.ErrorIsNil) 268 c.Assert(crOut, jc.DeepEquals, cr) 269 270 } 271 272 func (s *k8sRawClientSuite) TestEnsureServiceAccount(c *gc.C) { 273 ctrl := s.setupBroker(c) 274 defer ctrl.Finish() 275 276 sa := &core.ServiceAccount{ 277 ObjectMeta: metav1.ObjectMeta{ 278 Name: "juju-admin-sa", 279 Namespace: s.namespace, 280 }, 281 } 282 283 gomock.InOrder( 284 s.mockServiceAccounts.EXPECT().Create(sa).Times(1). 285 Return(sa, nil), 286 s.mockServiceAccounts.EXPECT().Get(sa.Name, metav1.GetOptions{}).Times(1). 287 Return(sa, nil), 288 ) 289 saOut, err := clientconfig.EnsureServiceAccount(s.k8sClient, sa.Name, s.namespace) 290 c.Assert(err, jc.ErrorIsNil) 291 c.Assert(saOut, jc.DeepEquals, sa) 292 293 gomock.InOrder( 294 s.mockServiceAccounts.EXPECT().Create(sa).Times(1). 295 Return(sa, s.k8sAlreadyExistsError()), 296 s.mockServiceAccounts.EXPECT().Get(sa.Name, metav1.GetOptions{}).Times(1). 297 Return(sa, nil), 298 ) 299 saOut, err = clientconfig.EnsureServiceAccount(s.k8sClient, sa.Name, s.namespace) 300 c.Assert(err, jc.ErrorIsNil) 301 c.Assert(saOut, jc.DeepEquals, sa) 302 } 303 304 func (s *k8sRawClientSuite) TestEnsureClusterRoleBinding(c *gc.C) { 305 ctrl := s.setupBroker(c) 306 defer ctrl.Finish() 307 308 sa := &core.ServiceAccount{ 309 ObjectMeta: metav1.ObjectMeta{ 310 Name: "juju-admin-sa", 311 Namespace: s.namespace, 312 }, 313 } 314 315 cr := &rbacv1.ClusterRole{ 316 ObjectMeta: metav1.ObjectMeta{ 317 Name: "juju-admin-cluster-role", 318 Namespace: s.namespace, 319 }, 320 Rules: []rbacv1.PolicyRule{ 321 { 322 APIGroups: []string{rbacv1.APIGroupAll}, 323 Resources: []string{rbacv1.ResourceAll}, 324 Verbs: []string{rbacv1.VerbAll}, 325 }, 326 { 327 NonResourceURLs: []string{rbacv1.NonResourceAll}, 328 Verbs: []string{rbacv1.VerbAll}, 329 }, 330 }, 331 } 332 333 clusterRoleBinding := &rbacv1.ClusterRoleBinding{ 334 ObjectMeta: metav1.ObjectMeta{ 335 Name: "juju-admin-cluster-role-binding", 336 }, 337 RoleRef: rbacv1.RoleRef{ 338 Kind: "ClusterRole", 339 Name: cr.Name, 340 }, 341 Subjects: []rbacv1.Subject{ 342 { 343 Kind: rbacv1.ServiceAccountKind, 344 Name: sa.Name, 345 Namespace: sa.Namespace, 346 }, 347 }, 348 } 349 350 gomock.InOrder( 351 s.mockClusterRoleBinding.EXPECT().Create(clusterRoleBinding).Times(1). 352 Return(clusterRoleBinding, nil), 353 ) 354 clusterRoleBindingOut, err := clientconfig.EnsureClusterRoleBinding(s.k8sClient, clusterRoleBinding.Name, sa, cr) 355 c.Assert(err, jc.ErrorIsNil) 356 c.Assert(clusterRoleBindingOut, jc.DeepEquals, clusterRoleBinding) 357 358 gomock.InOrder( 359 s.mockClusterRoleBinding.EXPECT().Create(clusterRoleBinding).Times(1). 360 Return(clusterRoleBinding, s.k8sAlreadyExistsError()), 361 ) 362 clusterRoleBindingOut, err = clientconfig.EnsureClusterRoleBinding(s.k8sClient, clusterRoleBinding.Name, sa, cr) 363 c.Assert(err, jc.ErrorIsNil) 364 c.Assert(clusterRoleBindingOut, jc.DeepEquals, clusterRoleBinding) 365 } 366 367 func (s *k8sRawClientSuite) TestGetServiceAccountSecret(c *gc.C) { 368 ctrl := s.setupBroker(c) 369 defer ctrl.Finish() 370 371 secret := &core.Secret{ 372 ObjectMeta: metav1.ObjectMeta{ 373 Name: "juju-sa-secret", 374 Namespace: s.namespace, 375 }, 376 Data: map[string][]byte{ 377 "ca.crt": []byte("a base64 encoded cert"), 378 "namespace": []byte("base64 encoded namespace"), 379 "token": []byte("a base64 encoded bearer token"), 380 }, 381 } 382 sa := &core.ServiceAccount{ 383 ObjectMeta: metav1.ObjectMeta{ 384 Name: "juju-admin-sa", 385 Namespace: s.namespace, 386 }, 387 Secrets: []core.ObjectReference{ 388 { 389 Kind: "Secret", 390 Name: secret.Name, 391 }, 392 }, 393 } 394 395 gomock.InOrder( 396 s.mockSecrets.EXPECT().Get(sa.Secrets[0].Name, metav1.GetOptions{}).Times(1). 397 Return(secret, nil), 398 ) 399 secretOut, err := clientconfig.GetServiceAccountSecret(s.k8sClient, sa) 400 c.Assert(err, jc.ErrorIsNil) 401 c.Assert(secretOut, jc.DeepEquals, secret) 402 } 403 404 func (s *k8sRawClientSuite) TestReplaceAuthProviderWithServiceAccountAuthData(c *gc.C) { 405 cfg := newClientConfig() 406 contextName := reflect.ValueOf(cfg.Contexts).MapKeys()[0].Interface().(string) 407 authName := cfg.Contexts[contextName].AuthInfo 408 409 secret := &core.Secret{ 410 ObjectMeta: metav1.ObjectMeta{ 411 Name: "juju-sa-secret", 412 Namespace: s.namespace, 413 }, 414 Data: map[string][]byte{ 415 "ca.crt": []byte("a base64 encoded cert"), 416 "namespace": []byte("base64 encoded namespace"), 417 "token": []byte("a base64 encoded bearer token"), 418 }, 419 } 420 clientconfig.ReplaceAuthProviderWithServiceAccountAuthData(contextName, cfg, secret) 421 updatedAuthInfo := cfg.AuthInfos[authName] 422 c.Assert(updatedAuthInfo.AuthProvider, gc.IsNil) 423 c.Assert(updatedAuthInfo.ClientCertificateData, gc.DeepEquals, secret.Data[core.ServiceAccountRootCAKey]) 424 c.Assert(updatedAuthInfo.Token, gc.Equals, string(secret.Data[core.ServiceAccountTokenKey])) 425 }