Skip to content

Commit

Permalink
Merge pull request #143 from reddec/feature/template-secrets
Browse files Browse the repository at this point in the history
Add support for template secrets
  • Loading branch information
hitman99 authored Dec 20, 2023
2 parents 76f09b1 + 815fb2e commit aaa3af7
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 14 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ None
* Creates Kubernetes secret with postgres_uri in the same namespace as CR
* Support for AWS RDS and Azure Database for PostgresSQL
* Support for managing CRs in dynamically created namespaces
* Template secret values

## Cloud specific configuration

Expand Down Expand Up @@ -173,6 +174,8 @@ spec:
privileges: OWNER # Can be OWNER/READ/WRITE
annotations: # Annotations to be propagated to the secrets metadata section (optional)
foo: "bar"
secretTemplate: # Output secrets can be customized using standard Go templates
PQ_URL: "host={{.Host}} user={{.Role}} password={{.Password}} dbname={{.Database}}"
```

This creates a user role `username-<hash>` and grants role `test-db-group`, `test-db-writer` or `test-db-reader` depending on `privileges` property. Its credentials are put in secret `my-secret-my-db-user` (unless `KEEP_SECRET_NAME` is enabled).
Expand Down Expand Up @@ -203,6 +206,21 @@ With the help of annotations it is possible to create annotation-based copies of

For more information and an example, see [kubernetes-replicator#pull-based-replication](https://github.com/mittwald/kubernetes-replicator#pull-based-replication)

#### Template Use Case

Users can specify the structure and content of secrets based on their unique requirements using standard
[Go templates](https://pkg.go.dev/text/template#hdr-Actions). This flexibility allows for a more tailored approach to
meeting the specific needs of different applications.

Available context:

| Variable | Meaning |
|-------------|--------------------------|
| `.Host` | Database host |
| `.Role` | Generated user/role name |
| `.Database` | Referenced database name |
| `.Password` | Generated role password |

### Contribution

You can contribute to this project by opening a PR to merge to `master`, or one of the `vX.X.X` branches.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ spec:
spec:
description: PostgresUserSpec defines the desired state of PostgresUser
properties:
annotations:
additionalProperties:
type: string
type: object
database:
type: string
privileges:
Expand All @@ -39,6 +43,10 @@ spec:
type: string
secretName:
type: string
template:
additionalProperties:
type: string
type: object
required:
- database
- role
Expand Down
8 changes: 8 additions & 0 deletions deploy/crds/db.movetokube.com_postgresusers_crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ spec:
spec:
description: PostgresUserSpec defines the desired state of PostgresUser
properties:
annotations:
additionalProperties:
type: string
type: object
database:
type: string
privileges:
Expand All @@ -39,6 +43,10 @@ spec:
type: string
secretName:
type: string
secretTemplate:
additionalProperties:
type: string
type: object
required:
- database
- role
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/db/v1alpha1/postgresuser_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ type PostgresUserSpec struct {
Database string `json:"database"`
SecretName string `json:"secretName"`
// +optional
SecretTemplate map[string]string `json:"secretTemplate,omitempty"` // key-value, where key is secret field, value is go template
// +optional
Privileges string `json:"privileges"`
// +optional
Annotations map[string]string `json:"annotations,omitempty"`
Expand Down
17 changes: 16 additions & 1 deletion pkg/apis/db/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

73 changes: 60 additions & 13 deletions pkg/controller/postgresuser/postgresuser_controller.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package postgresuser

import (
"bytes"
"context"
goerr "errors"
"fmt"
"text/template"

"github.com/movetokube/postgres-operator/pkg/config"

Expand Down Expand Up @@ -221,7 +223,10 @@ func (r *ReconcilePostgresUser) Reconcile(request reconcile.Request) (reconcile.
return r.requeue(instance, err)
}

secret := r.newSecretForCR(instance, role, password, login)
secret, err := r.newSecretForCR(instance, role, password, login)
if err != nil {
return r.requeue(instance, err)
}

// Set PostgresUser instance as the owner and controller
if err := controllerutil.SetControllerReference(instance, secret, r.scheme); err != nil {
Expand Down Expand Up @@ -270,7 +275,7 @@ func (r *ReconcilePostgresUser) addFinalizer(reqLogger logr.Logger, m *dbv1alpha
return nil
}

func (r *ReconcilePostgresUser) newSecretForCR(cr *dbv1alpha1.PostgresUser, role, password, login string) *corev1.Secret {
func (r *ReconcilePostgresUser) newSecretForCR(cr *dbv1alpha1.PostgresUser, role, password, login string) (*corev1.Secret, error) {
pgUserUrl := fmt.Sprintf("postgresql://%s:%s@%s/%s", role, password, r.pgHost, cr.Status.DatabaseName)
pgJDBCUrl := fmt.Sprintf("jdbc:postgresql://%s/%s", r.pgHost, cr.Status.DatabaseName)
pgDotnetUrl := fmt.Sprintf("User ID=%s;Password=%s;Host=%s;Port=5432;Database=%s;", role, password, r.pgHost, cr.Status.DatabaseName)
Expand All @@ -283,24 +288,40 @@ func (r *ReconcilePostgresUser) newSecretForCR(cr *dbv1alpha1.PostgresUser, role
name = cr.Spec.SecretName
}

templateData, err := renderTemplate(cr.Spec.SecretTemplate, templateContext{
Role: role,
Host: r.pgHost,
Database: cr.Status.DatabaseName,
Password: password,
})
if err != nil {
return nil, fmt.Errorf("render templated keys: %w", err)
}

data := map[string][]byte{
"POSTGRES_URL": []byte(pgUserUrl),
"POSTGRES_JDBC_URL": []byte(pgJDBCUrl),
"POSTGRES_DOTNET_URL": []byte(pgDotnetUrl),
"HOST": []byte(r.pgHost),
"DATABASE_NAME": []byte(cr.Status.DatabaseName),
"ROLE": []byte(role),
"PASSWORD": []byte(password),
"LOGIN": []byte(login),
}
// templates may override standard keys
for k, v := range templateData {
data[k] = v
}

return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: cr.Namespace,
Labels: labels,
Annotations: annotations,
},
Data: map[string][]byte{
"POSTGRES_URL": []byte(pgUserUrl),
"POSTGRES_JDBC_URL": []byte(pgJDBCUrl),
"POSTGRES_DOTNET_URL": []byte(pgDotnetUrl),
"HOST": []byte(r.pgHost),
"DATABASE_NAME": []byte(cr.Status.DatabaseName),
"ROLE": []byte(role),
"PASSWORD": []byte(password),
"LOGIN": []byte(login),
},
}
Data: data,
}, nil
}

func (r *ReconcilePostgresUser) requeue(cr *dbv1alpha1.PostgresUser, reason error) (reconcile.Result, error) {
Expand Down Expand Up @@ -354,3 +375,29 @@ func (r *ReconcilePostgresUser) addOwnerRef(reqLogger logr.Logger, instance *dbv
err = r.client.Update(context.TODO(), instance)
return err
}

type templateContext struct {
Host string
Role string
Database string
Password string
}

func renderTemplate(data map[string]string, tc templateContext) (map[string][]byte, error) {
if len(data) == 0 {
return nil, nil
}
var out = make(map[string][]byte, len(data))
for key, templ := range data {
parsed, err := template.New("").Parse(templ)
if err != nil {
return nil, fmt.Errorf("parse template %q: %w", key, err)
}
var content bytes.Buffer
if err := parsed.Execute(&content, tc); err != nil {
return nil, fmt.Errorf("execute template %q: %w", key, err)
}
out[key] = content.Bytes()
}
return out, nil
}

0 comments on commit aaa3af7

Please sign in to comment.