github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/postgresql/config.go (about) 1 package postgresql 2 3 import ( 4 "bytes" 5 "database/sql" 6 "fmt" 7 "log" 8 "sync" 9 "unicode" 10 11 "github.com/hashicorp/errwrap" 12 _ "github.com/lib/pq" //PostgreSQL db 13 ) 14 15 // Config - provider config 16 type Config struct { 17 Host string 18 Port int 19 Database string 20 Username string 21 Password string 22 SSLMode string 23 ApplicationName string 24 Timeout int 25 ConnectTimeoutSec int 26 } 27 28 // Client struct holding connection string 29 type Client struct { 30 username string 31 connStr string 32 33 // PostgreSQL lock on pg_catalog. Many of the operations that Terraform 34 // performs are not permitted to be concurrent. Unlike traditional 35 // PostgreSQL tables that use MVCC, many of the PostgreSQL system 36 // catalogs look like tables, but are not in-fact able to be 37 // concurrently updated. 38 catalogLock sync.RWMutex 39 } 40 41 // NewClient returns new client config 42 func (c *Config) NewClient() (*Client, error) { 43 // NOTE: dbname must come before user otherwise dbname will be set to 44 // user. 45 const dsnFmt = "host=%s port=%d dbname=%s user=%s password=%s sslmode=%s fallback_application_name=%s connect_timeout=%d" 46 47 // Quote empty strings or strings that contain whitespace 48 q := func(s string) string { 49 b := bytes.NewBufferString(`'`) 50 b.Grow(len(s) + 2) 51 var haveWhitespace bool 52 for _, r := range s { 53 if unicode.IsSpace(r) { 54 haveWhitespace = true 55 } 56 57 switch r { 58 case '\'': 59 b.WriteString(`\'`) 60 case '\\': 61 b.WriteString(`\\`) 62 default: 63 b.WriteRune(r) 64 } 65 } 66 67 b.WriteString(`'`) 68 69 str := b.String() 70 if haveWhitespace || len(str) == 2 { 71 return str 72 } 73 return str[1 : len(str)-1] 74 } 75 76 logDSN := fmt.Sprintf(dsnFmt, q(c.Host), c.Port, q(c.Database), q(c.Username), q("<redacted>"), q(c.SSLMode), q(c.ApplicationName), c.ConnectTimeoutSec) 77 log.Printf("[INFO] PostgreSQL DSN: `%s`", logDSN) 78 79 connStr := fmt.Sprintf(dsnFmt, q(c.Host), c.Port, q(c.Database), q(c.Username), q(c.Password), q(c.SSLMode), q(c.ApplicationName), c.ConnectTimeoutSec) 80 client := Client{ 81 connStr: connStr, 82 username: c.Username, 83 } 84 85 return &client, nil 86 } 87 88 // Connect will manually connect/disconnect to prevent a large 89 // number or db connections being made 90 func (c *Client) Connect() (*sql.DB, error) { 91 db, err := sql.Open("postgres", c.connStr) 92 if err != nil { 93 return nil, errwrap.Wrapf("Error connecting to PostgreSQL server: {{err}}", err) 94 } 95 96 return db, nil 97 }