Extend infrastructure roles handling
Postgres Operator uses infrastructure roles to provide access to a
database for external users e.g. for monitoring purposes. Such
infrastructure roles are expected to be present in the form of k8s
secrets with the following content:
    inrole1: some_encrypted_role
    password1: some_encrypted_password
    user1: some_entrypted_name
    inrole2: some_encrypted_role
    password2: some_encrypted_password
    user2: some_entrypted_name
The format of this content is implied implicitely and not flexible
enough. In case if we do not have possibility to change the format of a
secret we want to use in the Operator, we need to recreate it in this
format.
To address this lets make the format of secret content explicitely. The
idea is to introduce a new configuration option for the Operator.
    infrastructure_roles_secrets:
    - secret: k8s_secret_name
      name: some_encrypted_name
      password: some_encrypted_password
      role: some_encrypted_role
    - secret: k8s_secret_name
      name: some_encrypted_name
      password: some_encrypted_password
      role: some_encrypted_role
This would allow Operator to use any avalable secrets to prepare
infrastructure roles. To make it backward compatible simulate the old
behaviour if the new option is not present.
The new configuration option is intended be used mainly from CRD, but
it's also available via Operator ConfigMap in a limited fashion. For
ConfigMap one can put there only a string with one secret definition in
the following format (as a string):
    infrastructure_roles_secret_name: |
        secret: k8s_secret_name,
        name: some_encrypted_name,
        password: some_encrypted_password,
        role: some_encrypted_role
			
			
