Skip to content
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
18 changes: 12 additions & 6 deletions src/jetstream/datastore/database_cf_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ func findDatabaseConfig(vcapServices map[string][]VCAPService, db *DatabaseConfi
return false
}

db.Username, db.Password, db.Host, db.Port, db.Database, err = findDatabaseConfigurationFromURI(uri, defaultDBProviderPort(service))
db.Username, db.Password, db.Host, db.Port, db.Database, db.QueryParams, err = findDatabaseConfigurationFromURI(uri, defaultDBProviderPort(service))

if err != nil {
log.Warnf("Failed to find Cloud Foundry service config from `%v` (failed to parse)", DB_URI)
Expand Down Expand Up @@ -197,12 +197,12 @@ func stringInSlice(a string, list []string) bool {
return false
}

func findDatabaseConfigurationFromURI(uri string, defaultPort int) (string, string, string, int, string, error) {
re := regexp.MustCompile(`(?P<provider>.+)://(?P<username>[^:]+)(?::(?P<password>.+))?@(?P<host>[^:]+)(?::(?P<port>.+))?\/(?P<dbname>.+)`)
func findDatabaseConfigurationFromURI(uri string, defaultPort int) (string, string, string, int, string, map[string]string, error) {
re := regexp.MustCompile(`(?P<provider>.+)://(?P<username>[^:]+)(?::(?P<password>.+))?@(?P<host>[^:]+)(?::(?P<port>.+))?\/(?P<dbname>[^?]+)(?:\?(?P<queryparams>.*))*`)
n1 := re.SubexpNames()
matches := re.FindAllStringSubmatch(uri, -1)
if len(matches) < 1 {
return "", "", "", 0, "", errors.New("failed to parse database URI")
return "", "", "", 0, "", map[string]string{}, errors.New("failed to parse database URI")
}

r2 := matches[0]
Expand All @@ -222,9 +222,15 @@ func findDatabaseConfigurationFromURI(uri string, defaultPort int) (string, stri
port = defaultPort
}
dbname := md["dbname"]
queryparamsraw := md["queryparams"]
queryparams := make(map[string]string)
for _, keyvalue := range strings.Split(queryparamsraw, "&") {
if key, value, valid := strings.Cut(keyvalue, "="); valid {
queryparams[key] = value
}
}

return username, password, host, port, dbname, nil

return username, password, host, port, dbname, queryparams, nil
}

func defaultDBProviderPort(service VCAPService) int {
Expand Down
23 changes: 12 additions & 11 deletions src/jetstream/datastore/datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,18 @@ func GetColumnNames(databaseName string, exclude ...string) []string {

// DatabaseConfig represents the connection configuration parameters
type DatabaseConfig struct {
DatabaseProvider string `configName:"DATABASE_PROVIDER"`
Username string `configName:"DB_USER"`
Password string `configName:"DB_PASSWORD"`
Database string `configName:"DB_DATABASE_NAME"`
Host string `configName:"DB_HOST"`
Port int `configName:"DB_PORT"`
SSLMode string `configName:"DB_SSL_MODE"`
ConnectionTimeoutInSecs int `configName:"DB_CONNECT_TIMEOUT_IN_SECS"`
SSLCertificate string `configName:"DB_CERT"`
SSLKey string `configName:"DB_CERT_KEY"`
SSLRootCertificate string `configName:"DB_ROOT_CERT"`
DatabaseProvider string `configName:"DATABASE_PROVIDER"`
Username string `configName:"DB_USER"`
Password string `configName:"DB_PASSWORD"`
Database string `configName:"DB_DATABASE_NAME"`
Host string `configName:"DB_HOST"`
Port int `configName:"DB_PORT"`
SSLMode string `configName:"DB_SSL_MODE"`
ConnectionTimeoutInSecs int `configName:"DB_CONNECT_TIMEOUT_IN_SECS"`
SSLCertificate string `configName:"DB_CERT"`
SSLKey string `configName:"DB_CERT_KEY"`
SSLRootCertificate string `configName:"DB_ROOT_CERT"`
QueryParams map[string]string `configName:"DB_QUERY_PARAMS"`
}

// SSLValidationMode is the PostgreSQL driver SSL validation modes
Expand Down
39 changes: 38 additions & 1 deletion src/jetstream/datastore/datastore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ func TestDatastore(t *testing.T) {

mockEnvVarsMap := make(map[string]string)
mockEnvVarsMap["DB_SSL_MODE"] = mockSSLModeVerifyCA
mockEnvVarsMap["VCAP_SERVICES"] = `{"cf-postgresql-service": [ { "name": "mock-stratos-ssl", "credentials": { "cacrt": "mockcert", "host": "mockhost", "name": "mockname", "password": "mockpassword", "port": 5432, "username": "mockusername", "uri": "postgres://mockuser:mockpassword@mockhost:5432/mockname" } } ] }`
mockEnvVarsMap["VCAP_SERVICES"] = `{"cf-postgresql-service": [ { "name": "mock-stratos-ssl", "credentials": { "cacrt": "mockcert", "host": "mockhost", "name": "mockname", "password": "mockpassword", "port": 5432, "username": "mockusername", "uri": "postgres://mockusername:mockpassword@mockhost:5432/mockname?mockquery=true" } } ] }`

mockVarSet := env.NewVarSet(env.WithMapLookup(mockEnvVarsMap))

Expand All @@ -453,5 +453,42 @@ func TestDatastore(t *testing.T) {
So(mockDatabaseConfigSSL.SSLMode, ShouldEqual, mockSSLModeVerifyCA)
})
})

Convey("when the cloudfoundry database config is present but incomplete", func() {

Convey("err will be nil and fallback to uri will be used", func() {
Convey("with query params present", func() {
mockEnvVarsMap["VCAP_SERVICES"] = `{"cf-postgresql-service": [ { "name": "mock-stratos-ssl", "credentials": { "cacrt": "mockcert", "host": "mockhost", "password": "mockpassword", "port": 5432, "username": "mockusername", "uri": "postgres://mockusername:mockpassword@mockhost:5432/mockname?mockquery=true" } } ] }`
mockVarSet := env.NewVarSet(env.WithMapLookup(mockEnvVarsMap))

_, err := ParseCFEnvs(&mockDatabaseConfigSSL, mockVarSet)
So(err, ShouldBeNil)
So(mockDatabaseConfigSSL.SSLRootCertificate, ShouldContainSubstring, "postgres-ssl-")
So(mockDatabaseConfigSSL.SSLRootCertificate, ShouldEndWith, ".crt")
So(mockDatabaseConfigSSL.Username, ShouldEqual, "mockusername")
So(mockDatabaseConfigSSL.Password, ShouldEqual, "mockpassword")
So(mockDatabaseConfigSSL.Database, ShouldEqual, "mockname")
So(mockDatabaseConfigSSL.Host, ShouldEqual, "mockhost")
So(mockDatabaseConfigSSL.SSLMode, ShouldEqual, mockSSLModeVerifyCA)
So(mockDatabaseConfigSSL.QueryParams, ShouldEqual, map[string]string{"mockquery": "true"})
})

Convey("without query params present", func() {
mockEnvVarsMap["VCAP_SERVICES"] = `{"cf-postgresql-service": [ { "name": "mock-stratos-ssl", "credentials": { "cacrt": "mockcert", "host": "mockhost", "password": "mockpassword", "port": 5432, "username": "mockusername", "uri": "postgres://mockusername:mockpassword@mockhost:5432/mockname" } } ] }`
mockVarSet := env.NewVarSet(env.WithMapLookup(mockEnvVarsMap))

_, err := ParseCFEnvs(&mockDatabaseConfigSSL, mockVarSet)
So(err, ShouldBeNil)
So(mockDatabaseConfigSSL.SSLRootCertificate, ShouldContainSubstring, "postgres-ssl-")
So(mockDatabaseConfigSSL.SSLRootCertificate, ShouldEndWith, ".crt")
So(mockDatabaseConfigSSL.Username, ShouldEqual, "mockusername")
So(mockDatabaseConfigSSL.Password, ShouldEqual, "mockpassword")
So(mockDatabaseConfigSSL.Database, ShouldEqual, "mockname")
So(mockDatabaseConfigSSL.Host, ShouldEqual, "mockhost")
So(mockDatabaseConfigSSL.SSLMode, ShouldEqual, mockSSLModeVerifyCA)
So(mockDatabaseConfigSSL.QueryParams, ShouldEqual, map[string]string{})
})
})
})
})
}