diff --git a/hack/e2e.sh b/hack/e2e.sh index c553cbdde..06661fac3 100755 --- a/hack/e2e.sh +++ b/hack/e2e.sh @@ -207,12 +207,6 @@ function test_cassandracluster() { kubectl create namespace "${namespace}" - # XXX Temporary work around until cassandra controller manages RBAC - kubectl create --namespace "${namespace}" \ - rolebinding "${namespace}-binding" \ - --clusterrole=cluster-admin \ - --serviceaccount="${namespace}:default" - if ! kubectl get \ --namespace "${namespace}" \ CassandraClusters; then diff --git a/pkg/controllers/cassandra/cassandra.go b/pkg/controllers/cassandra/cassandra.go index 59d9d6d8e..cdcab27c8 100644 --- a/pkg/controllers/cassandra/cassandra.go +++ b/pkg/controllers/cassandra/cassandra.go @@ -24,8 +24,12 @@ import ( "github.com/jetstack/navigator/pkg/controllers" "github.com/jetstack/navigator/pkg/controllers/cassandra/nodepool" "github.com/jetstack/navigator/pkg/controllers/cassandra/pilot" + "github.com/jetstack/navigator/pkg/controllers/cassandra/role" + "github.com/jetstack/navigator/pkg/controllers/cassandra/rolebinding" servicecql "github.com/jetstack/navigator/pkg/controllers/cassandra/service/cql" serviceseedprovider "github.com/jetstack/navigator/pkg/controllers/cassandra/service/seedprovider" + "github.com/jetstack/navigator/pkg/controllers/cassandra/serviceaccount" + rbacinformers "k8s.io/client-go/informers/rbac/v1beta1" ) // NewCassandra returns a new CassandraController that can be used @@ -35,16 +39,19 @@ import ( // It accepts a list of informers that are then used to monitor the state of the // target cluster. type CassandraController struct { - control ControlInterface - cassLister listersv1alpha1.CassandraClusterLister - statefulSetLister appslisters.StatefulSetLister - cassListerSynced cache.InformerSynced - serviceListerSynced cache.InformerSynced - statefulSetListerSynced cache.InformerSynced - pilotsListerSynced cache.InformerSynced - podsListerSynced cache.InformerSynced - queue workqueue.RateLimitingInterface - recorder record.EventRecorder + control ControlInterface + cassLister listersv1alpha1.CassandraClusterLister + statefulSetLister appslisters.StatefulSetLister + cassListerSynced cache.InformerSynced + serviceListerSynced cache.InformerSynced + statefulSetListerSynced cache.InformerSynced + pilotsListerSynced cache.InformerSynced + podsListerSynced cache.InformerSynced + serviceAccountsListerSynced cache.InformerSynced + rolesListerSynced cache.InformerSynced + roleBindingsListerSynced cache.InformerSynced + queue workqueue.RateLimitingInterface + recorder record.EventRecorder } func NewCassandra( @@ -55,6 +62,9 @@ func NewCassandra( statefulSets appsinformers.StatefulSetInformer, pilots navigatorinformers.PilotInformer, pods coreinformers.PodInformer, + serviceAccounts coreinformers.ServiceAccountInformer, + roles rbacinformers.RoleInformer, + roleBindings rbacinformers.RoleBindingInformer, recorder record.EventRecorder, ) *CassandraController { queue := workqueue.NewNamedRateLimitingQueue( @@ -82,6 +92,9 @@ func NewCassandra( cc.statefulSetListerSynced = statefulSets.Informer().HasSynced cc.pilotsListerSynced = pilots.Informer().HasSynced cc.podsListerSynced = pods.Informer().HasSynced + cc.serviceAccountsListerSynced = serviceAccounts.Informer().HasSynced + cc.rolesListerSynced = roles.Informer().HasSynced + cc.roleBindingsListerSynced = roleBindings.Informer().HasSynced cc.control = NewControl( serviceseedprovider.NewControl( kubeClient, @@ -105,6 +118,21 @@ func NewCassandra( statefulSets.Lister(), recorder, ), + serviceaccount.NewControl( + kubeClient, + serviceAccounts.Lister(), + recorder, + ), + role.NewControl( + kubeClient, + roles.Lister(), + recorder, + ), + rolebinding.NewControl( + kubeClient, + roleBindings.Lister(), + recorder, + ), recorder, ) cc.recorder = recorder @@ -274,6 +302,9 @@ func CassandraControllerFromContext(ctx *controllers.Context) *CassandraControll ctx.KubeSharedInformerFactory.Apps().V1beta1().StatefulSets(), ctx.SharedInformerFactory.Navigator().V1alpha1().Pilots(), ctx.KubeSharedInformerFactory.Core().V1().Pods(), + ctx.KubeSharedInformerFactory.Core().V1().ServiceAccounts(), + ctx.KubeSharedInformerFactory.Rbac().V1beta1().Roles(), + ctx.KubeSharedInformerFactory.Rbac().V1beta1().RoleBindings(), ctx.Recorder, ) } diff --git a/pkg/controllers/cassandra/cluster_control.go b/pkg/controllers/cassandra/cluster_control.go index 44d49b99f..37dff28ae 100644 --- a/pkg/controllers/cassandra/cluster_control.go +++ b/pkg/controllers/cassandra/cluster_control.go @@ -5,8 +5,11 @@ import ( v1alpha1 "github.com/jetstack/navigator/pkg/apis/navigator/v1alpha1" "github.com/jetstack/navigator/pkg/controllers/cassandra/nodepool" "github.com/jetstack/navigator/pkg/controllers/cassandra/pilot" + "github.com/jetstack/navigator/pkg/controllers/cassandra/role" + "github.com/jetstack/navigator/pkg/controllers/cassandra/rolebinding" servicecql "github.com/jetstack/navigator/pkg/controllers/cassandra/service/cql" serviceseedprovider "github.com/jetstack/navigator/pkg/controllers/cassandra/service/seedprovider" + "github.com/jetstack/navigator/pkg/controllers/cassandra/serviceaccount" apiv1 "k8s.io/api/core/v1" "k8s.io/client-go/tools/record" ) @@ -17,6 +20,8 @@ const ( SuccessSync = "SuccessSync" MessageErrorSyncServiceAccount = "Error syncing service account: %s" + MessageErrorSyncRole = "Error syncing role: %s" + MessageErrorSyncRoleBinding = "Error syncing role binding: %s" MessageErrorSyncConfigMap = "Error syncing config map: %s" MessageErrorSyncService = "Error syncing service: %s" MessageErrorSyncNodePools = "Error syncing node pools: %s" @@ -35,6 +40,9 @@ type defaultCassandraClusterControl struct { cqlServiceControl servicecql.Interface nodepoolControl nodepool.Interface pilotControl pilot.Interface + serviceAccountControl serviceaccount.Interface + roleControl role.Interface + roleBindingControl rolebinding.Interface recorder record.EventRecorder } @@ -43,6 +51,9 @@ func NewControl( cqlServiceControl servicecql.Interface, nodepoolControl nodepool.Interface, pilotControl pilot.Interface, + serviceAccountControl serviceaccount.Interface, + roleControl role.Interface, + roleBindingControl rolebinding.Interface, recorder record.EventRecorder, ) ControlInterface { return &defaultCassandraClusterControl{ @@ -50,6 +61,9 @@ func NewControl( cqlServiceControl: cqlServiceControl, nodepoolControl: nodepoolControl, pilotControl: pilotControl, + serviceAccountControl: serviceAccountControl, + roleControl: roleControl, + roleBindingControl: roleBindingControl, recorder: recorder, } } @@ -100,6 +114,39 @@ func (e *defaultCassandraClusterControl) Sync(c *v1alpha1.CassandraCluster) erro ) return err } + err = e.serviceAccountControl.Sync(c) + if err != nil { + e.recorder.Eventf( + c, + apiv1.EventTypeWarning, + ErrorSync, + MessageErrorSyncServiceAccount, + err, + ) + return err + } + err = e.roleControl.Sync(c) + if err != nil { + e.recorder.Eventf( + c, + apiv1.EventTypeWarning, + ErrorSync, + MessageErrorSyncRole, + err, + ) + return err + } + err = e.roleBindingControl.Sync(c) + if err != nil { + e.recorder.Eventf( + c, + apiv1.EventTypeWarning, + ErrorSync, + MessageErrorSyncRoleBinding, + err, + ) + return err + } e.recorder.Event( c, apiv1.EventTypeNormal, diff --git a/pkg/controllers/cassandra/nodepool/resource.go b/pkg/controllers/cassandra/nodepool/resource.go index 6b6120ec0..039128878 100644 --- a/pkg/controllers/cassandra/nodepool/resource.go +++ b/pkg/controllers/cassandra/nodepool/resource.go @@ -47,6 +47,7 @@ func StatefulSetForCluster( Labels: nodePoolLabels, }, Spec: apiv1.PodSpec{ + ServiceAccountName: util.ServiceAccountName(cluster), Volumes: []apiv1.Volume{ apiv1.Volume{ Name: sharedVolumeName, diff --git a/pkg/controllers/cassandra/role/control.go b/pkg/controllers/cassandra/role/control.go new file mode 100644 index 000000000..dbca1033a --- /dev/null +++ b/pkg/controllers/cassandra/role/control.go @@ -0,0 +1,90 @@ +package role + +import ( + "github.com/jetstack/navigator/pkg/apis/navigator" + "github.com/jetstack/navigator/pkg/apis/navigator/v1alpha1" + "github.com/jetstack/navigator/pkg/controllers/cassandra/util" + rbacv1 "k8s.io/api/rbac/v1beta1" + k8sErrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + rbaclisters "k8s.io/client-go/listers/rbac/v1beta1" + + "k8s.io/client-go/tools/record" +) + +type Interface interface { + Sync(*v1alpha1.CassandraCluster) error +} + +type control struct { + kubeClient kubernetes.Interface + roleLister rbaclisters.RoleLister + recorder record.EventRecorder +} + +var _ Interface = &control{} + +func NewControl( + kubeClient kubernetes.Interface, + roleLister rbaclisters.RoleLister, + recorder record.EventRecorder, +) *control { + return &control{ + kubeClient: kubeClient, + roleLister: roleLister, + recorder: recorder, + } +} + +func (c *control) Sync(cluster *v1alpha1.CassandraCluster) error { + newRole := RoleForCluster(cluster) + client := c.kubeClient.RbacV1beta1().Roles(newRole.Namespace) + _, err := c.roleLister. + Roles(newRole.Namespace). + Get(newRole.Name) + if k8sErrors.IsNotFound(err) { + _, err = client.Create(newRole) + } + return err +} + +func RoleForCluster(cluster *v1alpha1.CassandraCluster) *rbacv1.Role { + return &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: util.PilotRBACRoleName(cluster), + Namespace: cluster.Namespace, + OwnerReferences: []metav1.OwnerReference{ + util.NewControllerRef(cluster), + }, + Labels: util.ClusterLabels(cluster), + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Verbs: []string{"create", "update", "patch"}, + Resources: []string{"events"}, + }, + { + APIGroups: []string{""}, + Verbs: []string{"create", "update", "patch", "get", "list", "watch"}, + Resources: []string{"configmaps"}, + }, + { + APIGroups: []string{navigator.GroupName}, + Verbs: []string{"get", "list", "watch"}, + Resources: []string{ + "pilots", + "cassandraclusters", + }, + }, + { + APIGroups: []string{navigator.GroupName}, + Verbs: []string{"update", "patch"}, + Resources: []string{ + "pilots/status", + }, + }, + }, + } +} diff --git a/pkg/controllers/cassandra/role/control_test.go b/pkg/controllers/cassandra/role/control_test.go new file mode 100644 index 000000000..385ff71e7 --- /dev/null +++ b/pkg/controllers/cassandra/role/control_test.go @@ -0,0 +1,40 @@ +package role_test + +import ( + "fmt" + "testing" + + "github.com/jetstack/navigator/pkg/controllers/cassandra/role" + casstesting "github.com/jetstack/navigator/pkg/controllers/cassandra/testing" +) + +func TestRoleSync(t *testing.T) { + t.Run( + "role missing", + func(t *testing.T) { + f := casstesting.NewFixture(t) + f.Run() + f.AssertRolesLength(1) + }, + ) + t.Run( + "role exists", + func(t *testing.T) { + f := casstesting.NewFixture(t) + existingRole := role.RoleForCluster(f.Cluster) + f.AddObjectK(existingRole) + f.Run() + f.AssertRolesLength(1) + }, + ) + t.Run( + "sync fails", + func(t *testing.T) { + f := casstesting.NewFixture(t) + f.RoleControl = &casstesting.FakeControl{ + SyncError: fmt.Errorf("simulated sync error"), + } + f.RunExpectError() + }, + ) +} diff --git a/pkg/controllers/cassandra/rolebinding/control.go b/pkg/controllers/cassandra/rolebinding/control.go new file mode 100644 index 000000000..f6e76ebc2 --- /dev/null +++ b/pkg/controllers/cassandra/rolebinding/control.go @@ -0,0 +1,72 @@ +package rolebinding + +import ( + "github.com/jetstack/navigator/pkg/apis/navigator/v1alpha1" + "github.com/jetstack/navigator/pkg/controllers/cassandra/util" + rbacv1 "k8s.io/api/rbac/v1beta1" + k8sErrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + rbaclisters "k8s.io/client-go/listers/rbac/v1beta1" + + "k8s.io/client-go/tools/record" +) + +type Interface interface { + Sync(*v1alpha1.CassandraCluster) error +} + +type control struct { + kubeClient kubernetes.Interface + roleBindingLister rbaclisters.RoleBindingLister + recorder record.EventRecorder +} + +var _ Interface = &control{} + +func NewControl( + kubeClient kubernetes.Interface, + roleBindingLister rbaclisters.RoleBindingLister, + recorder record.EventRecorder, +) *control { + return &control{ + kubeClient: kubeClient, + roleBindingLister: roleBindingLister, + recorder: recorder, + } +} + +func (c *control) Sync(cluster *v1alpha1.CassandraCluster) error { + newRoleBinding := RoleBindingForCluster(cluster) + client := c.kubeClient.RbacV1beta1().RoleBindings(newRoleBinding.Namespace) + _, err := c.roleBindingLister. + RoleBindings(newRoleBinding.Namespace). + Get(newRoleBinding.Name) + if k8sErrors.IsNotFound(err) { + _, err = client.Create(newRoleBinding) + } + return err +} + +func RoleBindingForCluster(cluster *v1alpha1.CassandraCluster) *rbacv1.RoleBinding { + return &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: util.PilotRBACRoleName(cluster), + Namespace: cluster.Namespace, + OwnerReferences: []metav1.OwnerReference{ + util.NewControllerRef(cluster), + }, + Labels: util.ClusterLabels(cluster), + }, + Subjects: []rbacv1.Subject{ + { + Kind: rbacv1.ServiceAccountKind, + Name: util.ServiceAccountName(cluster), + }, + }, + RoleRef: rbacv1.RoleRef{ + Kind: "Role", + Name: util.PilotRBACRoleName(cluster), + }, + } +} diff --git a/pkg/controllers/cassandra/rolebinding/control_test.go b/pkg/controllers/cassandra/rolebinding/control_test.go new file mode 100644 index 000000000..59844cd67 --- /dev/null +++ b/pkg/controllers/cassandra/rolebinding/control_test.go @@ -0,0 +1,40 @@ +package rolebinding_test + +import ( + "fmt" + "testing" + + "github.com/jetstack/navigator/pkg/controllers/cassandra/rolebinding" + casstesting "github.com/jetstack/navigator/pkg/controllers/cassandra/testing" +) + +func TestRoleBindingSync(t *testing.T) { + t.Run( + "role missing", + func(t *testing.T) { + f := casstesting.NewFixture(t) + f.Run() + f.AssertRoleBindingsLength(1) + }, + ) + t.Run( + "role exists", + func(t *testing.T) { + f := casstesting.NewFixture(t) + existingRoleBinding := rolebinding.RoleBindingForCluster(f.Cluster) + f.AddObjectK(existingRoleBinding) + f.Run() + f.AssertRoleBindingsLength(1) + }, + ) + t.Run( + "sync fails", + func(t *testing.T) { + f := casstesting.NewFixture(t) + f.RoleBindingControl = &casstesting.FakeControl{ + SyncError: fmt.Errorf("simulated sync error"), + } + f.RunExpectError() + }, + ) +} diff --git a/pkg/controllers/cassandra/serviceaccount/control.go b/pkg/controllers/cassandra/serviceaccount/control.go new file mode 100644 index 000000000..63fe13383 --- /dev/null +++ b/pkg/controllers/cassandra/serviceaccount/control.go @@ -0,0 +1,61 @@ +package serviceaccount + +import ( + "github.com/jetstack/navigator/pkg/apis/navigator/v1alpha1" + "github.com/jetstack/navigator/pkg/controllers/cassandra/util" + "k8s.io/api/core/v1" + k8sErrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + corelisters "k8s.io/client-go/listers/core/v1" + "k8s.io/client-go/tools/record" +) + +type Interface interface { + Sync(*v1alpha1.CassandraCluster) error +} + +type control struct { + kubeClient kubernetes.Interface + serviceAccountLister corelisters.ServiceAccountLister + recorder record.EventRecorder +} + +var _ Interface = &control{} + +func NewControl( + kubeClient kubernetes.Interface, + serviceAccountLister corelisters.ServiceAccountLister, + recorder record.EventRecorder, +) *control { + return &control{ + kubeClient: kubeClient, + serviceAccountLister: serviceAccountLister, + recorder: recorder, + } +} + +func (c *control) Sync(cluster *v1alpha1.CassandraCluster) error { + newAccount := ServiceAccountForCluster(cluster) + client := c.kubeClient.CoreV1().ServiceAccounts(newAccount.Namespace) + _, err := c.serviceAccountLister. + ServiceAccounts(newAccount.Namespace). + Get(newAccount.Name) + if k8sErrors.IsNotFound(err) { + _, err = client.Create(newAccount) + } + return err +} + +func ServiceAccountForCluster(cluster *v1alpha1.CassandraCluster) *v1.ServiceAccount { + return &v1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: util.ServiceAccountName(cluster), + Namespace: cluster.Namespace, + OwnerReferences: []metav1.OwnerReference{ + util.NewControllerRef(cluster), + }, + Labels: util.ClusterLabels(cluster), + }, + } +} diff --git a/pkg/controllers/cassandra/serviceaccount/control_test.go b/pkg/controllers/cassandra/serviceaccount/control_test.go new file mode 100644 index 000000000..d54598c5c --- /dev/null +++ b/pkg/controllers/cassandra/serviceaccount/control_test.go @@ -0,0 +1,40 @@ +package serviceaccount_test + +import ( + "fmt" + "testing" + + "github.com/jetstack/navigator/pkg/controllers/cassandra/serviceaccount" + casstesting "github.com/jetstack/navigator/pkg/controllers/cassandra/testing" +) + +func TestServiceAccountSync(t *testing.T) { + t.Run( + "service account missing", + func(t *testing.T) { + f := casstesting.NewFixture(t) + f.Run() + f.AssertServiceAccountsLength(1) + }, + ) + t.Run( + "service account exists", + func(t *testing.T) { + f := casstesting.NewFixture(t) + existingServiceaccount := serviceaccount.ServiceAccountForCluster(f.Cluster) + f.AddObjectK(existingServiceaccount) + f.Run() + f.AssertServiceAccountsLength(1) + }, + ) + t.Run( + "sync fails", + func(t *testing.T) { + f := casstesting.NewFixture(t) + f.ServiceAccountControl = &casstesting.FakeControl{ + SyncError: fmt.Errorf("simulated sync error"), + } + f.RunExpectError() + }, + ) +} diff --git a/pkg/controllers/cassandra/testing/testing.go b/pkg/controllers/cassandra/testing/testing.go index f566d967e..191734c44 100644 --- a/pkg/controllers/cassandra/testing/testing.go +++ b/pkg/controllers/cassandra/testing/testing.go @@ -4,13 +4,17 @@ import ( "testing" navinformers "github.com/jetstack/navigator/pkg/client/informers/externalversions" + rbacv1 "k8s.io/api/rbac/v1beta1" "github.com/jetstack/navigator/pkg/apis/navigator/v1alpha1" "github.com/jetstack/navigator/pkg/controllers/cassandra" "github.com/jetstack/navigator/pkg/controllers/cassandra/nodepool" "github.com/jetstack/navigator/pkg/controllers/cassandra/pilot" + "github.com/jetstack/navigator/pkg/controllers/cassandra/role" + "github.com/jetstack/navigator/pkg/controllers/cassandra/rolebinding" servicecql "github.com/jetstack/navigator/pkg/controllers/cassandra/service/cql" serviceseedprovider "github.com/jetstack/navigator/pkg/controllers/cassandra/service/seedprovider" + "github.com/jetstack/navigator/pkg/controllers/cassandra/serviceaccount" "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -48,6 +52,9 @@ type Fixture struct { CqlServiceControl servicecql.Interface NodepoolControl nodepool.Interface PilotControl pilot.Interface + ServiceAccountControl serviceaccount.Interface + RoleControl role.Interface + RoleBindingControl rolebinding.Interface k8sClient *fake.Clientset k8sObjects []runtime.Object naviClient *navigatorfake.Clientset @@ -114,12 +121,41 @@ func (f *Fixture) setupAndSync() error { recorder, ) } + serviceAccounts := k8sFactory.Core().V1().ServiceAccounts().Lister() + if f.ServiceAccountControl == nil { + f.ServiceAccountControl = serviceaccount.NewControl( + f.k8sClient, + serviceAccounts, + recorder, + ) + } + + roles := k8sFactory.Rbac().V1beta1().Roles().Lister() + if f.RoleControl == nil { + f.RoleControl = role.NewControl( + f.k8sClient, + roles, + recorder, + ) + } + + roleBindings := k8sFactory.Rbac().V1beta1().RoleBindings().Lister() + if f.RoleBindingControl == nil { + f.RoleBindingControl = rolebinding.NewControl( + f.k8sClient, + roleBindings, + recorder, + ) + } c := cassandra.NewControl( f.SeedProviderServiceControl, f.CqlServiceControl, f.NodepoolControl, f.PilotControl, + f.ServiceAccountControl, + f.RoleControl, + f.RoleBindingControl, recorder, ) stopCh := make(chan struct{}) @@ -132,6 +168,9 @@ func (f *Fixture) setupAndSync() error { k8sFactory.Core().V1().Services().Informer().HasSynced, k8sFactory.Apps().V1beta1().StatefulSets().Informer().HasSynced, naviFactory.Navigator().V1alpha1().Pilots().Informer().HasSynced, + k8sFactory.Core().V1().ServiceAccounts().Informer().HasSynced, + k8sFactory.Rbac().V1beta1().Roles().Informer().HasSynced, + k8sFactory.Rbac().V1beta1().RoleBindings().Informer().HasSynced, ) { f.t.Fatal("WaitForCacheSync failure") } @@ -174,6 +213,78 @@ func (f *Fixture) AssertServicesLength(l int) { } } +func (f *Fixture) ServiceAccounts() *v1.ServiceAccountList { + serviceAccounts, err := f.k8sClient. + CoreV1(). + ServiceAccounts(f.Cluster.Namespace). + List(metav1.ListOptions{}) + if err != nil { + f.t.Fatal(err) + } + return serviceAccounts +} + +func (f *Fixture) AssertServiceAccountsLength(l int) { + serviceAccounts := f.ServiceAccounts() + serviceAccountsLength := len(serviceAccounts.Items) + if serviceAccountsLength != l { + f.t.Log(serviceAccounts) + f.t.Errorf( + "Incorrect number of services accounts. Expected %d. Got %d.", + l, + serviceAccountsLength, + ) + } +} + +func (f *Fixture) Roles() *rbacv1.RoleList { + roles, err := f.k8sClient. + RbacV1beta1(). + Roles(f.Cluster.Namespace). + List(metav1.ListOptions{}) + if err != nil { + f.t.Fatal(err) + } + return roles +} + +func (f *Fixture) AssertRolesLength(l int) { + roles := f.Roles() + rolesLength := len(roles.Items) + if rolesLength != l { + f.t.Log(roles) + f.t.Errorf( + "Incorrect number of roles. Expected %d. Got %d.", + l, + rolesLength, + ) + } +} + +func (f *Fixture) RoleBindings() *rbacv1.RoleBindingList { + roleBindings, err := f.k8sClient. + RbacV1beta1(). + RoleBindings(f.Cluster.Namespace). + List(metav1.ListOptions{}) + if err != nil { + f.t.Fatal(err) + } + return roleBindings +} + +func (f *Fixture) AssertRoleBindingsLength(l int) { + roleBindings := f.RoleBindings() + roleBindingsLength := len(roleBindings.Items) + if roleBindingsLength != l { + f.t.Log(roleBindings) + f.t.Errorf( + "Incorrect number of role bindings. Expected %d. Got %d.", + l, + roleBindingsLength, + ) + } +} + func (f *Fixture) StatefulSets() *apps.StatefulSetList { sets, err := f.k8sClient. AppsV1beta1(). diff --git a/pkg/controllers/cassandra/util/util.go b/pkg/controllers/cassandra/util/util.go index 64416efc0..eea60c4a3 100644 --- a/pkg/controllers/cassandra/util/util.go +++ b/pkg/controllers/cassandra/util/util.go @@ -44,6 +44,14 @@ func CqlServiceName(c *v1alpha1.CassandraCluster) string { return fmt.Sprintf("%s-cql", ResourceBaseName(c)) } +func ServiceAccountName(c *v1alpha1.CassandraCluster) string { + return ResourceBaseName(c) +} + +func PilotRBACRoleName(c *v1alpha1.CassandraCluster) string { + return fmt.Sprintf("%s-pilot", ResourceBaseName(c)) +} + func ClusterLabels(c *v1alpha1.CassandraCluster) map[string]string { return map[string]string{ "app": "cassandracluster",