This commit is contained in:
		
							parent
							
								
									3b82af4b86
								
							
						
					
					
						commit
						d5b7c94ba3
					
				| 
						 | 
					@ -78,10 +78,10 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
 | 
				
			||||||
			result.InfrastructureRoles = append(
 | 
								result.InfrastructureRoles = append(
 | 
				
			||||||
				result.InfrastructureRoles,
 | 
									result.InfrastructureRoles,
 | 
				
			||||||
				&config.InfrastructureRole{
 | 
									&config.InfrastructureRole{
 | 
				
			||||||
					Secret:   secret.Secret,
 | 
										SecretName: secret.SecretName,
 | 
				
			||||||
					Name:     secret.Name,
 | 
										Name:       secret.Name,
 | 
				
			||||||
					Role:     secret.Role,
 | 
										Role:       secret.Role,
 | 
				
			||||||
					Password: secret.Password,
 | 
										Password:   secret.Password,
 | 
				
			||||||
				})
 | 
									})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -110,7 +110,7 @@ func readDecodedRole(s string) (*spec.PgUser, error) {
 | 
				
			||||||
	return &result, nil
 | 
						return &result, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var emptyNamespacedName = (spec.NamespacedName{})
 | 
					var emptyName = (spec.NamespacedName{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Return information about what secrets we need to use to create
 | 
					// Return information about what secrets we need to use to create
 | 
				
			||||||
// infrastructure roles and in which format are they. This is done in
 | 
					// infrastructure roles and in which format are they. This is done in
 | 
				
			||||||
| 
						 | 
					@ -120,7 +120,7 @@ func (c *Controller) getInfrastructureRoleDefinitions() []*config.Infrastructure
 | 
				
			||||||
	var roleDef config.InfrastructureRole
 | 
						var roleDef config.InfrastructureRole
 | 
				
			||||||
	rolesDefs := c.opConfig.InfrastructureRoles
 | 
						rolesDefs := c.opConfig.InfrastructureRoles
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if c.opConfig.InfrastructureRolesSecretName == emptyNamespacedName {
 | 
						if c.opConfig.InfrastructureRolesSecretName == emptyName {
 | 
				
			||||||
		// All the other possibilities require secret name to be present, so if
 | 
							// All the other possibilities require secret name to be present, so if
 | 
				
			||||||
		// it is not, then nothing else to be done here.
 | 
							// it is not, then nothing else to be done here.
 | 
				
			||||||
		return rolesDefs
 | 
							return rolesDefs
 | 
				
			||||||
| 
						 | 
					@ -140,8 +140,8 @@ func (c *Controller) getInfrastructureRoleDefinitions() []*config.Infrastructure
 | 
				
			||||||
		properties := strings.Split(c.opConfig.InfrastructureRolesDefs, propertySep)
 | 
							properties := strings.Split(c.opConfig.InfrastructureRolesDefs, propertySep)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		roleDef = config.InfrastructureRole{
 | 
							roleDef = config.InfrastructureRole{
 | 
				
			||||||
			Secret:   c.opConfig.InfrastructureRolesSecretName,
 | 
								SecretName: c.opConfig.InfrastructureRolesSecretName,
 | 
				
			||||||
			Template: false,
 | 
								Template:   false,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for _, property := range properties {
 | 
							for _, property := range properties {
 | 
				
			||||||
| 
						 | 
					@ -169,11 +169,11 @@ func (c *Controller) getInfrastructureRoleDefinitions() []*config.Infrastructure
 | 
				
			||||||
		// via existing definition structure and remember that it's just a
 | 
							// via existing definition structure and remember that it's just a
 | 
				
			||||||
		// template, the real values are in user1,password1,inrole1 etc.
 | 
							// template, the real values are in user1,password1,inrole1 etc.
 | 
				
			||||||
		roleDef = config.InfrastructureRole{
 | 
							roleDef = config.InfrastructureRole{
 | 
				
			||||||
			Secret:   c.opConfig.InfrastructureRolesSecretName,
 | 
								SecretName: c.opConfig.InfrastructureRolesSecretName,
 | 
				
			||||||
			Name:     "user",
 | 
								Name:       "user",
 | 
				
			||||||
			Password: "password",
 | 
								Password:   "password",
 | 
				
			||||||
			Role:     "inrole",
 | 
								Role:       "inrole",
 | 
				
			||||||
			Template: true,
 | 
								Template:   true,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -201,7 +201,7 @@ func (c *Controller) getInfrastructureRoles(
 | 
				
			||||||
	// current implementation is an empty rolesSecrets slice or all its items
 | 
						// current implementation is an empty rolesSecrets slice or all its items
 | 
				
			||||||
	// are empty.
 | 
						// are empty.
 | 
				
			||||||
	for _, role := range rolesSecrets {
 | 
						for _, role := range rolesSecrets {
 | 
				
			||||||
		if role.Secret != emptyNamespacedName {
 | 
							if role.SecretName != emptyName {
 | 
				
			||||||
			noRolesProvided = false
 | 
								noRolesProvided = false
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -258,10 +258,10 @@ func (c *Controller) getInfrastructureRole(
 | 
				
			||||||
	infraRole *config.InfrastructureRole) (
 | 
						infraRole *config.InfrastructureRole) (
 | 
				
			||||||
	[]spec.PgUser, error) {
 | 
						[]spec.PgUser, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rolesSecret := infraRole.Secret
 | 
						rolesSecret := infraRole.SecretName
 | 
				
			||||||
	roles := []spec.PgUser{}
 | 
						roles := []spec.PgUser{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if rolesSecret == (spec.NamespacedName{}) {
 | 
						if rolesSecret == emptyName {
 | 
				
			||||||
		// we don't have infrastructure roles defined, bail out
 | 
							// we don't have infrastructure roles defined, bail out
 | 
				
			||||||
		return nil, nil
 | 
							return nil, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -312,7 +312,12 @@ func (c *Controller) getInfrastructureRole(
 | 
				
			||||||
				delete(secretData, key)
 | 
									delete(secretData, key)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			roles = append(roles, t)
 | 
								if t.Valid() {
 | 
				
			||||||
 | 
									roles = append(roles, t)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									msg := "infrastructure role %q is not complete and ignored"
 | 
				
			||||||
 | 
									c.logger.Warningf(msg, t)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		roleDescr := &spec.PgUser{Origin: spec.RoleOriginInfrastructure}
 | 
							roleDescr := &spec.PgUser{Origin: spec.RoleOriginInfrastructure}
 | 
				
			||||||
| 
						 | 
					@ -327,6 +332,15 @@ func (c *Controller) getInfrastructureRole(
 | 
				
			||||||
			roleDescr.MemberOf = append(roleDescr.MemberOf, string(secretData[infraRole.Role]))
 | 
								roleDescr.MemberOf = append(roleDescr.MemberOf, string(secretData[infraRole.Role]))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if roleDescr.Valid() {
 | 
				
			||||||
 | 
								roles = append(roles, *roleDescr)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								msg := "infrastructure role %q is not complete and ignored"
 | 
				
			||||||
 | 
								c.logger.Warningf(msg, roleDescr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return nil, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if roleDescr.Name == "" {
 | 
							if roleDescr.Name == "" {
 | 
				
			||||||
			msg := "infrastructure role %q has no name defined and is ignored"
 | 
								msg := "infrastructure role %q has no name defined and is ignored"
 | 
				
			||||||
			c.logger.Warningf(msg, roleDescr.Name)
 | 
								c.logger.Warningf(msg, roleDescr.Name)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -132,11 +132,11 @@ func TestOldInfrastructureRoleFormat(t *testing.T) {
 | 
				
			||||||
		roles, errors := utilTestController.getInfrastructureRoles(
 | 
							roles, errors := utilTestController.getInfrastructureRoles(
 | 
				
			||||||
			[]*config.InfrastructureRole{
 | 
								[]*config.InfrastructureRole{
 | 
				
			||||||
				&config.InfrastructureRole{
 | 
									&config.InfrastructureRole{
 | 
				
			||||||
					Secret:   test.secretName,
 | 
										SecretName: test.secretName,
 | 
				
			||||||
					Name:     "user",
 | 
										Name:       "user",
 | 
				
			||||||
					Password: "password",
 | 
										Password:   "password",
 | 
				
			||||||
					Role:     "inrole",
 | 
										Role:       "inrole",
 | 
				
			||||||
					Template: true,
 | 
										Template:   true,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -193,6 +193,7 @@ func TestNewInfrastructureRoleFormat(t *testing.T) {
 | 
				
			||||||
					Origin:   spec.RoleOriginInfrastructure,
 | 
										Origin:   spec.RoleOriginInfrastructure,
 | 
				
			||||||
					Password: b64.StdEncoding.EncodeToString([]byte("password")),
 | 
										Password: b64.StdEncoding.EncodeToString([]byte("password")),
 | 
				
			||||||
					MemberOf: nil,
 | 
										MemberOf: nil,
 | 
				
			||||||
 | 
										Flags:    []string{"createdb"},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			nil,
 | 
								nil,
 | 
				
			||||||
| 
						 | 
					@ -230,11 +231,11 @@ func TestNewInfrastructureRoleFormat(t *testing.T) {
 | 
				
			||||||
		definitions := []*config.InfrastructureRole{}
 | 
							definitions := []*config.InfrastructureRole{}
 | 
				
			||||||
		for _, secret := range test.secrets {
 | 
							for _, secret := range test.secrets {
 | 
				
			||||||
			definitions = append(definitions, &config.InfrastructureRole{
 | 
								definitions = append(definitions, &config.InfrastructureRole{
 | 
				
			||||||
				Secret:   secret,
 | 
									SecretName: secret,
 | 
				
			||||||
				Name:     "user",
 | 
									Name:       "user",
 | 
				
			||||||
				Password: "password",
 | 
									Password:   "password",
 | 
				
			||||||
				Role:     "inrole",
 | 
									Role:       "inrole",
 | 
				
			||||||
				Template: false,
 | 
									Template:   false,
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -282,7 +283,7 @@ func TestInfrastructureRoleDefinitions(t *testing.T) {
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			[]*config.InfrastructureRole{
 | 
								[]*config.InfrastructureRole{
 | 
				
			||||||
				&config.InfrastructureRole{
 | 
									&config.InfrastructureRole{
 | 
				
			||||||
					Secret: spec.NamespacedName{
 | 
										SecretName: spec.NamespacedName{
 | 
				
			||||||
						Namespace: v1.NamespaceDefault,
 | 
											Namespace: v1.NamespaceDefault,
 | 
				
			||||||
						Name:      testInfrastructureRolesNewSecretName,
 | 
											Name:      testInfrastructureRolesNewSecretName,
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
| 
						 | 
					@ -296,7 +297,7 @@ func TestInfrastructureRoleDefinitions(t *testing.T) {
 | 
				
			||||||
			"",
 | 
								"",
 | 
				
			||||||
			[]*config.InfrastructureRole{
 | 
								[]*config.InfrastructureRole{
 | 
				
			||||||
				&config.InfrastructureRole{
 | 
									&config.InfrastructureRole{
 | 
				
			||||||
					Secret: spec.NamespacedName{
 | 
										SecretName: spec.NamespacedName{
 | 
				
			||||||
						Namespace: v1.NamespaceDefault,
 | 
											Namespace: v1.NamespaceDefault,
 | 
				
			||||||
						Name:      testInfrastructureRolesNewSecretName,
 | 
											Name:      testInfrastructureRolesNewSecretName,
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
| 
						 | 
					@ -317,7 +318,7 @@ func TestInfrastructureRoleDefinitions(t *testing.T) {
 | 
				
			||||||
			"",
 | 
								"",
 | 
				
			||||||
			[]*config.InfrastructureRole{
 | 
								[]*config.InfrastructureRole{
 | 
				
			||||||
				&config.InfrastructureRole{
 | 
									&config.InfrastructureRole{
 | 
				
			||||||
					Secret: spec.NamespacedName{
 | 
										SecretName: spec.NamespacedName{
 | 
				
			||||||
						Namespace: v1.NamespaceDefault,
 | 
											Namespace: v1.NamespaceDefault,
 | 
				
			||||||
						Name:      testInfrastructureRolesOldSecretName,
 | 
											Name:      testInfrastructureRolesOldSecretName,
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
| 
						 | 
					@ -338,7 +339,7 @@ func TestInfrastructureRoleDefinitions(t *testing.T) {
 | 
				
			||||||
			"name: test-user, password: test-password, role: test-role",
 | 
								"name: test-user, password: test-password, role: test-role",
 | 
				
			||||||
			[]*config.InfrastructureRole{
 | 
								[]*config.InfrastructureRole{
 | 
				
			||||||
				&config.InfrastructureRole{
 | 
									&config.InfrastructureRole{
 | 
				
			||||||
					Secret: spec.NamespacedName{
 | 
										SecretName: spec.NamespacedName{
 | 
				
			||||||
						Namespace: v1.NamespaceDefault,
 | 
											Namespace: v1.NamespaceDefault,
 | 
				
			||||||
						Name:      testInfrastructureRolesOldSecretName,
 | 
											Name:      testInfrastructureRolesOldSecretName,
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,6 +55,10 @@ type PgUser struct {
 | 
				
			||||||
	AdminRole  string            `yaml:"admin_role"`
 | 
						AdminRole  string            `yaml:"admin_role"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (user *PgUser) Valid() bool {
 | 
				
			||||||
 | 
						return user.Name != "" && user.Password != ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PgUserMap maps user names to the definitions.
 | 
					// PgUserMap maps user names to the definitions.
 | 
				
			||||||
type PgUserMap map[string]PgUser
 | 
					type PgUserMap map[string]PgUser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,7 +54,7 @@ type Resources struct {
 | 
				
			||||||
type InfrastructureRole struct {
 | 
					type InfrastructureRole struct {
 | 
				
			||||||
	// Name of a secret which describes the role, and optionally name of a
 | 
						// Name of a secret which describes the role, and optionally name of a
 | 
				
			||||||
	// configmap with an extra information
 | 
						// configmap with an extra information
 | 
				
			||||||
	Secret spec.NamespacedName
 | 
						SecretName spec.NamespacedName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Name     string
 | 
						Name     string
 | 
				
			||||||
	Password string
 | 
						Password string
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -325,7 +325,7 @@ func (c *mockConfigMap) Get(ctx context.Context, name string, options metav1.Get
 | 
				
			||||||
	newFormatConfigmap := &v1.ConfigMap{}
 | 
						newFormatConfigmap := &v1.ConfigMap{}
 | 
				
			||||||
	newFormatConfigmap.Name = "testcluster"
 | 
						newFormatConfigmap.Name = "testcluster"
 | 
				
			||||||
	newFormatConfigmap.Data = map[string]string{
 | 
						newFormatConfigmap.Data = map[string]string{
 | 
				
			||||||
		"new-foobar": "{}",
 | 
							"new-foobar": "{\"user_flags\": [\"createdb\"]}",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	configmaps := map[string]*v1.ConfigMap{
 | 
						configmaps := map[string]*v1.ConfigMap{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue