-
-
Notifications
You must be signed in to change notification settings - Fork 523
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
canned postgresql #98
Closed
Closed
Changes from 6 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
1c9f7dd
init postgres
borgoat 3342d68
wait on connection and sql select
borgoat 83971b7
move context out of container
borgoat 6444032
extract req.Started into test
borgoat 189379d
make wait strategy into proper SQLStrategy
borgoat 3aeb27d
the implementation of the wait strategy should not be specific to any…
borgoat d48761f
actually honour the context timeout
borgoat dce361e
Merge branch 'master' into feature/canned-postgresql
borgoat a2d49a8
some docs
borgoat fe037d2
fix build
borgoat 21ff950
actually fix build
borgoat f61fbb2
actually fixed the build this time
borgoat 31a3279
fix Example_GetDriver_postgresqlContainer has malformed example suffix
borgoat 8fbfa6c
try another notation for examples
borgoat a17bb06
rename postgres constants to avoid conflicts
borgoat 5c55d62
Allow using custom dockerfile
gordonbondon 865fdc2
Merge pull request #1 from koalificationio/feature/canned-postgresql
borgoat 21ceb2f
postgresqlContainer made public for better godoc
borgoat File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package canned | ||
|
||
import ( | ||
"context" | ||
"database/sql" | ||
"database/sql/driver" | ||
"fmt" | ||
|
||
"github.com/docker/go-connections/nat" | ||
"github.com/lib/pq" | ||
"github.com/pkg/errors" | ||
testcontainers "github.com/testcontainers/testcontainers-go" | ||
"github.com/testcontainers/testcontainers-go/wait" | ||
) | ||
|
||
const ( | ||
user = "user" | ||
password = "password" | ||
database = "database" | ||
image = "postgres" | ||
defaultTag = "11.5" | ||
port = "5432/tcp" | ||
) | ||
|
||
type PostgreSQLContainerRequest struct { | ||
testcontainers.GenericContainerRequest | ||
User string | ||
Password string | ||
Database string | ||
} | ||
|
||
type postgresqlContainer struct { | ||
Container testcontainers.Container | ||
db *sql.DB | ||
req PostgreSQLContainerRequest | ||
} | ||
|
||
func (c *postgresqlContainer) GetDriver(ctx context.Context) (*sql.DB, error) { | ||
|
||
host, err := c.Container.Host(ctx) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
mappedPort, err := c.Container.MappedPort(ctx, port) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
db, err := sql.Open("postgres", fmt.Sprintf( | ||
"host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", | ||
host, | ||
mappedPort.Int(), | ||
c.req.User, | ||
c.req.Password, | ||
c.req.Database, | ||
)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return db, nil | ||
} | ||
|
||
func PostgreSQLContainer(ctx context.Context, req PostgreSQLContainerRequest) (*postgresqlContainer, error) { | ||
|
||
provider, err := req.ProviderType.GetProvider() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// With the current logic it's not really possible to allow other ports... | ||
req.ExposedPorts = []string{port} | ||
|
||
if req.Env == nil { | ||
req.Env = map[string]string{} | ||
} | ||
|
||
// Set the default values if none were provided in the request | ||
if req.Image == "" { | ||
req.Image = fmt.Sprintf("%s:%s", image, defaultTag) | ||
} | ||
|
||
if req.User == "" { | ||
req.User = user | ||
} | ||
|
||
if req.Password == "" { | ||
req.Password = password | ||
} | ||
|
||
if req.Database == "" { | ||
req.Database = database | ||
} | ||
|
||
req.Env["POSTGRES_USER"] = req.User | ||
req.Env["POSTGRES_PASSWORD"] = req.Password | ||
req.Env["POSTGRES_DB"] = req.Database | ||
|
||
connectorVars := map[string]interface{}{ | ||
"port": port, | ||
"user": req.User, | ||
"password": req.Password, | ||
"database": req.Database, | ||
} | ||
|
||
req.WaitingFor = wait.ForSQL(postgresConnectorFromTarget, connectorVars) | ||
|
||
c, err := provider.CreateContainer(ctx, req.ContainerRequest) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "failed to create container") | ||
} | ||
|
||
postgresC := &postgresqlContainer{ | ||
Container: c, | ||
req: req, | ||
} | ||
|
||
if req.Started { | ||
if err := c.Start(ctx); err != nil { | ||
return postgresC, errors.Wrap(err, "failed to start container") | ||
} | ||
} | ||
|
||
return postgresC, nil | ||
} | ||
|
||
func postgresConnectorFromTarget(ctx context.Context, target wait.StrategyTarget, variables wait.SQLVariables) (driver.Connector, error) { | ||
|
||
host, err := target.Host(ctx) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
mappedPort, err := target.MappedPort(ctx, nat.Port(variables["port"].(string))) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
connString := fmt.Sprintf( | ||
"host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", | ||
host, | ||
mappedPort.Int(), | ||
variables["user"], | ||
variables["password"], | ||
variables["database"], | ||
) | ||
|
||
return pq.NewConnector(connString) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package canned | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
testcontainers "github.com/testcontainers/testcontainers-go" | ||
) | ||
|
||
func TestWriteIntoAPostgreSQLContainerViaDriver(t *testing.T) { | ||
|
||
ctx := context.Background() | ||
|
||
c, err := PostgreSQLContainer(ctx, PostgreSQLContainerRequest{ | ||
Database: "hello", | ||
GenericContainerRequest: testcontainers.GenericContainerRequest{ | ||
Started: true, | ||
}, | ||
}) | ||
if err != nil { | ||
t.Fatal(err.Error()) | ||
} | ||
defer c.Container.Terminate(ctx) | ||
|
||
sqlC, err := c.GetDriver(ctx) | ||
if err != nil { | ||
t.Fatal(err.Error()) | ||
} | ||
|
||
_, err = sqlC.ExecContext(ctx, "CREATE TABLE example ( id integer, data varchar(32) )") | ||
if err != nil { | ||
t.Fatal(err.Error()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package wait | ||
|
||
import ( | ||
"context" | ||
"time" | ||
|
||
"database/sql" | ||
"database/sql/driver" | ||
|
||
"github.com/pkg/errors" | ||
) | ||
|
||
type SQLVariables map[string]interface{} | ||
|
||
type SQLConnectorFromTarget func(ctx context.Context, target StrategyTarget, variables SQLVariables) (driver.Connector, error) | ||
|
||
var _ Strategy = (*SQLStrategy)(nil) | ||
|
||
type SQLStrategy struct { | ||
startupTimeout time.Duration | ||
ConnectorSource SQLConnectorFromTarget | ||
SQLVariables SQLVariables | ||
} | ||
|
||
func NewSQLStrategy(ds SQLConnectorFromTarget, sv SQLVariables) *SQLStrategy { | ||
return &SQLStrategy{ | ||
startupTimeout: defaultStartupTimeout(), | ||
ConnectorSource: ds, | ||
SQLVariables: sv, | ||
} | ||
} | ||
|
||
func ForSQL(ds SQLConnectorFromTarget, sv SQLVariables) *SQLStrategy { | ||
return NewSQLStrategy(ds, sv) | ||
} | ||
|
||
func (ws *SQLStrategy) WaitUntilReady(ctx context.Context, target StrategyTarget) error { | ||
|
||
conn, err := ws.ConnectorSource(ctx, target, ws.SQLVariables) | ||
if err != nil { | ||
return errors.Wrap(err, "could not retrieve the SQL connector from the provided function") | ||
} | ||
|
||
db := sql.OpenDB(conn) | ||
|
||
for { | ||
_, err := db.ExecContext(ctx, "SELECT 1") | ||
if err != nil { | ||
time.Sleep(500 * time.Millisecond) | ||
continue | ||
} | ||
break | ||
} | ||
|
||
return nil | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe there should be a max wait time, just in case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops... you're right. I completely forgot.
I did the same as in
wait/log.go
now. Also added the fluent builders as in LogStrategy for consistency