Skip to content

Commit

Permalink
dbaas: ensure password is always retained in connection URIs, replica…
Browse files Browse the repository at this point in the history
…s, and connection pools. (#1169)

* dbaas: ensure password is always retained in connection URI.

* dbaas: ensure password is always retained in connection pools.

* dbaas: ensure password is always retained in replicas.

* Remove unused const
  • Loading branch information
andrewsomething authored May 31, 2024
1 parent 56e3089 commit 3617acb
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 43 deletions.
37 changes: 13 additions & 24 deletions digitalocean/database/resource_database_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
)

const (
mongoDBEngineSlug = "mongodb"
mysqlDBEngineSlug = "mysql"
redisDBEngineSlug = "redis"
)
Expand Down Expand Up @@ -640,32 +639,12 @@ func flattenMaintWindowOpts(opts godo.DatabaseMaintenanceWindow) []map[string]in
return result
}

func buildDBConnectionURI(database *godo.Database, d *schema.ResourceData) (string, error) {
if database.EngineSlug == mongoDBEngineSlug {
return buildMongoDBConnectionURI(database.Connection, d)
}

return database.Connection.URI, nil
}

func buildDBPrivateURI(database *godo.Database, d *schema.ResourceData) (string, error) {
if database.EngineSlug == mongoDBEngineSlug {
uri, err := buildMongoDBConnectionURI(database.PrivateConnection, d)
if err != nil {
return "", err
}
return uri, nil
}

return database.PrivateConnection.URI, nil
}

