Skip to content

Commit

Permalink
⚗️ Try to identify by container name
Browse files Browse the repository at this point in the history
  • Loading branch information
elgohr committed Dec 26, 2023
1 parent c63ad4c commit fdcb496
Show file tree
Hide file tree
Showing 27 changed files with 1,482 additions and 54 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ require (
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
Expand Down
82 changes: 53 additions & 29 deletions localstack.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/go-connections/nat"
"github.com/google/uuid"
"github.com/moby/moby/client"
"github.com/sirupsen/logrus"
"io"
"log"
"strings"
"sync"
"time"

Expand All @@ -48,8 +50,8 @@ type Instance struct {
portMapping map[Service]string
portMappingMutex sync.RWMutex

containerId string
containerIdMutex sync.RWMutex
containerName string
containerNameMutex sync.RWMutex

labels map[string]string
version string
Expand Down Expand Up @@ -178,7 +180,7 @@ func (i *Instance) Stop() error {
// Endpoint returns the endpoint for the given service
// Endpoints are allocated dynamically (to avoid blocked ports), but are fix after starting the instance
func (i *Instance) Endpoint(service Service) string {
if i.getContainerId() != "" {
if i.getContainerName() != "" {
if i.fixedPort {
return i.getPortMapping(FixedPort)
}
Expand All @@ -190,7 +192,7 @@ func (i *Instance) Endpoint(service Service) string {
// EndpointV2 returns the endpoint for the given service when used by aws-sdk-v2
// Endpoints are allocated dynamically (to avoid blocked ports), but are fix after starting the instance
func (i *Instance) EndpointV2(service Service) string {
if i.getContainerId() != "" {
if i.getContainerName() != "" {
if i.fixedPort {
return "http://" + i.getPortMapping(FixedPort)
}
Expand Down Expand Up @@ -268,13 +270,13 @@ func (i *Instance) start(ctx context.Context, services ...Service) error {
}
}

containerId, err := i.startLocalstack(ctx, services...)
containerName, err := i.startLocalstack(ctx, services...)
if err != nil {
return err
}

i.log.Info("waiting for localstack to start...")
return i.waitToBeAvailable(ctx, containerId)
return i.waitToBeAvailable(ctx, containerName)
}

const imageName = "go-localstack"
Expand Down Expand Up @@ -304,6 +306,8 @@ func (i *Instance) startLocalstack(ctx context.Context, services ...Service) (st
}
}

containerName := fmt.Sprintf("go-localstack-%s", uuid.New().String())

resp, err := i.cli.ContainerCreate(ctx,
&container.Config{
Image: imageName,
Expand All @@ -315,12 +319,12 @@ func (i *Instance) startLocalstack(ctx context.Context, services ...Service) (st
}, &container.HostConfig{
PortBindings: pm,
AutoRemove: true,
}, nil, nil, "")
}, nil, nil, containerName)
if err != nil {
return "", fmt.Errorf("localstack: could not create container: %w", err)
}

i.setContainerId(resp.ID)
i.setContainerName(containerName)

i.log.Info("starting localstack")
containerId := resp.ID
Expand All @@ -332,7 +336,7 @@ func (i *Instance) startLocalstack(ctx context.Context, services ...Service) (st
go i.writeContainerLogToLogger(ctx, containerId)
}

return containerId, i.mapPorts(ctx, services, containerId, 0)
return containerName, i.mapPorts(ctx, services, containerId, 0)
}

//go:embed Dockerfile
Expand Down Expand Up @@ -412,30 +416,51 @@ func (i *Instance) mapPorts(ctx context.Context, services []Service, containerId
}

func (i *Instance) stop() error {
containerId := i.getContainerId()
if containerId == "" {
containerName := i.getContainerName()
if containerName == "" {
return nil
}
ctx := context.Background()

containerId, err := i.getContainerIdByName(ctx, containerName)
if err != nil {
return err
}
timeout := 1
if err := i.cli.ContainerStop(context.Background(), containerId, container.StopOptions{Timeout: &timeout}); err != nil {
if err := i.cli.ContainerStop(ctx, containerId, container.StopOptions{Timeout: &timeout}); err != nil {
return err
}
i.setContainerId("")
i.setContainerName("")
i.resetPortMapping()
return nil
}

func (i *Instance) waitToBeAvailable(ctx context.Context, containerId string) error {
func (i *Instance) getContainerIdByName(ctx context.Context, containerName string) (string, error) {
containers, err := i.cli.ContainerList(ctx, types.ContainerListOptions{All: true})
if err != nil {
return "", err
}
for _, c := range containers {
for _, name := range c.Names {
if strings.Contains(name, containerName) {
return c.ID, nil
}
}
}
return "", fmt.Errorf("could not find container for %s", containerName)
}

func (i *Instance) waitToBeAvailable(ctx context.Context, containerName string) error {
ticker := time.NewTicker(300 * time.Millisecond)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-ticker.C:
//if err := i.isRunning(ctx, containerId); err != nil {
// return err
//}
if err := i.isRunning(ctx, containerName); err != nil {
return err
}
if err := i.checkAvailable(ctx); err != nil {
i.log.Debug(err)
} else {
Expand All @@ -446,9 +471,8 @@ func (i *Instance) waitToBeAvailable(ctx context.Context, containerId string) er
}
}

func (i *Instance) isRunning(ctx context.Context, containerId string) error {
_, err := i.cli.ContainerInspect(ctx, containerId)
if err != nil {
func (i *Instance) isRunning(ctx context.Context, containerName string) error {
if _, err := i.getContainerIdByName(ctx, containerName); err != nil {
i.log.Debug(err)
return errors.New("localstack container has been stopped")
}
Expand Down Expand Up @@ -496,19 +520,19 @@ func (i *Instance) checkAvailable(ctx context.Context) error {
}

func (i *Instance) isAlreadyRunning() bool {
return i.getContainerId() != ""
return i.getContainerName() != ""
}

func (i *Instance) getContainerId() string {
i.containerIdMutex.RLock()
defer i.containerIdMutex.RUnlock()
return i.containerId
func (i *Instance) getContainerName() string {
i.containerNameMutex.RLock()
defer i.containerNameMutex.RUnlock()
return i.containerName
}

func (i *Instance) setContainerId(v string) {
i.containerIdMutex.Lock()
defer i.containerIdMutex.Unlock()
i.containerId = v
func (i *Instance) setContainerName(v string) {
i.containerNameMutex.Lock()
defer i.containerNameMutex.Unlock()
i.containerName = v
}

func (i *Instance) resetPortMapping() {
Expand Down
10 changes: 5 additions & 5 deletions localstack_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ func TestInstance_Start_Fails(t *testing.T) {
given: func(f *internalfakes.FakeDockerClient) *Instance {
f.ContainerStopReturns(errors.New("can't stop"))
return &Instance{
cli: f,
log: logrus.StandardLogger(),
containerId: "running",
cli: f,
log: logrus.StandardLogger(),
containerName: "running",
}
},
then: func(t *testing.T, err error, f *internalfakes.FakeDockerClient) {
Expand Down Expand Up @@ -182,15 +182,15 @@ func TestInstance_StartWithContext_Fails_Stop_AfterTest(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()
f.ContainerStopReturns(errors.New("can't stop"))
i := &Instance{cli: f, log: logrus.StandardLogger(), containerId: "something"}
i := &Instance{cli: f, log: logrus.StandardLogger(), containerName: "something"}
require.EqualError(t, i.StartWithContext(ctx), "localstack: can't stop an already running instance: can't stop")
}

func TestInstance_Stop_Fails(t *testing.T) {
t.Parallel()
f := &internalfakes.FakeDockerClient{}
f.ContainerStopReturns(errors.New("can't stop"))
i := &Instance{cli: f, log: logrus.StandardLogger(), containerId: "something"}
i := &Instance{cli: f, log: logrus.StandardLogger(), containerName: "something"}
require.EqualError(t, i.Stop(), "can't stop")
}

Expand Down
39 changes: 19 additions & 20 deletions localstack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,26 +81,25 @@ func TestWithLogger(t *testing.T) {
}
}

//
//func TestWithTimeoutOnStartup(t *testing.T) {
// ctx, cancel := context.WithCancel(context.Background())
// defer cancel()
// l, err := localstack.NewInstance(localstack.WithTimeout(time.Second))
// require.NoError(t, err)
// require.EqualError(t, l.StartWithContext(ctx), "localstack container has been stopped")
//
// cli, err := client.NewClientWithOpts(client.FromEnv)
// require.NoError(t, err)
// cli.NegotiateAPIVersion(ctx)
//
// containers, err := cli.ContainerList(ctx, types.ContainerListOptions{})
// require.NoError(t, err)
// for _, c := range containers {
// if strings.Contains(c.Image, "go-localstack") {
// t.Fatalf("%s is still running but should be terminated", c.Image)
// }
// }
//}
func TestWithTimeoutOnStartup(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
l, err := localstack.NewInstance(localstack.WithTimeout(time.Second))
require.NoError(t, err)
require.EqualError(t, l.StartWithContext(ctx), "localstack container has been stopped")

cli, err := client.NewClientWithOpts(client.FromEnv)
require.NoError(t, err)
cli.NegotiateAPIVersion(ctx)

containers, err := cli.ContainerList(ctx, types.ContainerListOptions{})
require.NoError(t, err)
for _, c := range containers {
if strings.Contains(c.Image, "go-localstack") {
t.Fatalf("%s is still running but should be terminated", c.Image)
}
}
}

func TestWithTimeoutAfterStartup(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
Expand Down
28 changes: 28 additions & 0 deletions vendor/github.com/google/uuid/CHANGELOG.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions vendor/github.com/google/uuid/CONTRIBUTING.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions vendor/github.com/google/uuid/CONTRIBUTORS

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions vendor/github.com/google/uuid/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit fdcb496

Please sign in to comment.