Skip to content

Commit

Permalink
add mysql user-provided services handling
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesClonk committed Nov 21, 2019
1 parent 0a5009e commit 23510f5
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 46 deletions.
5 changes: 3 additions & 2 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,12 @@ export VCAP_SERVICES='{
"name": "my_mysql_db",
"database": "mysql",
"username": "root",
"password": "my-secret-pw"
"password": "my-secret-pw",
"database_uri": "mysql://127.0.0.1:3306"
},
"syslog_drain_url": null,
"volume_mounts": [],
"label": "mysql",
"label": "user-provided",
"provider": null,
"plan": "usage",
"name": "my_mysql_db",
Expand Down
2 changes: 1 addition & 1 deletion manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ applications:

# ### push either as docker image
docker:
image: jamesclonk/backman:1.7.2 # choose version from https://hub.docker.com/r/jamesclonk/backman/tags, or 'latest'
image: jamesclonk/backman:1.8.0 # choose version from https://hub.docker.com/r/jamesclonk/backman/tags, or 'latest'
# ### or as buildpack/src
# buildpacks:
# - https://github.com/cloudfoundry/apt-buildpack
Expand Down
28 changes: 7 additions & 21 deletions service/mysql/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"io"
"os"
"os/exec"
"strconv"
"strings"
"sync"
"time"
Expand All @@ -33,21 +32,8 @@ func Backup(ctx context.Context, s3 *s3.Client, service util.Service, binding *c

state.BackupStart(service)

host, _ := binding.CredentialString("host")
database, _ := binding.CredentialString("database")
username, _ := binding.CredentialString("username")
password, _ := binding.CredentialString("password")
port, _ := binding.CredentialString("port")
if len(port) == 0 {
switch p := binding.Credentials["port"].(type) {
case float64:
port = strconv.Itoa(int(p))
case int, int32, int64:
port = strconv.Itoa(p.(int))
}
}

os.Setenv("MYSQL_PWD", password)
credentials := GetCredentials(binding)
os.Setenv("MYSQL_PWD", credentials.Password)

// prepare mysqldump command
var command []string
Expand All @@ -56,14 +42,14 @@ func Backup(ctx context.Context, s3 *s3.Client, service util.Service, binding *c
command = append(command, "--quick")
command = append(command, "--skip-add-locks")
command = append(command, "-h")
command = append(command, host)
command = append(command, credentials.Hostname)
command = append(command, "-P")
command = append(command, port)
command = append(command, credentials.Port)
command = append(command, "-u")
command = append(command, username)
if len(database) > 0 {
command = append(command, credentials.Username)
if len(credentials.Database) > 0 {
command = append(command, "--databases")
command = append(command, database)
command = append(command, credentials.Database)
} else {
command = append(command, "--all-databases")
}
Expand Down
106 changes: 106 additions & 0 deletions service/mysql/binding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package mysql

import (
"net"
"net/url"
"regexp"
"strconv"
"strings"

"github.com/cloudfoundry-community/go-cfenv"
)

type Credentials struct {
Hostname string
Database string
Username string
Password string
Port string
}

func IsMySQLBinding(binding *cfenv.Service) bool {
c := GetCredentials(binding)
if len(c.Hostname) > 0 &&
len(c.Database) > 0 &&
len(c.Username) > 0 &&
len(c.Password) > 0 &&
len(c.Port) > 0 {
for key := range binding.Credentials {
switch key {
case "database_uri", "jdbcUrl", "jdbc_url", "url", "uri":
if uri, _ := binding.CredentialString(key); len(uri) > 0 && strings.Contains(uri, "mysql://") {
return true
}
}
}
}
return false
}

func GetCredentials(binding *cfenv.Service) *Credentials {
host, _ := binding.CredentialString("host")
hostname, _ := binding.CredentialString("hostname")
database, _ := binding.CredentialString("database")
username, _ := binding.CredentialString("username")
password, _ := binding.CredentialString("password")
port, _ := binding.CredentialString("port")

if len(port) == 0 {
switch p := binding.Credentials["port"].(type) {
case float64:
port = strconv.Itoa(int(p))
case int, int32, int64:
port = strconv.Itoa(p.(int))
}
}

// figure out hostname & port from host if still missing
if len(hostname) == 0 || len(port) == 0 {
if len(host) > 0 && strings.Contains(host, ":") {
if u, err := url.Parse(host); err == nil {
hostname = u.Hostname()
port = u.Port()
}
}
}

// figure out credentials from URL if still missing
for key := range binding.Credentials {
switch key {
case "database_uri", "jdbcUrl", "jdbc_url", "url", "uri":
if uri, _ := binding.CredentialString(key); len(uri) > 0 && strings.Contains(uri, "mysql://") {
if u, err := url.Parse(uri); err == nil {
if len(username) == 0 {
username = u.User.Username()
}
if len(password) == 0 {
p, _ := u.User.Password()
password = p
}

h, p, _ := net.SplitHostPort(u.Host)
if len(hostname) == 0 {
hostname = h
}
if len(port) == 0 {
port = p
}

if len(database) == 0 {
database = strings.TrimPrefix(u.Path, "/")
rx := regexp.MustCompile(`([^\?]*)\?.*`) // trim connection options
database = rx.ReplaceAllString(database, "${1}")
}
}
}
}
}

return &Credentials{
Hostname: hostname,
Database: database,
Username: username,
Password: password,
Port: port,
}
}
26 changes: 6 additions & 20 deletions service/mysql/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"fmt"
"os"
"os/exec"
"strconv"
"strings"

"github.com/cloudfoundry-community/go-cfenv"
Expand All @@ -27,32 +26,19 @@ func Restore(ctx context.Context, s3 *s3.Client, service util.Service, binding *

state.RestoreStart(service)

host, _ := binding.CredentialString("host")
database, _ := binding.CredentialString("database")
username, _ := binding.CredentialString("username")
password, _ := binding.CredentialString("password")
port, _ := binding.CredentialString("port")
if len(port) == 0 {
switch p := binding.Credentials["port"].(type) {
case float64:
port = strconv.Itoa(int(p))
case int, int32, int64:
port = strconv.Itoa(p.(int))
}
}

os.Setenv("MYSQL_PWD", password)
credentials := GetCredentials(binding)
os.Setenv("MYSQL_PWD", credentials.Password)

// prepare mysql restore command
var command []string
command = append(command, "mysql")
command = append(command, database)
command = append(command, credentials.Database)
command = append(command, "-h")
command = append(command, host)
command = append(command, credentials.Hostname)
command = append(command, "-P")
command = append(command, port)
command = append(command, credentials.Port)
command = append(command, "-u")
command = append(command, username)
command = append(command, credentials.Username)

log.Debugf("executing mysql restore command: %v", strings.Join(command, " "))
cmd := exec.CommandContext(ctx, command[0], command[1:]...)
Expand Down
4 changes: 2 additions & 2 deletions service/postgres/binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func IsPostgresBinding(binding *cfenv.Service) bool {
len(c.Username) > 0 &&
len(c.Password) > 0 &&
len(c.Port) > 0 {
for key, _ := range binding.Credentials {
for key := range binding.Credentials {
switch key {
case "database_uri", "jdbcUrl", "jdbc_url", "url", "uri":
if uri, _ := binding.CredentialString(key); len(uri) > 0 && strings.Contains(uri, "postgres://") {
Expand Down Expand Up @@ -64,7 +64,7 @@ func GetCredentials(binding *cfenv.Service) *Credentials {
}

// figure out credentials from URL if still missing
for key, _ := range binding.Credentials {
for key := range binding.Credentials {
switch key {
case "database_uri", "jdbcUrl", "jdbc_url", "url", "uri":
if uri, _ := binding.CredentialString(key); len(uri) > 0 && strings.Contains(uri, "postgres://") {
Expand Down
4 changes: 4 additions & 0 deletions service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/swisscom/backman/config"
"github.com/swisscom/backman/log"
"github.com/swisscom/backman/s3"
"github.com/swisscom/backman/service/mysql"
"github.com/swisscom/backman/service/postgres"
"github.com/swisscom/backman/service/util"
"github.com/swisscom/backman/state"
Expand Down Expand Up @@ -63,6 +64,9 @@ func (s *Service) parseServices() {
// can it be identified as a custom postgres binding?
if postgres.IsPostgresBinding(&service) {
service.Label = "postgres"
// or a mysql binding?
} else if mysql.IsMySQLBinding(&service) {
service.Label = "mysql"
} else {
continue // cannot handle service binding
}
Expand Down

0 comments on commit 23510f5

Please sign in to comment.