Documentation ¶
Overview ¶
Package ghostgres is a utility to start and control a PostgreSQL database. The expected usage is in tests where it allows for easy startup and shutdown of a database. The easiest way is to have Ghostgres build a template from which it can clone a fresh database when you need one. In order to do this run
// Fetch the package go get -t github.com/surullabs/ghostgres // Run tests and create a default postgres cluster that will be used // as a template for future clusters. go test github.com/surullabs/ghostgres --ghostgres_pg_bin_dir=<path_to_your_postgres_bin_dir>
In your test code you can now use (with appropriate error checks)
// Create a cloned cluster from the default template in a temporary directory cluster, err := ghostgres.FromDefault("") if err != nil { // fail } // Start the postgres server err = cluster.Start() // Handle error // Remember to stop it! This will delete the temporary directory. defer cluster.Stop() // Connect to the running postgres server through a unix socket. var connStr string connStr, err = cluster.TestConnectString() // Handle error db, err := sql.Open("postgres", fmt.Sprintf("%s dbname=postgres", connStr))
Please consult the examples for other sample usage.
Example ¶
// Using a postgres cluster with test defaults in a temporary directory tempDir, err := ioutil.TempDir("", "ghostgres") if err != nil { log.Fatal(err) return } defer func() { os.RemoveAll(tempDir) }() // A postgres cluster which will be created in tempDir and use binaries // from /usr/lib/postgresql/9.3/bin. log.Fatal will be run if any errors // occur on any of the exported methods of ghostgres. // This can also be an instance of testing.T.Fatal to automatically abort // tests on error master := &PostgresCluster{ Config: TestConfig, DataDir: tempDir, BinDir: "/usr/lib/postgresql/9.3/bin", } // Initialize the cluster master.Init() master.Start() defer master.Stop() // Now use the database db, err := sql.Open("postgres", fmt.Sprintf("%s dbname=postgres", testcheck.Return(master.TestConnectString()).(string))) if err != nil { log.Fatal(err) return } defer db.Close() master.Stop()
Output:
Example (Cloning) ¶
// Using a postgres cluster with test defaults that is cloned from a previously known // location. // The temporary directory will be used for the clone tempDir, err := ioutil.TempDir("", "ghostgres") if err != nil { log.Fatal(err) return } defer func() { os.RemoveAll(tempDir) }() // A postgres cluster which will be loaded from testdata/templatedb and use binaries // from /usr/lib/postgresql/9.3/bin. log.Fatal will be run if any errors // occur on any of the exported methods of ghostgres. // This can also be an instance of testing.T.Fatal to automatically abort // tests on error master := &PostgresCluster{ Config: TestConfig, DataDir: "testdata/templatedb", BinDir: "/usr/lib/postgresql/9.3/bin", } // Initialize the cluster if needed. This allows you to create a template // easily. You can then choose to store the template in version control // but be warned that it takes up to 33 MB. master.InitIfNeeded() // Create a clone which we will use for tests. clone, _ := master.Clone(tempDir) defer clone.Stop() // Now use the database db, err := sql.Open("postgres", fmt.Sprintf("%s dbname=postgres", testcheck.Return(clone.TestConnectString()))) if err != nil { log.Fatal(err) return } defer db.Close() clone.Stop()
Output:
Index ¶
- Constants
- Variables
- func Delete(dir, name string) (err error)
- type ConfigOpt
- type FailureHandler
- type PostgresCluster
- func (p *PostgresCluster) Clone(dest string) (c *PostgresCluster, err error)
- func (cluster *PostgresCluster) Freeze(dir, name string) (err error)
- func (p *PostgresCluster) Init() (err error)
- func (p *PostgresCluster) InitIfNeeded() (err error)
- func (p *PostgresCluster) Initialized() bool
- func (p *PostgresCluster) Port() (portVal int, err error)
- func (p *PostgresCluster) Running() bool
- func (p *PostgresCluster) SocketDir() (str string, err error)
- func (p *PostgresCluster) SocketFile() (socketFile string, err error)
- func (p *PostgresCluster) Start() (err error)
- func (p *PostgresCluster) Stop() (err error)
- func (p *PostgresCluster) TestConnectString() (str string, err error)
- func (p *PostgresCluster) Wait() (err error)
- func (p *PostgresCluster) WaitTillServing(timeout time.Duration) (err error)
Examples ¶
Constants ¶
const DefaultTemplate = ""
DefaultTemplate is a convenience value used to refer to a default template. If used the value of the --ghostgres_template flag will be used as the template name.
const DefaultTemplateDir = ""
DefaultTemplateDir is a convenience value used to refer to the installed location of the ghostgres package. It is to be used as the root location if you would like to have ghostgres manage all template copies.
const TestLogFileName = "postgresql-tests.log"
TestLogFileName is the file name to which PostgresSQL will log if TestConfigWithLogging is used. The path is relative to DataDir/pg_log
Variables ¶
var LoggingConfig = []ConfigOpt{ {"logging_collector", "on", "Collecting query logs can be useful to debug tests"}, {"log_filename", TestLogFileName, "Well known file name to make log parsing easy in tests"}, {"log_statement", "all", "Log all statements"}, {"log_directory", "pg_log", "Logging directory"}, }
LoggingConfig provides useful defaults for logging in tests.
var TestConfig = []ConfigOpt{
{"port", "5432", "Use the default port since we disable TCP listen"},
{"listen_addresses", "''", "Do not listen on TCP. Instead use a unix domain socket for communication"},
{"ssl", "false", "No ssl for unit tests"},
{"shared_buffers", "10MB", "Smaller shared buffers to reduce resource usage"},
{"fsync", "off", "Ignore system crashes, since tests will fail in that event anyway"},
{"autovacuum", "off", "Don't run autovacuum for tests"},
{"full_page_writes", "off", "Useless without fsync"},
}
TestConfig provides some sane defaults for a cluster to be used in unit tests.
var TestConfigWithLogging = append(TestConfig, LoggingConfig...)
TestConfigWithLogging combines TestConfig and LoggingConfig
Functions ¶
Types ¶
type ConfigOpt ¶
ConfigOpt represents a PostgreSQL configuration option It is used both to specify command line arguments as well as populate the postgresql.conf file.
type FailureHandler ¶
type FailureHandler func(...interface{})
FailureHandler defines a function to be called when errors occur. Setting one makes using PostgresCluster easier in tests.
type PostgresCluster ¶
type PostgresCluster struct { // Key value pairs used to create a postgresql.conf file. They are // written out as // key = value # comment Config []ConfigOpt // Directory in which to initialize the cluster. DataDir string // A set of options to be used when creating the cluster. These // will be passed directly to initdb. A example would be // {"--auth", "trust", ""}, {"--nosync", "", ""} to enable easy testing. // For more details on the command line flags see // http://www.postgresql.org/docs/9.3/static/app-initdb.html InitOpts []ConfigOpt // A set of options to be used when running the postgres server. RunOpts []ConfigOpt // Directory containing postgres binaries BinDir string // The password for the super user Password string // contains filtered or unexported fields }
PostgresCluster describes a single PostgreSQL cluster
func FromDefault ¶
func FromDefault(dest string) (p *PostgresCluster, err error)
FromDefault is equivalent to FromTemplate(DefaultTemplateDir, DefaultTemplate, dest)
func FromTemplate ¶
func FromTemplate(dir, name, dest string) (p *PostgresCluster, err error)
FromTemplate will attempt to clone a cluster from a template located at
%dir%/%name%/%pg_version%/
where dir and name have the same behaviour as in Freeze(dir,name).
If the defaults don't exist an error will be returned. Please call Freeze(dir, name) first before calling FromTemplate.
If dest is empty a temporary directory is created for the clone and will be deleted when Stop() is called on the cluster.
func (*PostgresCluster) Clone ¶
func (p *PostgresCluster) Clone(dest string) (c *PostgresCluster, err error)
Clone clones a previous postgres database by copying the entire directory This currently only works on systems which have a cp command. This will not work if the destination directory exists.
func (*PostgresCluster) Freeze ¶
func (cluster *PostgresCluster) Freeze(dir, name string) (err error)
Freeze will save a template to
%dir%/%name%/%pg_version%/
where
%dir% directory into which to freeze. This will create a copy of the cluster into %dir%/data If %dir% is empty <path_to_ghostgres>/testdata/template is used. %name% is the value of the parameter 'name'. If empty the value of the ghostgres_template flag is used. %pg_version% is the result of calling PostgresVersion()
If a frozen template exists it will return an error
func (*PostgresCluster) Init ¶
func (p *PostgresCluster) Init() (err error)
Init will run initdb to create the cluster in the specified directory. Init will return an error if the directory contains an existing cluster. Use InitIfNeeded() to skip initialization of existing clusters.
Please note that this can be time consuming and it is recommended that a golden version of a database is first initialized outside of the test system and then used as a source for cloning using Clone(string). A newly initialized cluster usually takes up about 33 MB of space. One potential option is to have the golden version be initialized in a location that will not be committed into a source repository. Use InitIfNeeded instead of Init and always use Clone(string) and only call Start() on the clone. This allows a single golden copy to be shared among multiple tests with fast start times.
func (*PostgresCluster) InitIfNeeded ¶
func (p *PostgresCluster) InitIfNeeded() (err error)
InitIfNeeded calls Init() if a call to Initialized returns false.
func (*PostgresCluster) Initialized ¶
func (p *PostgresCluster) Initialized() bool
Initialized checks if a cluster has been initialized in the data directory. It uses the existence of the postgresql.conf file as a signal that the cluster has been initialized.
func (*PostgresCluster) Port ¶
func (p *PostgresCluster) Port() (portVal int, err error)
Port attempts to parse a port from the provided config options and returns the parsed port or an error if no port could be parsed..
func (*PostgresCluster) Running ¶
func (p *PostgresCluster) Running() bool
Running will return true if the server is running. Please note that this is still not very accurate as it merely checks if the server has been started.
func (*PostgresCluster) SocketDir ¶
func (p *PostgresCluster) SocketDir() (str string, err error)
SocketDir returns the location of the postgres unix socket directory. Note: This will panic if it is unable to find the absolute path to the socket directory.
func (*PostgresCluster) SocketFile ¶
func (p *PostgresCluster) SocketFile() (socketFile string, err error)
SocketFile returns the location of the postgres socket file
func (*PostgresCluster) Start ¶
func (p *PostgresCluster) Start() (err error)
Start starts the postgres database. It will add the following extra flags in addition to the RunOpts provided.
-D p.DataDir // Use the specified data directory -k p.DataDir // Use the data directory as the socket directory for unix sockets. -c config_file=p.DataDir/postgresql.confg // Custom config file.
It does not attempt to read the config file to determine the data directory or the socket directory.
func (*PostgresCluster) Stop ¶
func (p *PostgresCluster) Stop() (err error)
Stop stops the postgres cluster if it is running by sending it a SIGTERM signal. This will request a slow shutdown and the postgres server will wait for all existing connections to close. It is an error to call this if the server is not running.
func (*PostgresCluster) TestConnectString ¶
func (p *PostgresCluster) TestConnectString() (str string, err error)
TestConnectString returns a connect string to use when using TestConfig or an error if unable to build the string.
func (*PostgresCluster) Wait ¶
func (p *PostgresCluster) Wait() (err error)
Wait waits for a running postgres server to terminate. It is useful when you wish to freeze a test and inspect the database. Once it is frozen it can be stopped using
pg_ctl -D p.DataDir stop
It will return an error if the server exits with any return code other than 0 or as a result of SIGTERM. It is an error to call this before calling Start.
func (*PostgresCluster) WaitTillServing ¶
func (p *PostgresCluster) WaitTillServing(timeout time.Duration) (err error)
WaitTillServing waits for a duration of timeout for the postgres server to start. It must be called after a call to Start() and before a call to Stop() or Wait() It polls for the existence of the socket file every 10ms to detect if the server is running and accessible and will return an error if it cannot detect the server within timeout.