Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add options to specify AWS credential source for RDS functions #3289

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 56 additions & 11 deletions docs/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,15 @@ Arguments:
| ---------- | :------: | ------ | ----------- |
| instanceID | Yes | string | ID of RDS instance you want to create snapshot of |
| dbEngine | No | string | Required in case of RDS Aurora instance. Supported DB Engines: `aurora` `aurora-mysql` and `aurora-postgresql` |
| credentialsSource | No | string | Source for aws credentials. Supported sources: `profile`, `secret`, `serviceaccount`. Default value is `profile` |
| credentialsSecret | No | string | Secret to get credentials from. Only used with `credentialsSource: secret`. Secret with this name should be referenced in the Actionset |
| region | No | string | AWS region to use. Derived from profile or serviceaccount if not set |

::: tip NOTE

`credentialsSource: serviceaccount` uses kanister operator service account. IAM role for service
account should be set up to access the RDS database.
:::

Outputs:

Expand Down Expand Up @@ -994,6 +1003,10 @@ Arguments:
| image | No | string | kanister-tools image to be used for running export job |
| podAnnotations | No | map[string]string | custom annotations for the temporary pod that gets created |
| podLabels | No | map[string]string | custom labels for the temporary pod that gets created |
| credentialsSource | No | string | Source for aws credentials. Supported sources: `profile`, `secret`, `serviceaccount`. Default value is `profile` |
| credentialsSecret | No | string | Secret to get credentials from. Only used with `credentialsSource: secret`. Secret with this name should be [referenced in the Actionset](templates.html#secrets) |
| region | No | string | AWS region to use. Derived from profile or serviceaccount if not set |


::: tip NOTE

Expand All @@ -1006,6 +1019,19 @@ associated with instance with `instanceID` and will pass the same. - If
set, `default` DB Subnet group will be used.
:::

::: tip NOTE

If `credentialsSource` is configured to `profile` (default behaviour), the profile used has to be
an S3 profile configured with the same region as the database snapshot.
If it's required to export to another region, `credentialSource: secret` or `credentialSource: serviceaccount` can be used.
:::

::: tip NOTE

`credentialsSource: serviceaccount` uses kanister operator service account. IAM role for service
account should be set up to access the RDS database.
:::

Outputs:

| Output | Type | Description |
Expand Down Expand Up @@ -1069,13 +1095,13 @@ stored in an object storage.

::: tip NOTE

\- If [snapshotID] is set, the function will restore RDS
instance from the RDS snapshot. Otherwise *backupID* needs
to be set to restore the RDS instance from data dump. - While restoring
the data from RDS snapshot if RDS instance (where we have to restore the
data) doesn\'t exist, the RDS instance will be created. But if the data
- If `snapshotID` is set, the function will restore RDS
instance from the RDS snapshot. Otherwise `backupID` needs
to be set to restore the RDS instance from data dump.
- While restoring the data from RDS snapshot if RDS instance (where we have to restore the
data) doesn't exist, the RDS instance will be created. But if the data
is being restored from the Object Storage (data dump) and the RDS
instance doesn\'t exist new RDS instance will not be created and will
instance doesn't exist new RDS instance will not be created and will
result in an error.
:::

Expand All @@ -1096,15 +1122,25 @@ Arguments:
| image | No | string | kanister-tools image to be used for running restore, only relevant when restoring from data dump (if `snapshotID` is empty) |
| podAnnotations | No | map[string]string | custom annotations for the temporary pod that gets created |
| podLabels | No | map[string]string | custom labels for the temporary pod that gets created |
| credentialsSource | No | string | Source for aws credentials. Supported sources: `profile`, `secret`, `serviceaccount`. Default value is `profile` |
| credentialsSecret | No | string | Secret to get credentials from. Only used with `credentialsSource: secret`. Secret with this name should be referenced in the Actionset |
| region | No | string | AWS region to use. Derived from profile or serviceaccount if not set |


::: tip NOTE

\- If `snapshotID` is not set, restore will be done from data dump. In
that case `backupID` [arg] is required. - If
`securityGroupID` argument is not set, `RestoreRDSSnapshot` will find
- If `snapshotID` is not set, restore will be done from data dump. In
that case `backupID` arg is required.
- If `securityGroupID` argument is not set, `RestoreRDSSnapshot` will find
out Security Group IDs associated with instance with `instanceID` and
will pass the same. - If `dbSubnetGroup` argument is not set, `default`
DB Subnet group will be used.
will pass the same.
- If `dbSubnetGroup` argument is not set, `default` DB Subnet group will be used.
:::

::: tip NOTE

`credentialsSource: serviceaccount` uses kanister operator service account. IAM role for service
account should be set up to access the RDS database.
:::

Outputs:
Expand Down Expand Up @@ -1153,6 +1189,15 @@ Arguments:
| Argument | Required | Type | Description |
| ---------- | :------: | ------ | ----------- |
| snapshotID | No | string | ID of the RDS snapshot |
| credentialsSource | No | string | Source for aws credentials. Supported sources: `profile`, `secret`, `serviceaccount`. Default value is `profile` |
| credentialsSecret | No | string | Secret to get credentials from. Only used with `credentialsSource: secret`. Secret with this name should be referenced in the Actionset |
| region | No | string | AWS region to use. Derived from profile or serviceaccount if not set |

::: tip NOTE

`credentialsSource: serviceaccount` uses kanister operator service account. IAM role for service
account should be set up to access the RDS database.
:::

Example:

Expand Down
103 changes: 76 additions & 27 deletions examples/aws-rds/postgresql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ aws rds wait db-instance-available --db-instance-identifier=test-postgresql-inst

Create a configmap which contains information to connect to the RDS DB instance

```
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
Expand Down Expand Up @@ -82,6 +82,32 @@ data operations such as backup should go. This is stored as a `profiles.cr.kanis
requires a Profile reference to complete the action. This CR (`profiles.cr.kanister.io`)
can be shared between Kanister-enabled application instances.

### Configure a secret to access RDS

By default the blueprints in this example are using credentials from the profile to access RDS
resources.

If you want to export your backups to a different region or a different object store (recommended),
you need to provide alternative credential configuration.

Here we use k8s secret with AWS credentials.
You need to make sure credentials provided in this secret can be used to access RDS operations.

```yaml
---
apiVersion: v1
kind: Secret
metadata:
name: rds-secret
namespace: pgtestrds
type: secrets.kanister.io/aws
data:
aws_access_key_id: "<your access key id>"
aws_secret_access_key: "<you secret>"
role: ""
```

This secret needs to be referenced in the acitonset.

### Create Blueprint

Expand All @@ -97,6 +123,12 @@ So as you can see we will have to create a blueprint depending on how are we goi

Use `rds-postgres-snap-blueprint.yaml` or `rds-postgres-blueprint.yaml` Blueprint if you want to take backup using RDS snapshots or you can use `rds-postgres-dump-blueprint.yaml` Blueprint if you want to extract postgres dump from snapshot and push to S3 storage

**NOTE:**
- The `rds-postgres-dump-blueprint.yaml` blueprint demonstrates how to use credentials from the secret.
- The `rds-postgres-snap-blueprint.yaml` blueprint demonstrates how to use credentials from the profile.

To change that you can modify the blueprints using those examples and documentation in https://docs.kanister.io/functions.html#createrdssnapshot


```bash
$ kubectl create -f <blueprint> -n kasten-io
Expand All @@ -106,40 +138,57 @@ $ kubectl create -f <blueprint> -n kasten-io

You can now take a snapshot of the PostgreSQL RDS instance data using an ActionSet defining backup for this application. Create an ActionSet in the same namespace as the controller.

> If you have deployed your application which uses RDS instance in namespace other than `pgtestrds`, you need to modify the commands used below to use the correct namespace
Get profile:

```bash
$ kubectl get profile -n pgtestrds
NAME AGE
s3-profile-sph7s 2h
```

Create actionset file:

> Use correct blueprint name (one of `rds-postgres-dump-bp` or `rds-postgres-snapshot-bp`) you have created earlier
> If you have deployed your application which uses RDS instance in namespace other than `pgtestrds`, you need to modify the commands used below to use the correct namespace
> Please make sure `region` option corresponds to the AWS region where your RDS is deployed.

```yaml
apiVersion: cr.kanister.io/v1alpha1
kind: ActionSet
metadata:
name: rds-backup
namespace: kasten-io
spec:
actions:
- name: backup
blueprint: <blueprint-name>
object:
apiVersion: v1
name: dbconfig
namespace: pgtestrds
resource: configmaps
profile:
name: <your profile>
namespace: pgtestrds
secrets:
aws:
name: rds-secret
namespace: pgtestrds
options:
region: <rds region>
```

Where:
- dbconfig is a configmap holding RDS infromation
Please see pgtest/deploy/config.yaml for configmap format
- rds-secret is an AWS secret with access to RDS resources

# Use correct blueprint name (one of `rds-postgres-dump-bp` or `rds-postgres-snapshot-bp`) you have created earlier

cat <<EOF | kubectl apply -f -
> apiVersion: cr.kanister.io/v1alpha1
> kind: ActionSet
> metadata:
> name: rds-backup
> namespace: kasten-io
> spec:
> actions:
> - name: backup
> blueprint: <blueprint-name>
> object:
> apiVersion: v1
> name: dbconfig
> namespace: pgtestrds
> resource: configmaps
> profile:
> name: s3-profile-sph7s
> namespace: pgtestrds
> EOF
actionset.cr.kanister.io/rds-backup created

# Where,
# dbconfig is a configmap holding RDS infromation
# Please see pgtest/deploy/config.yaml for configmap format
Apply actionset:

```bash
$ kubectl apply -f rds-backup-actionset.yaml
actionset.cr.kanister.io/rds-backup created

# View the status of the actionset
$ kubectl --namespace kasten-io describe actionset rds-backup
Expand Down
12 changes: 12 additions & 0 deletions examples/aws-rds/postgresql/rds-postgres-dump-blueprint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ actions:
name: createSnapshot
args:
instanceID: '{{ index .Object.data "postgres.instanceid" }}'
credentialsSource: secret
credentialsSecret: aws
region: '{{ .Options.region }}'
- func: ExportRDSSnapshotToLocation
name: exportSnapshot
objects:
Expand All @@ -35,10 +38,16 @@ actions:
snapshotID: "{{ .Phases.createSnapshot.Output.snapshotID }}"
backupArtifactPrefix: test-postgresql-instance/postgres
dbSubnetGroup: "{{ .Phases.createSnapshot.Output.dbSubnetGroup }}"
credentialsSource: secret
credentialsSecret: aws
region: '{{ .Options.region }}'
- func: DeleteRDSSnapshot
name: deleteSnapshot
args:
snapshotID: "{{ .Phases.createSnapshot.Output.snapshotID }}"
credentialsSource: secret
credentialsSecret: aws
region: '{{ .Options.region }}'
restore:
inputArtifactNames:
- backupInfo
Expand All @@ -60,6 +69,9 @@ actions:
password: '{{ index .Phases.restoreSnapshots.Secrets.dbsecret.Data "password" | toString }}'
dbEngine: "PostgreSQL"
dbSubnetGroup: "{{ .ArtifactsIn.backupInfo.KeyValue.dbSubnetGroup }}"
credentialsSource: secret
credentialsSecret: aws
region: '{{ .Options.region }}'
delete:
phases:
- func: KubeTask
Expand Down
21 changes: 12 additions & 9 deletions pkg/function/create_rds_snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,12 @@ func (*createRDSSnapshotFunc) Name() string {
return CreateRDSSnapshotFuncName
}

func createRDSSnapshot(ctx context.Context, instanceID string, dbEngine RDSDBEngine, profile *param.Profile) (map[string]interface{}, error) {
func createRDSSnapshot(ctx context.Context, instanceID string, dbEngine RDSDBEngine, credentialsSource awsCredentialsSource, tp param.TemplateParams) (map[string]interface{}, error) {
var allocatedStorage int64
// Validate profile
if err := ValidateProfile(profile); err != nil {
return nil, errkit.Wrap(err, "Profile Validation failed")
}

// Get aws config from profile
awsConfig, region, err := getAWSConfigFromProfile(ctx, profile)
awsConfig, region, err := getAwsConfig(ctx, credentialsSource, tp)
if err != nil {
return nil, errkit.Wrap(err, "Failed to get AWS creds from profile")
return nil, errkit.Wrap(err, "Failed to get AWS creds")
}

// Create rds client
Expand Down Expand Up @@ -185,7 +180,12 @@ func (crs *createRDSSnapshotFunc) Exec(ctx context.Context, tp param.TemplatePar
return nil, err
}

return createRDSSnapshot(ctx, instanceID, dbEngine, tp.Profile)
credentialsSource, err := parseCredentialsSource(args)
if err != nil {
return nil, err
}

return createRDSSnapshot(ctx, instanceID, dbEngine, *credentialsSource, tp)
}

func (*createRDSSnapshotFunc) RequiredArgs() []string {
Expand All @@ -198,6 +198,9 @@ func (crs *createRDSSnapshotFunc) Arguments() []string {
return []string{
CreateRDSSnapshotInstanceIDArg,
CreateRDSSnapshotDBEngine,
CredentialsSourceArg,
CredentialsSecretArg,
RegionArg,
}
}

Expand Down
21 changes: 12 additions & 9 deletions pkg/function/delete_rds_snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,11 @@ func (*deleteRDSSnapshotFunc) Name() string {
return DeleteRDSSnapshotFuncName
}

func deleteRDSSnapshot(ctx context.Context, snapshotID string, profile *param.Profile, dbEngine RDSDBEngine) error {
// Validate profile
if err := ValidateProfile(profile); err != nil {
return errkit.Wrap(err, "Profile Validation failed")
}

func deleteRDSSnapshot(ctx context.Context, snapshotID string, credentialsSource awsCredentialsSource, tp param.TemplateParams, dbEngine RDSDBEngine) error {
// Get aws config from profile
awsConfig, region, err := getAWSConfigFromProfile(ctx, profile)
awsConfig, region, err := getAwsConfig(ctx, credentialsSource, tp)
if err != nil {
return errkit.Wrap(err, "Failed to get AWS creds from profile")
return errkit.Wrap(err, "Failed to get AWS creds")
}

// Create rds client
Expand Down Expand Up @@ -130,7 +125,12 @@ func (d *deleteRDSSnapshotFunc) Exec(ctx context.Context, tp param.TemplateParam
return nil, err
}

return nil, deleteRDSSnapshot(ctx, snapshotID, tp.Profile, dbEngine)
credentialsSource, err := parseCredentialsSource(args)
if err != nil {
return nil, err
}

return nil, deleteRDSSnapshot(ctx, snapshotID, *credentialsSource, tp, dbEngine)
}

func (*deleteRDSSnapshotFunc) RequiredArgs() []string {
Expand All @@ -141,6 +141,9 @@ func (*deleteRDSSnapshotFunc) Arguments() []string {
return []string{
DeleteRDSSnapshotSnapshotIDArg,
CreateRDSSnapshotDBEngine,
CredentialsSourceArg,
CredentialsSecretArg,
RegionArg,
}
}

Expand Down
Loading
Loading