Skip to content

Commit aaa3af7

Browse files
authored
Merge pull request movetokube#143 from reddec/feature/template-secrets
Add support for template secrets
2 parents 76f09b1 + 815fb2e commit aaa3af7

File tree

6 files changed

+112
-14
lines changed

6 files changed

+112
-14
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ None
3939
* Creates Kubernetes secret with postgres_uri in the same namespace as CR
4040
* Support for AWS RDS and Azure Database for PostgresSQL
4141
* Support for managing CRs in dynamically created namespaces
42+
* Template secret values
4243

4344
## Cloud specific configuration
4445

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

178181
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).
@@ -203,6 +206,21 @@ With the help of annotations it is possible to create annotation-based copies of
203206

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

209+
#### Template Use Case
210+
211+
Users can specify the structure and content of secrets based on their unique requirements using standard
212+
[Go templates](https://pkg.go.dev/text/template#hdr-Actions). This flexibility allows for a more tailored approach to
213+
meeting the specific needs of different applications.
214+
215+
Available context:
216+
217+
| Variable | Meaning |
218+
|-------------|--------------------------|
219+
| `.Host` | Database host |
220+
| `.Role` | Generated user/role name |
221+
| `.Database` | Referenced database name |
222+
| `.Password` | Generated role password |
223+
206224
### Contribution
207225

208226
You can contribute to this project by opening a PR to merge to `master`, or one of the `vX.X.X` branches.

charts/ext-postgres-operator/crds/db.movetokube.com_postgresusers_crd.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ spec:
3131
spec:
3232
description: PostgresUserSpec defines the desired state of PostgresUser
3333
properties:
34+
annotations:
35+
additionalProperties:
36+
type: string
37+
type: object
3438
database:
3539
type: string
3640
privileges:
@@ -39,6 +43,10 @@ spec:
3943
type: string
4044
secretName:
4145
type: string
46+
template:
47+
additionalProperties:
48+
type: string
49+
type: object
4250
required:
4351
- database
4452
- role

deploy/crds/db.movetokube.com_postgresusers_crd.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ spec:
3131
spec:
3232
description: PostgresUserSpec defines the desired state of PostgresUser
3333
properties:
34+
annotations:
35+
additionalProperties:
36+
type: string
37+
type: object
3438
database:
3539
type: string
3640
privileges:
@@ -39,6 +43,10 @@ spec:
3943
type: string
4044
secretName:
4145
type: string
46+
secretTemplate:
47+
additionalProperties:
48+
type: string
49+
type: object
4250
required:
4351
- database
4452
- role

pkg/apis/db/v1alpha1/postgresuser_types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ type PostgresUserSpec struct {
1414
Database string `json:"database"`
1515
SecretName string `json:"secretName"`
1616
// +optional
17+
SecretTemplate map[string]string `json:"secretTemplate,omitempty"` // key-value, where key is secret field, value is go template
18+
// +optional
1719
Privileges string `json:"privileges"`
1820
// +optional
1921
Annotations map[string]string `json:"annotations,omitempty"`

pkg/apis/db/v1alpha1/zz_generated.deepcopy.go

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/controller/postgresuser/postgresuser_controller.go

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package postgresuser
22

33
import (
4+
"bytes"
45
"context"
56
goerr "errors"
67
"fmt"
8+
"text/template"
79

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

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

224-
secret := r.newSecretForCR(instance, role, password, login)
226+
secret, err := r.newSecretForCR(instance, role, password, login)
227+
if err != nil {
228+
return r.requeue(instance, err)
229+
}
225230

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

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

291+
templateData, err := renderTemplate(cr.Spec.SecretTemplate, templateContext{
292+
Role: role,
293+
Host: r.pgHost,
294+
Database: cr.Status.DatabaseName,
295+
Password: password,
296+
})
297+
if err != nil {
298+
return nil, fmt.Errorf("render templated keys: %w", err)
299+
}
300+
301+
data := map[string][]byte{
302+
"POSTGRES_URL": []byte(pgUserUrl),
303+
"POSTGRES_JDBC_URL": []byte(pgJDBCUrl),
304+
"POSTGRES_DOTNET_URL": []byte(pgDotnetUrl),
305+
"HOST": []byte(r.pgHost),
306+
"DATABASE_NAME": []byte(cr.Status.DatabaseName),
307+
"ROLE": []byte(role),
308+
"PASSWORD": []byte(password),
309+
"LOGIN": []byte(login),
310+
}
311+
// templates may override standard keys
312+
for k, v := range templateData {
313+
data[k] = v
314+
}
315+
286316
return &corev1.Secret{
287317
ObjectMeta: metav1.ObjectMeta{
288318
Name: name,
289319
Namespace: cr.Namespace,
290320
Labels: labels,
291321
Annotations: annotations,
292322
},
293-
Data: map[string][]byte{
294-
"POSTGRES_URL": []byte(pgUserUrl),
295-
"POSTGRES_JDBC_URL": []byte(pgJDBCUrl),
296-
"POSTGRES_DOTNET_URL": []byte(pgDotnetUrl),
297-
"HOST": []byte(r.pgHost),
298-
"DATABASE_NAME": []byte(cr.Status.DatabaseName),
299-
"ROLE": []byte(role),
300-
"PASSWORD": []byte(password),
301-
"LOGIN": []byte(login),
302-
},
303-
}
323+
Data: data,
324+
}, nil
304325
}
305326

306327
func (r *ReconcilePostgresUser) requeue(cr *dbv1alpha1.PostgresUser, reason error) (reconcile.Result, error) {
@@ -354,3 +375,29 @@ func (r *ReconcilePostgresUser) addOwnerRef(reqLogger logr.Logger, instance *dbv
354375
err = r.client.Update(context.TODO(), instance)
355376
return err
356377
}
378+
379+
type templateContext struct {
380+
Host string
381+
Role string
382+
Database string
383+
Password string
384+
}
385+
386+
func renderTemplate(data map[string]string, tc templateContext) (map[string][]byte, error) {
387+
if len(data) == 0 {
388+
return nil, nil
389+
}
390+
var out = make(map[string][]byte, len(data))
391+
for key, templ := range data {
392+
parsed, err := template.New("").Parse(templ)
393+
if err != nil {
394+
return nil, fmt.Errorf("parse template %q: %w", key, err)
395+
}
396+
var content bytes.Buffer
397+
if err := parsed.Execute(&content, tc); err != nil {
398+
return nil, fmt.Errorf("execute template %q: %w", key, err)
399+
}
400+
out[key] = content.Bytes()
401+
}
402+
return out, nil
403+
}

0 commit comments

Comments
 (0)