Skip to content

Commit

Permalink
Added autoGeneratePassword flag for rds cluster
Browse files Browse the repository at this point in the history
  • Loading branch information
ravibagri4 committed Feb 21, 2024
1 parent 3000dfd commit a1ac771
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 12 deletions.
7 changes: 7 additions & 0 deletions apis/rds/v1beta1/zz_cluster_types.go

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

5 changes: 5 additions & 0 deletions apis/rds/v1beta1/zz_generated.deepcopy.go

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

26 changes: 26 additions & 0 deletions config/rds/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,34 @@ func Configure(p *config.Provider) {
if a, ok := attr["port"]; ok {
conn["port"] = []byte(fmt.Sprintf("%v", a))
}
if a, ok := attr["password"].(string); ok {
conn["password"] = []byte(a)
}
return conn, nil
}
desc, _ := comments.New("If true, the password will be auto-generated and"+
" stored in the Secret referenced by the masterPasswordSecretRef field.",
comments.WithTFTag("-"))
r.TerraformResource.Schema["auto_generate_password"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Description: desc.String(),
}
r.InitializerFns = append(r.InitializerFns,
PasswordGenerator(
"spec.forProvider.masterPasswordSecretRef",
"spec.forProvider.autoGeneratePassword",
))
r.TerraformResource.Schema["password"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Sensitive: true,
Description: "Password for the master DB user. If you set autoGeneratePassword to true, the Secret referenced here will be created or updated with generated password if it does not already contain one.",
}
r.TerraformResource.Schema["password"].Description = "Password for the " +
"master DB user. If you set autoGeneratePassword to true, the Secret" +
" referenced here will be created or updated with generated password" +
" if it does not already contain one."
})

