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  }