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  }