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 4 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.
Jump to
Jump to file
Failed to load files.
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,147 @@ | ||
package canned | ||
|
||
import ( | ||
"context" | ||
"database/sql" | ||
"fmt" | ||
"net" | ||
"os" | ||
"syscall" | ||
"time" | ||
|
||
_ "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 | ||
|
||
req.WaitingFor = wait.ForAll( | ||
wait.ForLog("database system is ready to accept connections"), | ||
wait.ForListeningPort(port), | ||
) | ||
|
||
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") | ||
} | ||
|
||
db, err := postgresC.GetDriver(ctx) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
for { | ||
_, err := db.ExecContext(ctx, "SELECT 1") | ||
if err != nil { | ||
fmt.Print(err) | ||
if v, ok := err.(*net.OpError); ok { | ||
if v2, ok := (v.Err).(*os.SyscallError); ok { | ||
if v2.Err == syscall.ECONNRESET { | ||
time.Sleep(500 * time.Millisecond) | ||
continue | ||
} | ||
} | ||
} | ||
|
||
return nil, err | ||
} | ||
break | ||
} | ||
} | ||
|
||
return postgresC, nil | ||
} |
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
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.
I guess this is because the postgres container "bounces" and logs the "database system is ready to accept connections" twice?
In the java testcontainer that's handled by having the Wait code have a state and wait for it twice.
I think I prefer this, as it keeps the Waits simple
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.
Yes, I don't understand why the Postgres container is doing that, but basically the process gets restarted, so you'd have to wait for the second
"database system is ready to accept connections"
. So I tried to mimic the Java implementation for the generic JDBC containers.I realised though, the wait strategy I put there is basically pointless? 🤔
I have another proposal to make the same wait process into a proper
SQLStrategy
wait strategy.