func setDatabaseConnectionInfo(database *godo.Database, d *schema.ResourceData) error {
if database.Connection != nil {
d.Set("host", database.Connection.Host)
d.Set("port", database.Connection.Port)

uri, err := buildDBConnectionURI(database, d)
uri, err := buildDBConnectionURI(database.Connection, d)
if err != nil {
return err
}
Expand All @@ -681,7 +660,7 @@ func setDatabaseConnectionInfo(database *godo.Database, d *schema.ResourceData)
if database.PrivateConnection != nil {
d.Set("private_host", database.PrivateConnection.Host)

privateUri, err := buildDBPrivateURI(database, d)
privateUri, err := buildDBPrivateURI(database.PrivateConnection, d)
if err != nil {
return err
}
Expand All @@ -705,11 +684,17 @@ func setUIConnectionInfo(database *godo.Database, d *schema.ResourceData) error
return nil
}

// buildDBConnectionURI constructs a connection URI using the password stored in state.
//
// MongoDB clusters only return their password in response to the initial POST.
// The host for the cluster is not known until it becomes available. In order to
// build a usable connection URI, we must save the password and then add it to
// the URL returned latter.
func buildMongoDBConnectionURI(conn *godo.DatabaseConnection, d *schema.ResourceData) (string, error) {
//
// This also protects against the password being removed from the URI if the user
// switches to using a read-only token. All database engines redact the password
// in that case
func buildDBConnectionURI(conn *godo.DatabaseConnection, d *schema.ResourceData) (string, error) {
password := d.Get("password")
uri, err := url.Parse(conn.URI)
if err != nil {
Expand All @@ -722,6 +707,10 @@ func buildMongoDBConnectionURI(conn *godo.DatabaseConnection, d *schema.Resource
return uri.String(), nil
}

func buildDBPrivateURI(conn *godo.DatabaseConnection, d *schema.ResourceData) (string, error) {
return buildDBConnectionURI(conn, d)
}

func expandBackupRestore(config []interface{}) *godo.DatabaseBackupRestore {
backupRestoreConfig := config[0].(map[string]interface{})

Expand Down
47 changes: 40 additions & 7 deletions digitalocean/database/resource_database_connection_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ func resourceDigitalOceanDatabaseConnectionPoolCreate(ctx context.Context, d *sc
d.SetId(createConnectionPoolID(clusterID, pool.Name))
log.Printf("[INFO] DatabaseConnectionPool Name: %s", pool.Name)

err = setConnectionPoolInfo(pool, d)
if err != nil {
return diag.Errorf("Error building connection URI: %s", err)
}

return resourceDigitalOceanDatabaseConnectionPoolRead(ctx, d, meta)
}

Expand Down Expand Up @@ -153,13 +158,41 @@ func resourceDigitalOceanDatabaseConnectionPoolRead(ctx context.Context, d *sche
d.Set("size", pool.Size)
d.Set("db_name", pool.Database)

// Computed values
d.Set("host", pool.Connection.Host)
d.Set("private_host", pool.PrivateConnection.Host)
d.Set("port", pool.Connection.Port)
d.Set("uri", pool.Connection.URI)
d.Set("private_uri", pool.PrivateConnection.URI)
d.Set("password", pool.Connection.Password)
err = setConnectionPoolInfo(pool, d)
if err != nil {
return diag.Errorf("Error building connection URI: %s", err)
}

return nil
}

func setConnectionPoolInfo(pool *godo.DatabasePool, d *schema.ResourceData) error {
if pool.Connection != nil {
d.Set("host", pool.Connection.Host)
d.Set("port", pool.Connection.Port)

if pool.Connection.Password != "" {
d.Set("password", pool.Connection.Password)
}

uri, err := buildDBConnectionURI(pool.Connection, d)
if err != nil {
return err
}

d.Set("uri", uri)
}

if pool.PrivateConnection != nil {
d.Set("private_host", pool.PrivateConnection.Host)

privateURI, err := buildDBConnectionURI(pool.PrivateConnection, d)
if err != nil {
return err
}

d.Set("private_uri", privateURI)
}

return nil
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,6 @@ func TestAccDigitalOceanDatabaseConnectionPool_Basic(t *testing.T) {
"digitalocean_database_connection_pool.pool-01", "name", databaseConnectionPoolName),
resource.TestCheckResourceAttr(
"digitalocean_database_connection_pool.pool-01", "mode", "session"),
resource.TestCheckResourceAttr(
"digitalocean_database_connection_pool.pool-01", "user", ""),
resource.TestCheckResourceAttr(
"digitalocean_database_connection_pool.pool-01", "password", ""),
),
},
},
Expand Down
52 changes: 44 additions & 8 deletions digitalocean/database/resource_database_replica.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,19 @@ func resourceDigitalOceanDatabaseReplicaCreate(ctx context.Context, d *schema.Re
}
}
replicaCluster = rc

return nil
})

if err != nil {
return diag.FromErr(err)
}

err = setReplicaConnectionInfo(replicaCluster, d)
if err != nil {
return diag.Errorf("Error building connection URI: %s", err)
}

replica, err := waitForDatabaseReplica(client, clusterId, "online", replicaCluster.Name)
if err != nil {
return diag.Errorf("Error creating DatabaseReplica: %s", err)
Expand Down Expand Up @@ -218,17 +224,47 @@ func resourceDigitalOceanDatabaseReplicaRead(ctx context.Context, d *schema.Reso

// Computed values
d.Set("uuid", replica.ID)
d.Set("host", replica.Connection.Host)
d.Set("private_host", replica.PrivateConnection.Host)
d.Set("port", replica.Connection.Port)
d.Set("uri", replica.Connection.URI)
d.Set("private_uri", replica.PrivateConnection.URI)
d.Set("database", replica.Connection.Database)
d.Set("user", replica.Connection.User)
d.Set("password", replica.Connection.Password)
d.Set("private_network_uuid", replica.PrivateNetworkUUID)
d.Set("storage_size_mib", strconv.FormatUint(replica.StorageSizeMib, 10))

err = setReplicaConnectionInfo(replica, d)
if err != nil {
return diag.Errorf("Error building connection URI: %s", err)
}

return nil
}

func setReplicaConnectionInfo(replica *godo.DatabaseReplica, d *schema.ResourceData) error {
if replica.Connection != nil {
d.Set("host", replica.Connection.Host)
d.Set("port", replica.Connection.Port)
d.Set("database", replica.Connection.Database)
d.Set("user", replica.Connection.User)

if replica.Connection.Password != "" {
d.Set("password", replica.Connection.Password)
}

uri, err := buildDBConnectionURI(replica.Connection, d)
if err != nil {
return err
}

d.Set("uri", uri)
}

if replica.PrivateConnection != nil {
d.Set("private_host", replica.PrivateConnection.Host)

privateURI, err := buildDBConnectionURI(replica.PrivateConnection, d)
if err != nil {
return err
}

d.Set("private_uri", privateURI)
}

return nil
}

Expand Down

0 comments on commit 3617acb

Please sign in to comment.