Skip to content

Commit

Permalink
New controller watching config maps named: image-controller-additiona…
Browse files Browse the repository at this point in the history
…l-users

which creates team in quay.io for users in the config map, and grants
permissions to repositories in all imageRepositories

STONEBLD-2667

Signed-off-by: Robert Cerven <[email protected]>
  • Loading branch information
rcerven committed Aug 19, 2024
1 parent 338fc0d commit 5f2aa98
Show file tree
Hide file tree
Showing 9 changed files with 681 additions and 48 deletions.
3 changes: 2 additions & 1 deletion config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,5 @@ rules:
- get
- list
- watch

- patch
- update
30 changes: 15 additions & 15 deletions controllers/imagerepository_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,30 +448,30 @@ func (r *ImageRepositoryReconciler) GrantAdditionalRepositoryAccess(ctx context.
log.Error(err, "failed to read config map with additional users", "ConfigMapName", additionalUsersConfigMapName, l.Action, l.ActionView)
return err
}
additionalUsersStr, usersExist := additionalUsersConfigMap.Data[additionalUsersConfigMapKey]
_, usersExist := additionalUsersConfigMap.Data[additionalUsersConfigMapKey]
if !usersExist {
log.Info("Config map with additional users doesn't have the key", "ConfigMapName", additionalUsersConfigMapName, "ConfigMapKey", additionalUsersConfigMapKey, l.Action, l.ActionView)
return nil
}

additionalUsers := strings.Fields(strings.TrimSpace(additionalUsersStr))
log.Info("Additional users configured in config map", "AdditionalUsers", additionalUsers)

imageRepositoryName := imageRepository.Spec.Image.Name
teamName := fmt.Sprintf("%s-team", imageRepository.Namespace)

for _, user := range additionalUsers {
err := r.QuayClient.AddPermissionsForRepositoryToAccount(r.QuayOrganization, imageRepositoryName, user, false, false)
if err != nil {
if strings.Contains(err.Error(), "Invalid username:") {
log.Info("failed to add permissions for account, because it doesn't exist", "AccountName", user)
continue
}
// get team, if team doesn't exist it will be created, we don't care about users as that will be taken care of by config map controller
// so in this case if config map exists, team already exists as well with appropriate users
log.Info("Ensure team", "TeamName", teamName)
if _, err := r.QuayClient.EnsureTeam(r.QuayOrganization, teamName); err != nil {
log.Error(err, "failed to get or create team", "TeamName", teamName, l.Action, l.ActionView)
return err
}

log.Error(err, "failed to add permissions for account", "AccountName", user, l.Action, l.ActionUpdate, l.Audit, "true")
return err
}
log.Info("Additional user access was granted for", "UserName", user)
// add repo permission to the team
log.Info("Adding repository permission to the team", "TeamName", teamName, "RepositoryName", imageRepositoryName)
if err := r.QuayClient.AddReadPermissionsForRepositoryToTeam(r.QuayOrganization, imageRepositoryName, teamName); err != nil {
log.Error(err, "failed to grant repo permission to the team", "TeamName", teamName, "RepositoryName", imageRepositoryName, l.Action, l.ActionAdd)
return err
}

return nil
}

Expand Down
53 changes: 36 additions & 17 deletions controllers/imagerepository_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ var _ = Describe("Image repository controller", func() {
createServiceAccount(defaultNamespace, buildPipelineServiceAccountName)
})

