Improve infrastructure role definitions (#208)

Enhance definitions of infrastructure roles by allowing membership in multiple roles, role options and per-role configuration to be specified in the infrastructure role configmap, which must have the same name as the infrastructure role secret. See manifests/infrastructure-roles-configmap.yaml for the examples and updated README for the description of different types of database roles supposed by the operator and their purposes.

Change the logic of merging infrastructure roles with the manifest roles when they have the same name, to return the infrastructure role unchanged instead of merging. Previously, we used to propagate flags from the manifest role to the resulting infrastructure one, as there were no way to define flags for the infrastructure role; however, this is not the case anymore.

Code review and tests by @erthalion
This commit is contained in:
Oleksii Kliukin
2018-04-04 17:21:36 +02:00
committed by GitHub
parent d264be9faa
commit 26db91c53e
10 changed files with 261 additions and 51 deletions
+38 -25
View File
@@ -674,24 +674,18 @@ func (c *Cluster) initRobotUsers() error {
if err != nil {
return fmt.Errorf("invalid flags for user %q: %v", username, err)
}
if _, present := c.pgUsers[username]; !present {
c.pgUsers[username] = spec.PgUser{
Origin: spec.RoleOriginManifest,
Name: username,
Password: util.RandomPassword(constants.PasswordLength),
Flags: flags,
}
newRole := spec.PgUser{
Origin: spec.RoleOriginManifest,
Name: username,
Password: util.RandomPassword(constants.PasswordLength),
Flags: flags,
}
if currentRole, present := c.pgUsers[username]; present {
c.pgUsers[username] = c.resolveNameConflict(&currentRole, &newRole)
} else {
// avoid overwriting the password if the user is already there. The flags should be
// merged here, but since there is no mechanism to define them for non-robot roles
// they are assigned from the robot user.
c.logger.Debugf("merging manifest and infrastructure user %q data", username)
user := c.pgUsers[username]
user.Flags = flags
c.pgUsers[username] = user
c.pgUsers[username] = newRole
}
}
return nil
}
@@ -715,41 +709,60 @@ func (c *Cluster) initHumanUsers() error {
}
}
if _, present := c.pgUsers[username]; present {
c.logger.Warnf("overwriting existing user %q with the data from the teams API", username)
}
c.pgUsers[username] = spec.PgUser{
newRole := spec.PgUser{
Origin: spec.RoleOriginTeamsAPI,
Name: username,
Flags: flags,
MemberOf: memberOf,
Parameters: c.OpConfig.TeamAPIRoleConfiguration,
}
if currentRole, present := c.pgUsers[username]; present {
c.pgUsers[username] = c.resolveNameConflict(&currentRole, &newRole)
} else {
c.pgUsers[username] = newRole
}
}
return nil
}
func (c *Cluster) initInfrastructureRoles() error {
// add infrastucture roles from the operator's definition
for username, data := range c.InfrastructureRoles {
// add infrastructure roles from the operator's definition
for username, newRole := range c.InfrastructureRoles {
if !isValidUsername(username) {
return fmt.Errorf("invalid username: '%v'", username)
}
if c.shouldAvoidProtectedOrSystemRole(username, "infrastructure role") {
continue
}
flags, err := normalizeUserFlags(data.Flags)
flags, err := normalizeUserFlags(newRole.Flags)
if err != nil {
return fmt.Errorf("invalid flags for user '%v': %v", username, err)
}
data.Flags = flags
c.pgUsers[username] = data
newRole.Flags = flags
if currentRole, present := c.pgUsers[username]; present {
c.pgUsers[username] = c.resolveNameConflict(&currentRole, &newRole)
} else {
c.pgUsers[username] = newRole
}
}
return nil
}
// resolves naming conflicts between existing and new roles by chosing either of them.
func (c *Cluster) resolveNameConflict(currentRole, newRole *spec.PgUser) (result spec.PgUser) {
if newRole.Origin >= currentRole.Origin {
result = *newRole
} else {
result = *currentRole
}
c.logger.Debugf("resolved a conflict of role %q between %s and %s to %s",
newRole.Name, newRole.Origin, currentRole.Origin, result.Origin)
return
}
func (c *Cluster) shouldAvoidProtectedOrSystemRole(username, purpose string) bool {
if c.isProtectedUsername(username) {
c.logger.Warnf("cannot initialize a new %s with the name of the protected user %q", purpose, username)
+2 -3
View File
@@ -33,9 +33,8 @@ func TestInitRobotUsers(t *testing.T) {
}{
{
manifestUsers: map[string]spec.UserFlags{"foo": {"superuser", "createdb"}},
infraRoles: map[string]spec.PgUser{"foo": {Origin: spec.RoleOriginManifest, Name: "foo", Password: "bar"}},
result: map[string]spec.PgUser{"foo": {Origin: spec.RoleOriginManifest,
Name: "foo", Password: "bar", Flags: []string{"CREATEDB", "LOGIN", "SUPERUSER"}}},
infraRoles: map[string]spec.PgUser{"foo": {Origin: spec.RoleOriginInfrastructure, Name: "foo", Password: "bar"}},
result: map[string]spec.PgUser{"foo": {Origin: spec.RoleOriginInfrastructure, Name: "foo", Password: "bar"}},
err: nil,
},
{