Skip to content
This repository has been archived by the owner on Jul 6, 2022. It is now read-only.

Commit

Permalink
Support specifying server name, admin username and password of postgr…
Browse files Browse the repository at this point in the history
…eSQL (#711)

* Support specifying server name, admin username and admin password of postgreSQL

* Fix lint issues and add lifecycle test

* Change regexp to custom validator

* Fix typo

* Update lifecycle test

* Fix bug

* Fix role name error
  • Loading branch information
norshtein authored Jul 18, 2019
1 parent d631928 commit 87d222f
Show file tree
Hide file tree
Showing 17 changed files with 386 additions and 32 deletions.
9 changes: 8 additions & 1 deletion docs/modules/postgresql.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Open Service Broker for Azure contains three types of Azure Database for Postgre
| `azure-postgresql-*-dbms` | Provision only an Azure Database for PostgreSQL DBMS. This can be used to provision multiple databases at a later time. |
| `azure-postgresql-*-database` | Provision a new database only upon a previously provisioned DBMS. |

The `azure-postgresql-*` services allow you to provision both a DBMS and a database. When the provision operation is successful, the database will be ready to use. You can not provision additional databases onto an instance provisioned through these two services. The `azure-postgresql-*-dbms` and `azure-postgresql-*-database` services, on the other hand, can be combined to provision multiple databases on a single DBMS. Currently, OSBA supports two versions of Azure Database for PostgreSQL services:
The `azure-postgresql-*` services allow you to provision both a DBMS and a database. When the provision operation is successful, the database will be ready to use. You can not provision additional databases onto an instance of `azure-postgresql-*`. The `azure-postgresql-*-dbms` and `azure-postgresql-*-database` services, on the other hand, can be combined to provision multiple databases on a single DBMS. Currently, OSBA supports two versions of Azure Database for PostgreSQL services:
<table>
<thead>
<tr>
Expand Down Expand Up @@ -78,6 +78,10 @@ name.
|----------------|------|-------------|----------|---------------|
| `location` | `string` | The Azure region in which to provision applicable resources. | Y | |
| `resourceGroup` | `string` | The (new or existing) resource group with which to associate new resources. | Y | |
| `serverName` | `string` | Name of the PostgreSQL server. | N | A random generated string. |
| `adminAccountSettings` | `object` | Settings of administrator account of PostgreSQL server. Typically you do not need to specify this. | N | Default admin username is "postgres" and password is a randomly generated string. |
| `adminAccountSettings.adminUsername` | `string` | The administrator username for the server. | N | "postgres" |
| `adminAccountSettings.adminPassword` | `string` | The administrator password for the server. **Warning**: you may leak your password if you specify this property, others can see this password in your request body and `ServiceInstance` definition. DO NOT use this property unless you know what you are doing. | N | A random generated password. |
| `sslEnforcement` | `string` | Specifies whether the server requires the use of TLS when connecting. Valid valued are `""` (unspecified), `enabled`, or `disabled`. | N | `""`. Left unspecified, SSL _will_ be enforced. |
| `firewallRules` | `array` | Specifies the firewall rules to apply to the server. Definition follows. | N | `[]` Left unspecified, Firewall will default to only Azure IPs. If rules are provided, they must have valid values. |
| `firewallRules[n].name` | `string` | Specifies the name of the generated firewall rule |Y | |
Expand Down Expand Up @@ -275,6 +279,9 @@ Provisions an Azure Database for PostgreSQL DBMS instance containing no database
|----------------|------|-------------|----------|---------------|
| `location` | `string` | The Azure region in which to provision applicable resources. | Y | |
| `resourceGroup` | `string` | The (new or existing) resource group with which to associate new resources. | Y | |
| `serverName` | `string` | Name of the PostgreSQL server. | N | A random generated string. |
| `adminUsername` | `string` | The administrator username for the server. | N | "postgresql" |
| `adminPassword` | `string` | The administrator password for the server. **Warning**: you may leak your password if you specify this property, others can see this password in your request body and `ServiceInstance` definition. DO NOT use this property unless you know what you are doing. | N | A random generated password. |
| `alias` | `string` | Specifies an alias that can be used by later provision actions to create databases on this DBMS. | Y | |
| `sslEnforcement` | `string` | Specifies whether the server requires the use of TLS when connecting. Valid valued are `""` (unspecified), `enabled`, or `disabled`. | N | `""`. Left unspecified, SSL _will_ be enforced. |
| `firewallRules` | `array` | Specifies the firewall rules to apply to the server. Definition follows. | N | `[]` Left unspecified, Firewall will default to only Azure IPs. If rules are provided, they must have valid values. |
Expand Down
2 changes: 1 addition & 1 deletion pkg/services/postgresql/all_in_one_arm_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var allInOneARMTemplateBytes = []byte(`
"name": "{{ .serverName }}",
"properties": {
"version": "{{.version}}",
"administratorLogin": "postgres",
"administratorLogin": "{{ .administratorLogin }}",
"administratorLoginPassword": "{{ .administratorLoginPassword }}",
"storageProfile": {
"storageMB": {{.storage}},
Expand Down
1 change: 1 addition & 0 deletions pkg/services/postgresql/all_in_one_bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ func (a *allInOneManager) Bind(
dt := instance.Details.(*allInOneInstanceDetails)
bd, err := createBinding(
isSSLRequired(*instance.ProvisioningParameters),
dt.AdministratorLogin,
dt.ServerName,
string(dt.AdministratorLoginPassword),
dt.FullyQualifiedDomainName,
Expand Down
20 changes: 10 additions & 10 deletions pkg/services/postgresql/all_in_one_provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (

"github.com/Azure/open-service-broker-azure/pkg/generate"
"github.com/Azure/open-service-broker-azure/pkg/service"
uuid "github.com/satori/go.uuid"
)

func (a *allInOneManager) GetProvisioner(
Expand All @@ -22,24 +21,23 @@ func (a *allInOneManager) GetProvisioner(

func (a *allInOneManager) preProvision(
ctx context.Context,
_ service.Instance,
instance service.Instance,
) (service.InstanceDetails, error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
serverName, err := getAvailableServerName(

dbmsInstanceDetails, err := generateDBMSInstanceDetails(
ctx,
instance,
a.checkNameAvailabilityClient,
)
if err != nil {
return nil, err
return nil, fmt.Errorf("error generating instance detail: %v", err)
}

return &allInOneInstanceDetails{
dbmsInstanceDetails: dbmsInstanceDetails{
ARMDeploymentName: uuid.NewV4().String(),
ServerName: serverName,
AdministratorLoginPassword: service.SecureString(generate.NewPassword()),
},
DatabaseName: generate.NewIdentifier(),
dbmsInstanceDetails: *dbmsInstanceDetails,
DatabaseName: generate.NewIdentifier(),
}, nil
}

Expand Down Expand Up @@ -97,6 +95,7 @@ func (a *allInOneManager) setupDatabase(
dt := instance.Details.(*allInOneInstanceDetails)
err := setupDatabase(
isSSLRequired(*instance.ProvisioningParameters),
dt.AdministratorLogin,
dt.ServerName,
string(dt.AdministratorLoginPassword),
dt.FullyQualifiedDomainName,
Expand All @@ -117,6 +116,7 @@ func (a *allInOneManager) createExtensions(
if len(extensions) > 0 {
err := createExtensions(
isSSLRequired(*instance.ProvisioningParameters),
dt.AdministratorLogin,
dt.ServerName,
string(dt.AdministratorLoginPassword),
dt.FullyQualifiedDomainName,
Expand Down
1 change: 1 addition & 0 deletions pkg/services/postgresql/all_in_one_unbind.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ func (a *allInOneManager) Unbind(
bd := binding.Details.(*bindingDetails)
return unbind(
isSSLRequired(*instance.ProvisioningParameters),
dt.AdministratorLogin,
dt.ServerName,
string(dt.AdministratorLoginPassword),
dt.FullyQualifiedDomainName,
Expand Down
6 changes: 4 additions & 2 deletions pkg/services/postgresql/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var dbExtensionsSchema = &service.ArrayPropertySchema{

func getDBConnection(
enforceSSL bool,
administratorLogin string,
serverName string,
administratorLoginPassword string,
fullyQualifiedDomainName string,
Expand All @@ -29,12 +30,13 @@ func getDBConnection(
var connectionStrTemplate string
if enforceSSL {
connectionStrTemplate =
"postgres://postgres@%s:%s@%s/%s?sslmode=require"
"postgres://%s@%s:%s@%s/%s?sslmode=require"
} else {
connectionStrTemplate = "postgres://postgres@%s:%s@%s/%s"
connectionStrTemplate = "postgres://%s@%s:%s@%s/%s"
}
db, err := sql.Open("postgres", fmt.Sprintf(
connectionStrTemplate,
administratorLogin,
serverName,
administratorLoginPassword,
fullyQualifiedDomainName,
Expand Down
2 changes: 2 additions & 0 deletions pkg/services/postgresql/common_bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

func createBinding(
enforceSSL bool,
administratorLogin string,
serverName string,
administratorLoginPassword string,
fullyQualifiedDomainName string,
Expand All @@ -22,6 +23,7 @@ func createBinding(

db, err := getDBConnection(
enforceSSL,
administratorLogin,
serverName,
administratorLoginPassword,
fullyQualifiedDomainName,
Expand Down
106 changes: 99 additions & 7 deletions pkg/services/postgresql/common_provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"

postgresSDK "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2017-12-01/postgresql" // nolint: lll
"github.com/Azure/open-service-broker-azure/pkg/generate"
"github.com/Azure/open-service-broker-azure/pkg/service"
log "github.com/Sirupsen/logrus"
uuid "github.com/satori/go.uuid"
Expand All @@ -21,33 +22,115 @@ func getAvailableServerName(
) (string, error) {
for {
serverName := uuid.NewV4().String()
nameAvailability, err := checkNameAvailabilityClient.Execute(
available, err := isServerNameAvailable(
ctx,
postgresSDK.NameAvailabilityRequest{
Name: &serverName,
},
serverName,
checkNameAvailabilityClient,
)
if err != nil {
return "", fmt.Errorf(
"error determining server name availability: %s",
err,
)
}
if *nameAvailability.NameAvailable {
if available {
return serverName, nil
}
}
}

func isServerNameAvailable(
ctx context.Context,
serverName string,
checkNameAvailabilityClient postgresSDK.CheckNameAvailabilityClient,
) (bool, error) {
nameAvailability, err := checkNameAvailabilityClient.Execute(
ctx,
postgresSDK.NameAvailabilityRequest{
Name: &serverName,
},
)
if err != nil {
return false, err
}

if *nameAvailability.NameAvailable {
return true, nil
}
return false, nil
}

// generateDBMSInstanceDetail will read information
// from instance provision parameters, and generate
// a dbmsInstanceDetail. This method is expected to
// be invoked by preProvision step of all-in-one and
// dbms.
func generateDBMSInstanceDetails(
ctx context.Context,
instance service.Instance,
checkNameAvailabilityClient postgresSDK.CheckNameAvailabilityClient,
) (*dbmsInstanceDetails, error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()

// Determine server name. If specified,
// check availability; else, generate one.
pp := instance.ProvisioningParameters
serverName := pp.GetString("serverName")
if serverName != "" {
available, err := isServerNameAvailable(
ctx,
serverName,
checkNameAvailabilityClient,
)
if err != nil {
return nil, err
} else if !available {
return nil, fmt.Errorf("server name %s is already in use", serverName)
}
} else {
var err error
serverName, err = getAvailableServerName(
ctx,
checkNameAvailabilityClient,
)
if err != nil {
return nil, err
}
}

// Determine administratorLogin. If specified,
// use it; else, use default value "postgres".
adminAccountSettings := pp.GetObject("adminAccountSettings")
adminUsername := adminAccountSettings.GetString("adminUsername")
if adminUsername == "" {
adminUsername = "postgres"
}
// Determine AdministratorLoginPassword. If specified,
// use it; else, generate one.
adminPassword := adminAccountSettings.GetString("adminPassword")
if adminPassword == "" {
adminPassword = generate.NewPassword()
}
return &dbmsInstanceDetails{
ARMDeploymentName: uuid.NewV4().String(),
ServerName: serverName,
AdministratorLogin: adminUsername,
AdministratorLoginPassword: service.SecureString(adminPassword),
}, nil
}

func setupDatabase(
enforceSSL bool,
administratorLogin string,
serverName string,
administratorLoginPassword string,
fullyQualifiedDomainName string,
dbName string,
) error {
db, err := getDBConnection(
enforceSSL,
administratorLogin,
serverName,
administratorLoginPassword,
fullyQualifiedDomainName,
Expand All @@ -74,12 +157,18 @@ func setupDatabase(
); err != nil {
return fmt.Errorf(`error creating role "%s": %s`, dbName, err)
}
// Azure will automatically create a role having name of
// postgreSQL server admin user.
// Here the purpose of granting one role to another role is
// to make admin role to be a member of created db role.
// Please see: https://www.postgresql.org/docs/10/role-membership.html
if _, err = tx.Exec(
fmt.Sprintf("grant %s to postgres", dbName),
fmt.Sprintf("grant %s to %s", dbName, administratorLogin),
); err != nil {
return fmt.Errorf(
`error adding role "%s" to role "postgres": %s`,
`error adding role "%s" to role "%s": %s`,
dbName,
administratorLogin,
err,
)
}
Expand All @@ -105,6 +194,7 @@ func setupDatabase(

func createExtensions(
enforceSSL bool,
administratorLogin string,
serverName string,
administratorLoginPassword string,
fullyQualifiedDomainName string,
Expand All @@ -113,6 +203,7 @@ func createExtensions(
) error {
db, err := getDBConnection(
enforceSSL,
administratorLogin,
serverName,
administratorLoginPassword,
fullyQualifiedDomainName,
Expand Down Expand Up @@ -179,6 +270,7 @@ func buildGoTemplateParameters(
}
p["version"] = version
p["serverName"] = dt.ServerName
p["administratorLogin"] = dt.AdministratorLogin
p["administratorLoginPassword"] = string(dt.AdministratorLoginPassword)
if isSSLRequired(pp) {
p["sslEnforcement"] = enabledARMString
Expand Down
2 changes: 2 additions & 0 deletions pkg/services/postgresql/common_unbind.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import (

func unbind(
enforceSSL bool,
administratorLogin string,
serverName string,
administratorLoginPassword string,
fullyQualifiedDomainName string,
loginName string,
) error {
db, err := getDBConnection(
enforceSSL,
administratorLogin,
serverName,
administratorLoginPassword,
fullyQualifiedDomainName,
Expand Down
1 change: 1 addition & 0 deletions pkg/services/postgresql/database_bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ func (d *databaseManager) Bind(
dt := instance.Details.(*databaseInstanceDetails)
bd, err := createBinding(
isSSLRequired(*instance.Parent.ProvisioningParameters),
pdt.AdministratorLogin,
pdt.ServerName,
string(pdt.AdministratorLoginPassword),
pdt.FullyQualifiedDomainName,
Expand Down
2 changes: 2 additions & 0 deletions pkg/services/postgresql/database_provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func (d *databaseManager) setupDatabase(
dt := instance.Details.(*databaseInstanceDetails)
err := setupDatabase(
isSSLRequired(*instance.Parent.ProvisioningParameters),
pdt.AdministratorLogin,
pdt.ServerName,
string(pdt.AdministratorLoginPassword),
pdt.FullyQualifiedDomainName,
Expand All @@ -89,6 +90,7 @@ func (d *databaseManager) createExtensions(
if len(extensions) > 0 {
err := createExtensions(
isSSLRequired(*instance.Parent.ProvisioningParameters),
pdt.AdministratorLogin,
pdt.ServerName,
string(pdt.AdministratorLoginPassword),
pdt.FullyQualifiedDomainName,
Expand Down
1 change: 1 addition & 0 deletions pkg/services/postgresql/database_unbind.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ func (d *databaseManager) Unbind(
bd := binding.Details.(*bindingDetails)
return unbind(
isSSLRequired(*instance.Parent.ProvisioningParameters),
pdt.AdministratorLogin,
pdt.ServerName,
string(pdt.AdministratorLoginPassword),
pdt.FullyQualifiedDomainName,
Expand Down
2 changes: 1 addition & 1 deletion pkg/services/postgresql/dbms_arm_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var dbmsARMTemplateBytes = []byte(`
"name": "{{ .serverName }}",
"properties": {
"version": "{{.version}}",
"administratorLogin": "postgres",
"administratorLogin": "{{ .administratorLogin }}",
"administratorLoginPassword": "{{ .administratorLoginPassword }}",
"storageProfile": {
"storageMB": {{.storage}},
Expand Down
Loading

0 comments on commit 87d222f

Please sign in to comment.