p.AddResourceConfigurator("aws_rds_cluster_instance", func(r *config.Resource) {
Expand Down
182 changes: 182 additions & 0 deletions config/rds/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,20 @@ func TestPasswordGenerator(t *testing.T) {
err: errors.Wrap(errBoom, errGetPasswordSecret),
},
},
"CannotGetClusterSecret": {
reason: "An error should be returned if the referenced secret cannot be retrieved.",
args: args{
kube: &test.MockClient{
MockGet: test.NewMockGetFn(errBoom),
},
secretRefFieldPath: "",
toggleFieldPath: "",
mg: &fake.Managed{},
},
want: want{
err: errors.Wrap(errBoom, errGetPasswordSecret),
},
},
"SecretAlreadyFull": {
reason: "Should be no-op if the Secret already has password.",
args: args{
Expand Down Expand Up @@ -85,6 +99,35 @@ func TestPasswordGenerator(t *testing.T) {
},
},
},
"ClusterSecretAlreadyFull": {
reason: "Should be no-op if the Secret already has password.",
args: args{
kube: &test.MockClient{
MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
s, ok := obj.(*corev1.Secret)
if !ok {
return errors.New("needs to be secret")
}
s.Data = map[string][]byte{
"password": []byte("foo"),
}
return nil
},
},
secretRefFieldPath: "parameterizable.parameters.masterPasswordSecretRef",
mg: &ujfake.Terraformed{
Parameterizable: ujfake.Parameterizable{
Parameters: map[string]any{
"masterPasswordSecretRef": map[string]any{
"name": "foo",
"namespace": "bar",
"key": "password",
},
},
},
},
},
},
"NoSecretReference": {
reason: "Should be no-op if the secret reference is not given.",
args: args{
Expand All @@ -98,6 +141,19 @@ func TestPasswordGenerator(t *testing.T) {
},
},
},
"NoClusterSecretReference": {
reason: "Should be no-op if the secret reference is not given.",
args: args{
secretRefFieldPath: "parameterizable.parameters.masterPasswordSecretRef",
mg: &ujfake.Terraformed{
Parameterizable: ujfake.Parameterizable{
Parameters: map[string]any{
"another": "field",
},
},
},
},
},
"ToggleNotSet": {
reason: "Should be no-op if the toggle is not set at all.",
args: args{
Expand All @@ -119,6 +175,27 @@ func TestPasswordGenerator(t *testing.T) {
},
},
},
"ClusterToggleNotSet": {
reason: "Should be no-op if the toggle is not set at all.",
args: args{
kube: &test.MockClient{
MockGet: test.NewMockGetFn(nil),
},
secretRefFieldPath: "parameterizable.parameters.masterPasswordSecretRef",
toggleFieldPath: "parameterizable.parameters.autoGeneratePassword",
mg: &ujfake.Terraformed{
Parameterizable: ujfake.Parameterizable{
Parameters: map[string]any{
"masterPasswordSecretRef": map[string]any{
"name": "foo",
"namespace": "bar",
"key": "password",
},
},
},
},
},
},
"ToggleFalse": {
reason: "Should be no-op if the toggle is set to false.",
args: args{
Expand All @@ -141,6 +218,28 @@ func TestPasswordGenerator(t *testing.T) {
},
},
},
"ClusterToggleFalse": {
reason: "Should be no-op if the toggle is set to false.",
args: args{
kube: &test.MockClient{
MockGet: test.NewMockGetFn(nil),
},
secretRefFieldPath: "parameterizable.parameters.masterPasswordSecretRef",
toggleFieldPath: "parameterizable.parameters.autoGeneratePassword",
mg: &ujfake.Terraformed{
Parameterizable: ujfake.Parameterizable{
Parameters: map[string]any{
"masterPasswordSecretRef": map[string]any{
"name": "foo",
"namespace": "bar",
"key": "password",
},
"autoGeneratePassword": false,
},
},
},
},
},
"GenerateAndApply": {
reason: "Should apply if we generate, set the content of an already existing secret.",
args: args{
Expand Down Expand Up @@ -183,6 +282,48 @@ func TestPasswordGenerator(t *testing.T) {
},
},
},
"ClusterSecretGenerateAndApply": {
reason: "Should apply if we generate, set the content of an already existing secret.",
args: args{
kube: &test.MockClient{
MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
s, ok := obj.(*corev1.Secret)
if !ok {
return errors.New("needs to be secret")
}
s.CreationTimestamp = metav1.Time{Time: time.Now()}
return nil
},
MockPatch: func(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
s, ok := obj.(*corev1.Secret)
if !ok {
return errors.New("needs to be secret")
}
if len(s.Data["password"]) == 0 {
return errors.New("password is not set")
}
if len(s.OwnerReferences) != 0 {
return errors.New("owner references should not be set if secret already exists")
}
return nil
},
},
secretRefFieldPath: "parameterizable.parameters.masterPasswordSecretRef",
toggleFieldPath: "parameterizable.parameters.autoGeneratePassword",
mg: &ujfake.Terraformed{
Parameterizable: ujfake.Parameterizable{
Parameters: map[string]any{
"masterPasswordSecretRef": map[string]any{
"name": "foo",
"namespace": "bar",
"key": "password",
},
"autoGeneratePassword": true,
},
},
},
},
},
"GenerateAndCreate": {
reason: "Should create if we generate, set the content and there is no secret in place.",
args: args{
Expand Down Expand Up @@ -224,6 +365,47 @@ func TestPasswordGenerator(t *testing.T) {
},
},
},
"ClusterSecretGenerateAndCreate": {
reason: "Should create if we generate, set the content and there is no secret in place.",
args: args{
kube: &test.MockClient{
MockGet: test.NewMockGetFn(kerrors.NewNotFound(schema.GroupResource{}, "")),
MockCreate: func(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {
s, ok := obj.(*corev1.Secret)
if !ok {
return errors.New("needs to be secret")
}
if len(s.Data["password"]) == 0 {
return errors.New("password is not set")
}
if len(s.OwnerReferences) == 1 &&
s.OwnerReferences[0].Name == "foo-mgd" {
return nil
}
return errors.New("owner references should be set if secret is created")
},
},
secretRefFieldPath: "parameterizable.parameters.masterPasswordSecretRef",
toggleFieldPath: "parameterizable.parameters.autoGeneratePassword",
mg: &ujfake.Terraformed{
Managed: fake.Managed{
ObjectMeta: metav1.ObjectMeta{
Name: "foo-mgd",
},
},
Parameterizable: ujfake.Parameterizable{
Parameters: map[string]any{
"masterPasswordSecretRef": map[string]any{
"name": "foo",
"namespace": "bar",
"key": "password",
},
"autoGeneratePassword": true,
},
},
},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
Expand Down
10 changes: 1 addition & 9 deletions examples/rds/cluster.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ spec:
region: us-west-1
engine: aurora-postgresql
masterUsername: cpadmin
autoGeneratePassword: true
masterPasswordSecretRef:
name: sample-cluster-password
namespace: upbound-system
Expand All @@ -17,12 +18,3 @@ spec:
writeConnectionSecretToRef:
name: sample-rds-cluster-secret
namespace: upbound-system
---
apiVersion: v1
kind: Secret
metadata:
name: sample-cluster-password
namespace: upbound-system
type: Opaque
stringData:
password: TestPass0!
15 changes: 12 additions & 3 deletions package/crds/rds.aws.upbound.io_clusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ spec:
immediately, or during the next maintenance window. Default
is false. See Amazon RDS Documentation for more information.
type: boolean
autoGeneratePassword:
description: Password for the master DB user. Note that this may
show up in logs, and it will be stored in the state file. Cannot
be set if manage_master_user_password is set to true. If true,
the password will be auto-generated and stored in the Secret
referenced by the masterPasswordSecretRef field.
type: boolean
availabilityZones:
description: List of EC2 Availability Zones for the DB cluster
storage where DB cluster instances can be created. We recommend
Expand Down Expand Up @@ -355,9 +362,11 @@ spec:
type: boolean
masterPasswordSecretRef:
description: Password for the master DB user. Note that this may
show up in logs, and it will be stored in the state file. Please
refer to the RDS Naming Constraints. Cannot be set if manage_master_user_password
is set to true.
show up in logs, and it will be stored in the state file. Cannot
be set if manage_master_user_password is set to true. Password
for the master DB user. If you set autoGeneratePassword to true,
the Secret referenced here will be created or updated with generated
password if it does not already contain one.
properties:
key:
description: The key to select.
Expand Down

0 comments on commit a1ac771

Please sign in to comment.