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

Redis - Add TFVP Support #1659

Merged
merged 5 commits into from
Nov 9, 2022
Merged
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
124 changes: 124 additions & 0 deletions vault/resource_database_secret_backend_connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ var (
name: "snowflake",
defaultPluginName: "snowflake" + dbPluginSuffix,
}
dbEngineRedis = &dbEngine{
name: "redis",
defaultPluginName: "redis" + dbPluginSuffix,
}
dbEngineRedisElastiCache = &dbEngine{
name: "redis_elasticache",
defaultPluginName: "redis-elasticache" + dbPluginSuffix,
Expand All @@ -120,6 +124,7 @@ var (
dbEnginePostgres,
dbEngineOracle,
dbEngineSnowflake,
dbEngineRedis,
dbEngineRedisElastiCache,
dbEngineRedshift,
}
Expand Down Expand Up @@ -561,6 +566,60 @@ func getDatabaseSchema(typ schema.ValueType) schemaMap {
MaxItems: 1,
ConflictsWith: util.CalculateConflictsWith(dbEngineOracle.Name(), dbEngineTypes),
},
dbEngineRedis.name: {
Type: typ,
Optional: true,
Description: "Connection parameters for the redis-database-plugin plugin.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"host": {
Type: schema.TypeString,
Required: true,
Description: "Specifies the host to connect to",
},
"port": {
Type: schema.TypeInt,
Optional: true,
Description: "The transport port to use to connect to Redis.",
Default: 6379,
ValidateFunc: validation.IsPortNumber,
},
"username": {
Type: schema.TypeString,
Required: true,
Description: "Specifies the username for Vault to use.",
},
"password": {
Type: schema.TypeString,
Required: true,
Description: "Specifies the password corresponding to the given username.",
Sensitive: true,
},
"tls": {
Type: schema.TypeBool,
Optional: true,
Description: "Specifies whether to use TLS when connecting to Redis.",
Default: false,
},
"insecure_tls": {
Type: schema.TypeBool,
Optional: true,
Description: "Specifies whether to skip verification of the server " +
"certificate when using TLS.",
Default: false,
},
"ca_cert": {
Zlaticanin marked this conversation as resolved.
Show resolved Hide resolved
Type: schema.TypeString,
Optional: true,
Description: "The contents of a PEM-encoded CA cert file " +
"to use to verify the Redis server's identity.",
Default: false,
},
},
},
MaxItems: 1,
ConflictsWith: util.CalculateConflictsWith(dbEngineRedis.Name(), dbEngineTypes),
},
dbEngineRedisElastiCache.name: {
Type: typ,
Optional: true,
Expand Down Expand Up @@ -872,6 +931,8 @@ func getDatabaseAPIDataForEngine(engine *dbEngine, idx int, d *schema.ResourceDa
setDatabaseConnectionDataWithDisableEscaping(d, prefix, data)
case dbEngineElasticSearch:
setElasticsearchDatabaseConnectionData(d, prefix, data)
case dbEngineRedis:
setRedisDatabaseConnectionData(d, prefix, data)
case dbEngineRedisElastiCache:
setRedisElastiCacheDatabaseConnectionData(d, prefix, data)
case dbEngineSnowflake:
Expand Down Expand Up @@ -991,6 +1052,43 @@ func getMySQLConnectionDetailsFromResponse(d *schema.ResourceData, prefix string
return result
}

func getRedisConnectionDetailsFromResponse(d *schema.ResourceData, prefix string, resp *api.Secret) map[string]interface{} {
details := resp.Data["connection_details"]
data, ok := details.(map[string]interface{})
if !ok {
return nil
}
result := map[string]interface{}{}

if v, ok := data["host"]; ok {
result["host"] = v.(string)
}
if v, ok := data["port"]; ok {
port, _ := v.(json.Number).Int64()
result["port"] = port
}
if v, ok := data["username"]; ok {
result["username"] = v.(string)
}
if v, ok := data["password"]; ok {
result["password"] = v.(string)
} else if v, ok := d.GetOk(prefix + "password"); ok {
// keep the password we have in state/config if the API doesn't return one
result["password"] = v.(string)
}
if v, ok := data["tls"]; ok {
result["tls"] = v.(bool)
}
if v, ok := data["insecure_tls"]; ok {
result["insecure_tls"] = v.(bool)
}
if v, ok := data["ca_cert"]; ok {
result["ca_cert"] = v.(string)
}

return result
}

func getRedisElastiCacheConnectionDetailsFromResponse(d *schema.ResourceData, prefix string, resp *api.Secret) map[string]interface{} {
details := resp.Data["connection_details"]
data, ok := details.(map[string]interface{})
Expand Down Expand Up @@ -1254,6 +1352,30 @@ func setMySQLDatabaseConnectionData(d *schema.ResourceData, prefix string, data
}
}

func setRedisDatabaseConnectionData(d *schema.ResourceData, prefix string, data map[string]interface{}) {
if v, ok := d.GetOk(prefix + "host"); ok {
data["host"] = v.(string)
}
if v, ok := d.GetOk(prefix + "port"); ok {
data["port"] = v.(int)
}
if v, ok := d.GetOk(prefix + "username"); ok {
data["username"] = v.(string)
}
if v, ok := d.GetOk(prefix + "password"); ok {
data["password"] = v.(string)
}
if v, ok := d.GetOk(prefix + "tls"); ok {
data["tls"] = v.(bool)
}
if v, ok := d.GetOk(prefix + "insecure_tls"); ok {
data["insecure_tls"] = v.(bool)
}
if v, ok := d.GetOk(prefix + "ca_cert"); ok {
data["ca_cert"] = v.(string)
}
}

func setRedisElastiCacheDatabaseConnectionData(d *schema.ResourceData, prefix string, data map[string]interface{}) {
if v, ok := d.GetOk(prefix + "url"); ok {
data["url"] = v.(string)
Expand Down Expand Up @@ -1649,6 +1771,8 @@ func getDBConnectionConfig(d *schema.ResourceData, engine *dbEngine, idx int,
result = getElasticsearchConnectionDetailsFromResponse(d, prefix, resp)
case dbEngineSnowflake:
result = getSnowflakeConnectionDetailsFromResponse(d, prefix, resp)
case dbEngineRedis:
result = getRedisConnectionDetailsFromResponse(d, prefix, resp)
case dbEngineRedisElastiCache:
result = getRedisElastiCacheConnectionDetailsFromResponse(d, prefix, resp)
case dbEngineRedshift:
Expand Down
64 changes: 64 additions & 0 deletions vault/resource_database_secret_backend_connection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,47 @@ func TestAccDatabaseSecretBackendConnection_snowflake(t *testing.T) {
})
}

func TestAccDatabaseSecretBackendConnection_redis(t *testing.T) {
MaybeSkipDBTests(t, dbEngineRedis)

values := testutil.SkipTestEnvUnset(t, "REDIS_HOST", "REDIS_USERNAME", "REDIS_PASSWORD")
host := values[0]
username := values[1]
password := values[2]

backend := acctest.RandomWithPrefix("tf-test-db")
pluginName := dbEngineRedis.DefaultPluginName()
name := acctest.RandomWithPrefix("db")

resource.Test(t, resource.TestCase{
Providers: testProviders,
PreCheck: func() { testutil.TestAccPreCheck(t) },
CheckDestroy: testAccDatabaseSecretBackendConnectionCheckDestroy,
Steps: []resource.TestStep{
{
Config: testAccDatabaseSecretBackendConnectionConfig_redis(name, backend, host, username, password),
Check: testComposeCheckFuncCommonDatabaseSecretBackend(name, backend, pluginName,
Zlaticanin marked this conversation as resolved.
Show resolved Hide resolved
resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "allowed_roles.#", "1"),
resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "allowed_roles.0", "*"),
resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "verify_connection", "true"),
resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "redis.0.host", host),
resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "redis.0.port", "6379"),
resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "redis.0.username", username),
resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "redis.0.password", password),
resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "redis.0.tls", "false"),
resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "redis.0.insecure_tls", "false"),
),
},
{
ResourceName: testDefaultDatabaseSecretBackendResource,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"verify_connection", "redis.0.password"},
},
},
})
}

