github.com/jenkins-x/jx/v2@v2.1.155/pkg/cmd/controller/controller_role_test.go (about) 1 // +build unit 2 3 package controller_test 4 5 import ( 6 "fmt" 7 "testing" 8 9 "github.com/jenkins-x/jx/v2/pkg/cmd/controller" 10 "github.com/jenkins-x/jx/v2/pkg/cmd/testhelpers" 11 12 "github.com/jenkins-x/jx-logging/pkg/log" 13 "github.com/stretchr/testify/require" 14 "k8s.io/client-go/kubernetes" 15 16 v1 "github.com/jenkins-x/jx-api/pkg/apis/jenkins.io/v1" 17 "github.com/jenkins-x/jx/v2/pkg/cmd/opts" 18 "github.com/jenkins-x/jx/v2/pkg/gits" 19 "github.com/jenkins-x/jx/v2/pkg/helm" 20 "github.com/jenkins-x/jx/v2/pkg/kube" 21 resources_test "github.com/jenkins-x/jx/v2/pkg/kube/resources/mocks" 22 "github.com/jenkins-x/jx/v2/pkg/tests" 23 "github.com/jenkins-x/jx/v2/pkg/util" 24 "github.com/stretchr/testify/assert" 25 rbacv1 "k8s.io/api/rbac/v1" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/runtime" 28 ) 29 30 func TestEnvironmentRoleBinding(t *testing.T) { 31 t.Parallel() 32 o := &controller.ControllerRoleOptions{ 33 ControllerOptions: controller.ControllerOptions{ 34 CommonOptions: &opts.CommonOptions{}, 35 }, 36 NoWatch: true, 37 } 38 roleName := "myrole" 39 roleBindingName := roleName 40 roleNameWithoutLabel := "myroleWithoutLabel" 41 teamNs := "jx" 42 roleLabels := make(map[string]string) 43 roleLabels[kube.LabelKind] = kube.ValueKindEnvironmentRole 44 45 role := &rbacv1.Role{ 46 ObjectMeta: metav1.ObjectMeta{ 47 Name: roleName, 48 Namespace: teamNs, 49 Labels: roleLabels, 50 }, 51 Rules: []rbacv1.PolicyRule{ 52 { 53 Verbs: []string{"get", "watch", "list"}, 54 APIGroups: []string{""}, 55 Resources: []string{"configmaps", "pods", "services"}, 56 }, 57 }, 58 } 59 60 roleWithLabel := &rbacv1.Role{ 61 ObjectMeta: metav1.ObjectMeta{ 62 Name: roleNameWithoutLabel, 63 Namespace: teamNs, 64 }, 65 Rules: []rbacv1.PolicyRule{ 66 { 67 Verbs: []string{"get", "watch", "list"}, 68 APIGroups: []string{""}, 69 Resources: []string{"configmaps", "pods", "services"}, 70 }, 71 }, 72 } 73 74 envRoleBinding := &v1.EnvironmentRoleBinding{ 75 ObjectMeta: metav1.ObjectMeta{ 76 Name: roleBindingName, 77 Namespace: teamNs, 78 }, 79 Spec: v1.EnvironmentRoleBindingSpec{ 80 Subjects: []rbacv1.Subject{ 81 { 82 Kind: "ServiceAccount", 83 Name: "jenkins", 84 Namespace: teamNs, 85 }, 86 }, 87 RoleRef: rbacv1.RoleRef{ 88 APIGroup: "rbac.authorization.k8s.io", 89 Kind: "Role", 90 Name: roleName, 91 }, 92 Environments: []v1.EnvironmentFilter{ 93 { 94 Includes: []string{"*"}, 95 }, 96 }, 97 }, 98 } 99 100 testhelpers.ConfigureTestOptionsWithResources(o.CommonOptions, 101 []runtime.Object{ 102 role, 103 roleWithLabel, 104 }, 105 []runtime.Object{ 106 kube.NewPermanentEnvironment("staging"), 107 kube.NewPermanentEnvironment("production"), 108 kube.NewPreviewEnvironment(teamNs + "-jstrachan-demo96-pr-1"), 109 kube.NewPreviewEnvironment(teamNs + "-jstrachan-another-pr-3"), 110 envRoleBinding, 111 }, 112 gits.NewGitCLI(), 113 nil, 114 helm.NewHelmCLI("helm", helm.V2, "", true), 115 resources_test.NewMockInstaller(), 116 ) 117 118 err := o.Run() 119 assert.NoError(t, err) 120 121 nsNames := []string{teamNs, teamNs + "-staging", teamNs + "-production", teamNs + "-preview-jx-jstrachan-demo96-pr-1", teamNs + "-preview-jx-jstrachan-another-pr-3"} 122 123 kubeClient, err := o.KubeClient() 124 require.NoError(t, err) 125 jxClient, _, err := o.JXClient() 126 require.NoError(t, err) 127 128 for _, ns := range nsNames { 129 roleBinding, err := kubeClient.RbacV1().RoleBindings(ns).Get(roleBindingName, metav1.GetOptions{}) 130 assert.NoError(t, err, "Failed to find RoleBinding in namespace %s for name %s", ns, roleBindingName) 131 132 if roleBinding != nil && err == nil { 133 assert.Equal(t, envRoleBinding.Spec.RoleRef, roleBinding.RoleRef, 134 "RoleBinding.RoleRef for name %s in namespace %s", roleBindingName, ns) 135 } 136 137 r, err := kubeClient.RbacV1().Roles(ns).Get(roleName, metav1.GetOptions{}) 138 assert.NoError(t, err, "Failed to find Role in namespace %s for name %s", ns, roleName) 139 140 if r != nil && err == nil { 141 assert.Equal(t, role.Rules, r.Rules, 142 "Role.Rules for name %s in namespace %s", roleBindingName, ns) 143 } 144 if util.StringMatchesPattern(ns, teamNs) { 145 jxClient, ns, err := o.JXClient() 146 if err == nil { 147 envRoleBindings, err := jxClient.JenkinsV1().EnvironmentRoleBindings(ns).Get(roleName, metav1.GetOptions{}) 148 if err != nil { 149 assert.NotNil(t, envRoleBindings, "No EnvironmentRoleBinding called %s in namespace %s", roleName, ns) 150 } 151 } 152 } 153 } 154 155 if tests.IsDebugLog() { 156 namespaces, err := kubeClient.CoreV1().Namespaces().List(metav1.ListOptions{}) 157 assert.NoError(t, err) 158 if err == nil { 159 for _, ns := range namespaces.Items { 160 tests.Debugf("Has namespace %s\n", ns.Name) 161 } 162 } 163 } 164 165 // now lets add new user to the EnvironmentRoleBinding 166 newUserKind := "ServiceAccount" 167 newUser := "cheese" 168 envRoleBinding, err = jxClient.JenkinsV1().EnvironmentRoleBindings(teamNs).Get(roleBindingName, metav1.GetOptions{}) 169 require.NoError(t, err, "Loading EnvironmentRoleBinding in ns %s with name %s", teamNs, roleBindingName) 170 envRoleBinding.Spec.Subjects = append(envRoleBinding.Spec.Subjects, rbacv1.Subject{ 171 Kind: "ServiceAccount", 172 Name: newUser, 173 Namespace: teamNs, 174 }) 175 176 envRoleBinding, err = jxClient.JenkinsV1().EnvironmentRoleBindings(teamNs).PatchUpdate(envRoleBinding) 177 require.NoError(t, err, "Updating EnvironmentRoleBinding in ns %s with name %s", teamNs, roleBindingName) 178 179 // now lets simulate the watch... 180 err = o.UpsertEnvironmentRoleBinding(envRoleBinding) 181 require.NoError(t, err, "Failed to respond to updated EnvironmentRoleBinding in ns %s with name %s", teamNs, roleBindingName) 182 183 AssertRoleBindingsInEnvironmentsContainsSubject(t, kubeClient, nsNames, roleBindingName, newUserKind, teamNs, newUser) 184 185 message := fmt.Sprintf("For EnvironmentRoleBinding in namespace %s for name %s", teamNs, roleBindingName) 186 187 // lets add a new preview environment 188 newEnv := kube.NewPreviewEnvironment(teamNs + "-jstrachan-newthingy-pr-1") 189 newPreviewNS := newEnv.Spec.Namespace 190 _, err = jxClient.JenkinsV1().Environments(teamNs).Create(newEnv) 191 require.NoError(t, err, "Failed to create an Environment %s in ns %s", newPreviewNS, teamNs) 192 193 log.Logger().Infof("Created Preview Environment %s", newPreviewNS) 194 195 // now lets simulate the watch... 196 err = o.UpsertEnvironmentRoleBinding(envRoleBinding) 197 198 nsNames = append(nsNames, newPreviewNS) 199 AssertRoleBindingsInEnvironmentsContainsSubject(t, kubeClient, nsNames, roleBindingName, newUserKind, teamNs, newUser) 200 201 // now lets remove the user... 202 envRoleBinding.Spec.Subjects = AssertRemoveSubject(t, envRoleBinding.Spec.Subjects, message, newUserKind, teamNs, newUser) 203 envRoleBinding, err = jxClient.JenkinsV1().EnvironmentRoleBindings(teamNs).PatchUpdate(envRoleBinding) 204 require.NoError(t, err, "Updating EnvironmentRoleBinding in ns %s with name %s", teamNs, roleBindingName) 205 206 // now lets simulate the watch... 207 err = o.UpsertEnvironmentRoleBinding(envRoleBinding) 208 209 AssertRoleBindingsInEnvironmentsNotContainsSubject(t, kubeClient, nsNames, roleBindingName, newUserKind, teamNs, newUser) 210 211 // lets assert that roles get updated in all the namespaces 212 AssertRolesInEnvironmentsNotContainsPolicyRule(t, kubeClient, nsNames, roleName, "", "get", "secrets") 213 role, err = kubeClient.RbacV1().Roles(teamNs).Get(roleName, metav1.GetOptions{}) 214 require.NoError(t, err, "Failed to get Role in ns %s with name %s", teamNs, roleName) 215 216 lastIdx := len(role.Rules) - 1 217 role.Rules[lastIdx].Resources = append(role.Rules[lastIdx].Resources, "secrets") 218 log.Logger().Infof("Updated Role %s to be policies %#v", roleName, role.Rules) 219 _, err = kubeClient.RbacV1().Roles(teamNs).Update(role) 220 require.NoError(t, err, "Updating EnvironmentRoleBinding in ns %s with name %s", teamNs, roleBindingName) 221 222 // now lets simulate the watch... 223 err = o.UpsertRole(role) 224 225 AssertRolesInEnvironmentsContainsPolicyRule(t, kubeClient, nsNames, roleName, "", "get", "secrets") 226 } 227 228 // AssertRemoveSubject removes the subject from the slice of subjects for the given kind, ns, name or fails the test 229 func AssertRemoveSubject(t *testing.T, subjects []rbacv1.Subject, message string, kind string, ns string, name string) []rbacv1.Subject { 230 idx := -1 231 for i, subject := range subjects { 232 if subject.Kind == kind && subject.Namespace == ns && subject.Name == name { 233 idx = i 234 break 235 } 236 } 237 if idx < 0 { 238 assert.Fail(t, "Should not contain subject (%s,%s,%s) for %s - has subjects %#v", kind, ns, name, message, subjects) 239 return subjects 240 } 241 return append(subjects[0:idx], subjects[idx+1:]...) 242 } 243 244 // AssertRoleBindingsInEnvironmentsContainsSubject asserts that all the environments contain a role binding of the given name which contains the given subject 245 func AssertRoleBindingsInEnvironmentsContainsSubject(t *testing.T, kubeClient kubernetes.Interface, nsNames []string, roleBindingName string, kind string, teamNs string, newUser string) { 246 for _, ns := range nsNames { 247 roleBinding, err := kubeClient.RbacV1().RoleBindings(ns).Get(roleBindingName, metav1.GetOptions{}) 248 require.NoError(t, err, "Failed to find RoleBinding in namespace %s for name %s", ns, roleBindingName) 249 require.NotNil(t, roleBinding, "Failed to find RoleBinding in namespace %s for name %s", ns, roleBindingName) 250 251 messsage := fmt.Sprintf("RoleBinding in namespace %s for name %s", ns, roleBindingName) 252 AssertContainsSubject(t, roleBinding.Subjects, messsage, kind, teamNs, newUser) 253 } 254 } 255 256 // AssertRoleBindingsInEnvironmentsNotContainsSubject asserts that all the environments do not contain a role binding of the given name which contains the given subject 257 func AssertRoleBindingsInEnvironmentsNotContainsSubject(t *testing.T, kubeClient kubernetes.Interface, nsNames []string, roleBindingName string, kind string, teamNs string, newUser string) { 258 for _, ns := range nsNames { 259 roleBinding, err := kubeClient.RbacV1().RoleBindings(ns).Get(roleBindingName, metav1.GetOptions{}) 260 require.NoError(t, err, "Failed to find RoleBinding in namespace %s for name %s", ns, roleBindingName) 261 require.NotNil(t, roleBinding, "Failed to find RoleBinding in namespace %s for name %s", ns, roleBindingName) 262 263 messsage := fmt.Sprintf("RoleBinding in namespace %s for name %s", ns, roleBindingName) 264 AssertNotContainsSubject(t, roleBinding.Subjects, messsage, kind, teamNs, newUser) 265 } 266 } 267 268 // AssertContainsSubject asserts that the given array of subjects contains the given kind, namespace and name subject 269 func AssertContainsSubject(t *testing.T, subjects []rbacv1.Subject, message string, kind string, ns string, name string) bool { 270 for _, subject := range subjects { 271 if subject.Kind == kind && subject.Namespace == ns && subject.Name == name { 272 return true 273 } 274 } 275 log.Logger().Warnf("Does not contain Subject: (%s,%s,%s) for %s - has subjects %#v", kind, ns, name, message, subjects) 276 return assert.Fail(t, "Does not contain Subject: (%s,%s,%s) for %s - has subjects %#v", kind, ns, name, message, subjects) 277 } 278 279 // AssertNotContainsSubject asserts that the given array of subjects contains the given kind, namespace and name subject 280 func AssertNotContainsSubject(t *testing.T, subjects []rbacv1.Subject, message string, kind string, ns string, name string) bool { 281 for _, subject := range subjects { 282 if subject.Kind == kind && subject.Namespace == ns && subject.Name == name { 283 log.Logger().Warnf("Should not contain Subject (%s,%s,%s) for %s - has subjects %#v", kind, ns, name, message, subjects) 284 return assert.Fail(t, "Should not contain Subject (%s,%s,%s) for %s - has subjects %#v", kind, ns, name, message, subjects) 285 } 286 } 287 return true 288 } 289 290 // AssertRolesInEnvironmentsContainsPolicyRule asserts that all the environments contain a Role of the given name which contains the given policy rule 291 func AssertRolesInEnvironmentsContainsPolicyRule(t *testing.T, kubeClient kubernetes.Interface, nsNames []string, roleName string, apiGroup string, verb string, resource string) { 292 for _, ns := range nsNames { 293 role, err := kubeClient.RbacV1().Roles(ns).Get(roleName, metav1.GetOptions{}) 294 require.NoError(t, err, "Failed to find Role in namespace %s for name %s", ns, roleName) 295 require.NotNil(t, role, "Failed to find Role in namespace %s for name %s", ns, roleName) 296 297 messsage := fmt.Sprintf("Role in namespace %s for name %s", ns, roleName) 298 AssertContainsPolicyRule(t, role.Rules, messsage, apiGroup, verb, resource) 299 } 300 } 301 302 // AssertRolesInEnvironmentsNotContainsPolicyRule asserts that all the environments do not contain a Role of the given name which contains the given policy rule 303 func AssertRolesInEnvironmentsNotContainsPolicyRule(t *testing.T, kubeClient kubernetes.Interface, nsNames []string, roleName string, apiGroup string, verb string, resource string) { 304 for _, ns := range nsNames { 305 role, err := kubeClient.RbacV1().Roles(ns).Get(roleName, metav1.GetOptions{}) 306 require.NoError(t, err, "Failed to find RoleBinding in namespace %s for name %s", ns, roleName) 307 require.NotNil(t, role, "Failed to find RoleBinding in namespace %s for name %s", ns, roleName) 308 309 messsage := fmt.Sprintf("Role in namespace %s for name %s", ns, roleName) 310 AssertNotContainsPolicyRule(t, role.Rules, messsage, apiGroup, verb, resource) 311 } 312 } 313 314 // AssertContainsPolicyRule asserts that the given array of policy rules contains the given apiGroup, verb and resource subject 315 func AssertContainsPolicyRule(t *testing.T, rules []rbacv1.PolicyRule, message string, apiGroup string, verb string, resource string) bool { 316 for _, rule := range rules { 317 if util.StringArrayIndex(rule.APIGroups, apiGroup) >= 0 && util.StringArrayIndex(rule.Verbs, verb) >= 0 && util.StringArrayIndex(rule.Resources, resource) >= 0 { 318 return true 319 } 320 } 321 log.Logger().Warnf("Does not contain PolicyRule: (%s,%s,%s) for %s - has rules %#v", apiGroup, verb, resource, message, rules) 322 return assert.Fail(t, "Does not contain PolicyRule: (%s,%s,%s) for %s - has rules %#v", apiGroup, verb, resource, message, rules) 323 } 324 325 // AssertNotContainsPolicyRule asserts that the given array of policy rules contains the given apiGroup, verb and resource subject 326 func AssertNotContainsPolicyRule(t *testing.T, rules []rbacv1.PolicyRule, message string, apiGroup string, verb string, resource string) bool { 327 for _, rule := range rules { 328 if util.StringArrayIndex(rule.APIGroups, apiGroup) >= 0 && util.StringArrayIndex(rule.Verbs, verb) >= 0 && util.StringArrayIndex(rule.Resources, resource) >= 0 { 329 log.Logger().Warnf("Should not contain PolicyRule (%s,%s,%s) for %s - has rules %#v", apiGroup, verb, resource, message, rules) 330 return assert.Fail(t, "Should not contain PolicyRule (%s,%s,%s) for %s - has rules %#v", apiGroup, verb, resource, message, rules) 331 } 332 } 333 return true 334 }