Skip to content

Commit

Permalink
Merge pull request #13 from orlangure/api-changes
Browse files Browse the repository at this point in the history
Improve API (breaking changes)
  • Loading branch information
orlangure authored Apr 9, 2020
2 parents dd5878c + ce32f85 commit 9ddc33e
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 27 deletions.
16 changes: 16 additions & 0 deletions container.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,19 @@ func (c *Container) Address(name string) string {
p := c.Ports.Get(name)
return fmt.Sprintf("%s:%d", c.Host, p.Port)
}

// DefaultAddress return Address() with DefaultPort
func (c *Container) DefaultAddress() string {
return c.Address(DefaultPort)
}

// Port is a convenience function that returns port number with the provided
// name
func (c *Container) Port(name string) int {
return c.Ports.Get(name).Port
}

// DefaultPort returns Port() with DefaultPort
func (c *Container) DefaultPort() int {
return c.Port(DefaultPort)
}
42 changes: 28 additions & 14 deletions gnomock.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ import (

const defaultTag = "latest"

// Start creates a new container using provided image and binds random ports
// on the host to the provided ports inside the container. Image may include
// tag, which is set to "latest" by default. Optional configuration is
// StartCustom creates a new container using provided image and binds random
// ports on the host to the provided ports inside the container. Image may
// include tag, which is set to "latest" by default. Optional configuration is
// available through Option functions. The returned container must be stopped
// when no longer needed using its Stop() method
func Start(image string, ports NamedPorts, opts ...Option) (c *Container, err error) {
func StartCustom(image string, ports NamedPorts, opts ...Option) (c *Container, err error) {
config := buildConfig(opts...)
image = buildImage(image, config.tag)

Expand Down Expand Up @@ -101,28 +101,42 @@ func closeLogReader(logReader io.ReadCloser, g *errgroup.Group) func() error {
}
}

// StartPreset creates a container using the provided Preset. The Preset
// provides its own Options to configure Gnomock container. Usually this is
// enough, but it is still possible to extend/override Preset options with new
// values. For example, wait timeout defined in the Preset, if at all, might be
// not enough for this particular usage, so it can't be changed during this
// call.
// Start creates a container using the provided Preset. The Preset provides its
// own Options to configure Gnomock container. Usually this is enough, but it
// is still possible to extend/override Preset options with new values. For
// example, wait timeout defined in the Preset, if at all, might be not enough
// for this particular usage, so it can't be changed during this call.
//
// All provided Options are applied. First, Preset options are applied. Then,
// custom Options. If both Preset and custom Options change the same
// configuration, custom Options are used
func StartPreset(p Preset, opts ...Option) (*Container, error) {
func Start(p Preset, opts ...Option) (*Container, error) {
presetOpts := p.Options()

mergedOpts := make([]Option, 0, len(opts)+len(presetOpts))
mergedOpts = append(mergedOpts, presetOpts...)
mergedOpts = append(mergedOpts, opts...)

return Start(p.Image(), p.Ports(), mergedOpts...)
return StartCustom(p.Image(), p.Ports(), mergedOpts...)
}

// Stop stops this container and lets docker to remove it from the system
func Stop(c *Container) error {
// Stop stops the provided container and lets docker remove them from the
// system. Stop returns an error if any one of the containers couldn't stop
func Stop(cs ...*Container) error {
var g errgroup.Group

for _, c := range cs {
container := c

g.Go(func() error {
return stop(container)
})
}

return g.Wait()
}

func stop(c *Container) error {
if c == nil {
return nil
}
Expand Down
14 changes: 7 additions & 7 deletions gnomock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestGnomock_happyFlow(t *testing.T) {
"web80": gnomock.TCP(goodPort80),
"web8080": gnomock.TCP(goodPort8080),
}
container, err := gnomock.Start(
container, err := gnomock.StartCustom(
testImage, namedPorts,
gnomock.WithHealthCheckInterval(time.Microsecond*500),
gnomock.WithHealthCheck(healthcheck),
Expand Down Expand Up @@ -56,7 +56,7 @@ func TestGnomock_happyFlow(t *testing.T) {
func TestGnomock_wrongPort(t *testing.T) {
t.Parallel()

container, err := gnomock.Start(
container, err := gnomock.StartCustom(
testImage, gnomock.DefaultTCP(badPort),
gnomock.WithHealthCheck(healthcheck),
gnomock.WithWaitTimeout(time.Millisecond*50),
Expand All @@ -80,7 +80,7 @@ func TestGnomock_cancellation(t *testing.T) {
cancel()
}()

container, err := gnomock.Start(
container, err := gnomock.StartCustom(
testImage, gnomock.DefaultTCP(badPort),
gnomock.WithHealthCheck(healthcheck),
gnomock.WithContext(ctx),
Expand All @@ -96,7 +96,7 @@ func TestGnomock_cancellation(t *testing.T) {
func TestGnomock_defaultHealthcheck(t *testing.T) {
t.Parallel()

container, err := gnomock.Start(testImage, gnomock.DefaultTCP(badPort))
container, err := gnomock.StartCustom(testImage, gnomock.DefaultTCP(badPort))

defer func() {
require.NoError(t, gnomock.Stop(container))
Expand All @@ -114,7 +114,7 @@ func TestGnomock_initError(t *testing.T) {
return errNope
}

container, err := gnomock.Start(
container, err := gnomock.StartCustom(
testImage, gnomock.DefaultTCP(goodPort80),
gnomock.WithInit(initWithErr),
)
Expand All @@ -129,7 +129,7 @@ func TestGnomock_initError(t *testing.T) {
func TestGnomock_cantStart(t *testing.T) {
t.Parallel()

container, err := gnomock.Start(
container, err := gnomock.StartCustom(
"docker.io/orlangure/noimage",
gnomock.DefaultTCP(goodPort80),
)
Expand All @@ -144,7 +144,7 @@ func TestGnomock_withLogWriter(t *testing.T) {

r, w := io.Pipe()

container, err := gnomock.Start(
container, err := gnomock.StartCustom(
testImage, gnomock.DefaultTCP(goodPort80),
gnomock.WithLogWriter(w),
)
Expand Down
54 changes: 54 additions & 0 deletions parallel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package gnomock

import "golang.org/x/sync/errgroup"

// InParallel begins parallel preset execution setup. Use Start to add more
// presets with their configuration to parallel execution, and Go() in the end
// to kick-off everything
func InParallel() *Parallel {
return &Parallel{}
}

type configuredPreset struct {
Preset

opts []Option
}

// Parallel is a builder object that configures parallel preset execution
type Parallel struct {
presets []configuredPreset
}

// Start adds the provided preset with its configuration to the parallel
// execution kicked-off by Go(), together with other added presets
func (b *Parallel) Start(p Preset, opts ...Option) *Parallel {
b.presets = append(b.presets, configuredPreset{p, opts})

return b
}

// Go kicks-off parallel preset execution. Returned containers are in the same
// order as they were added with Start. An error is returned if any of the
// containers failed to start and become available. Even if Go() returns an
// errors, there still might be containers created in the process, and it is
// callers responsibility to Stop them
func (b *Parallel) Go() ([]*Container, error) {
var g errgroup.Group

containers := make([]*Container, len(b.presets))

for i, preset := range b.presets {
containerIndex := i
p := preset

g.Go(func() error {
c, err := Start(p.Preset, p.opts...)
containers[containerIndex] = c

return err
})
}

return containers, g.Wait()
}
28 changes: 25 additions & 3 deletions preset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,33 @@ import (
"github.com/stretchr/testify/require"
)

func TestPreset_parallel(t *testing.T) {
t.Parallel()

containers, err := gnomock.InParallel().
Start(&testPreset{testImage}, gnomock.WithHealthCheck(healthcheck)).
Start(&testPreset{testImage}, gnomock.WithHealthCheck(healthcheck)).
Start(&testPreset{testImage}, gnomock.WithHealthCheck(healthcheck)).
Start(&testPreset{testImage}, gnomock.WithHealthCheck(healthcheck)).
Start(&testPreset{testImage}, gnomock.WithHealthCheck(healthcheck)).
Go()

defer func() { require.NoError(t, gnomock.Stop(containers...)) }()

require.NoError(t, err)
require.Len(t, containers, 5)

for _, c := range containers {
require.NoError(t, callRoot("http://"+c.Address("web80")))
require.NoError(t, callRoot("http://"+c.Address("web8080")))
}
}

func TestPreset(t *testing.T) {
t.Parallel()

p := &testPreset{testImage}
container, err := gnomock.StartPreset(p)
container, err := gnomock.Start(p)

defer func(c *gnomock.Container) {
require.NoError(t, gnomock.Stop(c))
Expand All @@ -21,7 +43,7 @@ func TestPreset(t *testing.T) {
// by default, testPreset always fails its healthcheck
require.Error(t, err)

container, err = gnomock.StartPreset(p, gnomock.WithHealthCheck(healthcheck))
container, err = gnomock.Start(p, gnomock.WithHealthCheck(healthcheck))

defer func(c *gnomock.Container) {
require.NoError(t, gnomock.Stop(c))
Expand All @@ -35,7 +57,7 @@ func TestPreset_overrideTag(t *testing.T) {
t.Parallel()

p := &testPreset{testImage + ":latest"}
container, err := gnomock.StartPreset(p, gnomock.WithTag("bad"))
container, err := gnomock.Start(p, gnomock.WithTag("bad"))

defer func() {
require.NoError(t, gnomock.Stop(container))
Expand Down
22 changes: 19 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,30 @@ Gnomock implementation. Below is a sample

```go
p := mockredis.Preset()
container, _ := gnomock.StartPreset(p)
container, _ := gnomock.Start(p)

defer func() { _ = gnomock.Stop(container) }()

addr := container.Address(gnomock.DefaultPort)
addr := container.DefaultAddress()
client := redis.NewClient(&redis.Options{Addr: addr})
```

With Gnomock it is easy to setup complex environments using multiple presets.
It could be done in parallel:

```go
containers, err := gnomock.InParallel().
Start(mockredis.Preset()).
Start(mockpostgres.Preset(), mockpostgres.WithUser("user", "pass")).
Start(
localstack.Preset(),
localstack.WithServices(localstack.S3, localstack.SES),
).
Go()

defer func() { _ = gnomock.Stop(containers...) }()
```

## Official presets

The power of Gnomock is in the Presets developed by the community. Presets,
Expand Down Expand Up @@ -54,7 +70,7 @@ namedPorts := gnomock.NamedPorts{
}

// see docs for option description
container, err := gnomock.Start(
container, err := gnomock.StartCustom(
testImage, namedPorts,
gnomock.WithHealthCheckInterval(time.Microsecond*500),
gnomock.WithHealthCheck(healthcheck),
Expand Down

0 comments on commit 9ddc33e

Please sign in to comment.