func TestAccDatabaseSecretBackendConnection_redisElastiCache(t *testing.T) {
MaybeSkipDBTests(t, dbEngineRedisElastiCache)

Expand Down Expand Up @@ -1453,6 +1494,29 @@ resource "vault_database_secret_backend_connection" "test" {
`, path, name, url, username, password, userTempl)
}

func testAccDatabaseSecretBackendConnectionConfig_redis(name, path, host, username, password string) string {
config := fmt.Sprintf(`
resource "vault_mount" "db" {
path = "%s"
type = "database"
}
`, path)

config += fmt.Sprintf(`
resource "vault_database_secret_backend_connection" "test" {
backend = vault_mount.db.path
name = "%s"
allowed_roles = ["*"]
redis {
host = "%s"
username = "%s"
password = "%s"
}
}`, name, host, username, password)

return config
}

func testAccDatabaseSecretBackendConnectionConfig_redis_elasticache(name, path, connURL string) string {
config := fmt.Sprintf(`
resource "vault_mount" "db" {
Expand Down
20 changes: 20 additions & 0 deletions website/docs/r/database_secret_backend_connection.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ The following arguments are supported:

* `influxdb` - (Optional) A nested block containing configuration options for InfluxDB connections.

* `redis` - (Optional) A nested block containing configuration options for Redis connections.

* `redis_elasticache` - (Optional) A nested block containing configuration options for Redis ElastiCache connections.

Exactly one of the nested blocks of configuration options must be supplied.
Expand Down Expand Up @@ -167,6 +169,24 @@ Exactly one of the nested blocks of configuration options must be supplied.
* `connect_timeout` - (Optional) The number of seconds to use as a connection
timeout.

### Redis Configuration Options

* `host` - (Required) The host to connect to.

* `username` - (Required) The username to authenticate with.

* `password` - (Required) The password to authenticate with.

* `port` - (Required) The default port to connect to if no port is specified as
part of the host.

* `tls` - (Optional) Whether to use TLS when connecting to Redis.

* `insecure_tls` - (Optional) Whether to skip verification of the server
certificate when using TLS.

* `ca_cert` - (Optional) The contents of a PEM-encoded CA cert file to use to verify the Redis server's identity.

### Redis ElastiCache Configuration Options

* `url` - (Required) The url to connect to including the port; e.g. master.my-cluster.xxxxxx.use1.cache.amazonaws.com:6379.
Expand Down
23 changes: 22 additions & 1 deletion website/docs/r/database_secrets_mount.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,10 @@ Supported list of database secrets engines that can be configured:
* `influxdb` - (Optional) A nested block containing configuration options for InfluxDB connections.
*See [Configuration Options](#influxdb-configuration-options) for more info*

* `redis_elasticache` - (Optional) A nested block containing configuration options for InfluxDB connections.
* `redis` - (Optional) A nested block containing configuration options for Redis connections.
*See [Configuration Options](#redis-configuration-options) for more info*

* `redis_elasticache` - (Optional) A nested block containing configuration options for Redis ElastiCache connections.
*See [Configuration Options](#redis-elasticache-configuration-options) for more info*

### Cassandra Configuration Options
Expand Down Expand Up @@ -397,6 +400,24 @@ Supported list of database secrets engines that can be configured:
* `username_template` - (Optional) For Vault v1.7+. The template to use for username generation.
See [Vault docs](https://www.vaultproject.io/docs/concepts/username-templating)

### Redis Configuration Options

* `host` - (Required) The host to connect to.

* `username` - (Required) The username to authenticate with.

* `password` - (Required) The password to authenticate with.

* `port` - (Optional) The default port to connect to if no port is specified as
part of the host.

* `tls` - (Optional) Whether to use TLS when connecting to Redis.

* `insecure_tls` - (Optional) Whether to skip verification of the server
certificate when using TLS.

* `ca_cert` - (Optional) The contents of a PEM-encoded CA cert file to use to verify the Redis server's identity.

### Redis ElastiCache Configuration Options

* `url` - (Required) The configuration endpoint for the ElastiCache cluster to connect to.
Expand Down