assertProvisionRepository := func(updateComponentAnnotation bool, additionalUser string) {
assertProvisionRepository := func(updateComponentAnnotation, grantRepoPermission bool) {
isCreateRepositoryInvoked := false
quay.CreateRepositoryFunc = func(repository quay.RepositoryRequest) (*quay.Repository, error) {
defer GinkgoRecover()
Expand Down Expand Up @@ -506,22 +506,37 @@ var _ = Describe("Image repository controller", func() {
defer GinkgoRecover()
Expect(organization).To(Equal(quay.TestQuayOrg))
Expect(imageRepository).To(Equal(expectedImageName))

if isRobot {
Expect(strings.HasPrefix(accountName, expectedRobotAccountPrefix)).To(BeTrue())
if strings.HasSuffix(accountName, "_pull") {
Expect(isWrite).To(BeFalse())
isAddPullPermissionsToAccountInvoked = true
} else {
Expect(isWrite).To(BeTrue())
isAddPushPermissionsToAccountInvoked = true
}
} else {
Expect(accountName).To(Equal(additionalUser))
Expect(strings.HasPrefix(accountName, expectedRobotAccountPrefix)).To(BeTrue())
if strings.HasSuffix(accountName, "_pull") {
Expect(isWrite).To(BeFalse())
isAddPullPermissionsToAccountInvoked = true
} else {
Expect(isWrite).To(BeTrue())
isAddPushPermissionsToAccountInvoked = true
}
return nil
}
isEnsureTeamInvoked := false
isAddReadPermissionsForRepositoryToTeamInvoked := false
if grantRepoPermission {
quay.EnsureTeamFunc = func(organization, teamName string) ([]quay.Member, error) {
defer GinkgoRecover()
Expect(organization).To(Equal(quay.TestQuayOrg))
expectedTeamName := fmt.Sprintf("%s-team", resourceKey.Namespace)
Expect(teamName).To(Equal(expectedTeamName))
isEnsureTeamInvoked = true
return nil, nil
}
quay.AddReadPermissionsForRepositoryToTeamFunc = func(organization, imageRepository, teamName string) error {
defer GinkgoRecover()
Expect(organization).To(Equal(quay.TestQuayOrg))
Expect(imageRepository).To(Equal(expectedImageName))
expectedTeamName := fmt.Sprintf("%s-team", resourceKey.Namespace)
Expect(teamName).To(Equal(expectedTeamName))
isAddReadPermissionsForRepositoryToTeamInvoked = true
return nil
}
}
isCreateNotificationInvoked := false
quay.CreateNotificationFunc = func(organization, repository string, notification quay.Notification) (*quay.Notification, error) {
isCreateNotificationInvoked = true
Expand Down Expand Up @@ -574,6 +589,10 @@ var _ = Describe("Image repository controller", func() {
Eventually(func() bool { return isAddPushPermissionsToAccountInvoked }, timeout, interval).Should(BeTrue())
Eventually(func() bool { return isAddPullPermissionsToAccountInvoked }, timeout, interval).Should(BeTrue())
Eventually(func() bool { return isCreateNotificationInvoked }, timeout, interval).Should(BeTrue())
if grantRepoPermission {
Eventually(func() bool { return isEnsureTeamInvoked }, timeout, interval).Should(BeTrue())
Eventually(func() bool { return isAddReadPermissionsForRepositoryToTeamInvoked }, timeout, interval).Should(BeTrue())
}

waitImageRepositoryFinalizerOnImageRepository(resourceKey)

Expand Down Expand Up @@ -654,7 +673,7 @@ var _ = Describe("Image repository controller", func() {
}

It("should provision image repository for component, without update component annotation", func() {
assertProvisionRepository(false, "")
assertProvisionRepository(false, false)

quay.DeleteRobotAccountFunc = func(organization, robotAccountName string) (bool, error) {
return true, nil
Expand All @@ -666,10 +685,10 @@ var _ = Describe("Image repository controller", func() {
deleteImageRepository(resourceKey)
})

It("should provision image repository for component, with update component annotation and add additional user from config map", func() {
It("should provision image repository for component, with update component annotation and grant permission to team", func() {
usersConfigMapKey := types.NamespacedName{Name: additionalUsersConfigMapName, Namespace: resourceKey.Namespace}
createUsersConfigMap(usersConfigMapKey, []string{"user1"})
assertProvisionRepository(true, "user1")
assertProvisionRepository(true, true)

quay.DeleteRobotAccountFunc = func(organization, robotAccountName string) (bool, error) {
return true, nil
Expand All @@ -683,7 +702,7 @@ var _ = Describe("Image repository controller", func() {
})

It("should provision image repository for component, with update component annotation", func() {
assertProvisionRepository(true, "")
assertProvisionRepository(true, false)
})

It("should regenerate tokens and update secrets", func() {
Expand Down
Loading

0 comments on commit 5f2aa98

Please sign in to comment.