Skip to content
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

Local #2

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/containerd/containerd v1.5.5 // indirect
github.com/docker/docker v20.10.8+incompatible
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-connections v0.4.0
github.com/go-redis/redis/v8 v8.11.3
github.com/go-sql-driver/mysql v1.6.0
github.com/gorilla/mux v1.8.0 // indirect
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.7.0
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
Expand Down
15 changes: 12 additions & 3 deletions internal/services/icinga2/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (
"time"
)

const PORT = "5665"

type dockerCreator struct {
logger *zap.Logger
dockerClient *client.Client
Expand Down Expand Up @@ -59,11 +61,13 @@ func (i *dockerCreator) CreateIcinga2(name string) services.Icinga2Base {
panic(err)
}

port := utils.NewPortDecision(i.dockerClient, PORT)

cont, err := i.dockerClient.ContainerCreate(context.Background(), &container.Config{
Image: dockerImage,
Hostname: name,
Env: []string{"ICINGA_MASTER=1"},
}, nil, &network.NetworkingConfig{
}, &container.HostConfig{PublishAllPorts: port.Remote()}, &network.NetworkingConfig{
EndpointsConfig: map[string]*network.EndpointSettings{
networkName: {
NetworkID: i.dockerNetworkId,
Expand All @@ -90,10 +94,15 @@ func (i *dockerCreator) CreateIcinga2(name string) services.Icinga2Base {
}
logger.Debug("started container")

binding, err := port.Binding(context.Background(), i.dockerClient, cont.ID)
if err != nil {
logger.Fatal("can't create port binding", zap.Error(err))
}

n := &dockerInstance{
info: info{
host: utils.MustString(utils.DockerContainerAddress(context.Background(), i.dockerClient, cont.ID)),
port: "5665",
host: binding.Host,
port: binding.Port,
},
icinga2Docker: i,
logger: logger,
Expand Down
4 changes: 2 additions & 2 deletions internal/services/icingadb/docker_binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func (i *dockerBinaryCreator) CreateIcingaDb(
},
}, nil, containerName)
if err != nil {
inst.logger.Fatal("failed to create icingadb container")
inst.logger.Fatal("failed to create icingadb container", zap.Error(err))
}
inst.containerId = cont.ID
inst.logger = inst.logger.With(zap.String("container-id", cont.ID))
Expand All @@ -136,7 +136,7 @@ func (i *dockerBinaryCreator) CreateIcingaDb(

err = i.dockerClient.ContainerStart(context.Background(), cont.ID, types.ContainerStartOptions{})
if err != nil {
inst.logger.Fatal("failed to start container")
inst.logger.Fatal("failed to start container", zap.Error(err))
}
inst.logger.Debug("started container")

Expand Down
27 changes: 15 additions & 12 deletions internal/services/mysql/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"time"
)

const PORT = "3306"

type dockerCreator struct {
*rootConnection
logger *zap.Logger
Expand Down Expand Up @@ -38,13 +40,14 @@ func NewDockerCreator(logger *zap.Logger, dockerClient *client.Client, container
panic(err)
}

port := utils.NewPortDecision(dockerClient, PORT)

rootPassword := utils.RandomString(16)
cont, err := dockerClient.ContainerCreate(context.Background(), &container.Config{
ExposedPorts: nil,
Env: []string{"MYSQL_ROOT_PASSWORD=" + rootPassword},
Cmd: nil,
Image: dockerImage,
}, nil, &network.NetworkingConfig{
Env: []string{"MYSQL_ROOT_PASSWORD=" + rootPassword},
Cmd: nil,
Image: dockerImage,
}, &container.HostConfig{PortBindings: port.Map()}, &network.NetworkingConfig{
EndpointsConfig: map[string]*network.EndpointSettings{
networkName: {
Aliases: []string{"mysql"},
Expand Down Expand Up @@ -73,14 +76,14 @@ func NewDockerCreator(logger *zap.Logger, dockerClient *client.Client, container
}
logger.Debug("started mysql container")

containerAddress := utils.MustString(utils.DockerContainerAddress(context.Background(), dockerClient, cont.ID))

d := &dockerCreator{
rootConnection: newRootConnection(containerAddress, "3306", "root", rootPassword),
logger: logger,
client: dockerClient,
containerId: cont.ID,
containerName: containerName,
rootConnection: newRootConnection(
port.Address(context.Background(), dockerClient, cont.ID), port.Port(), "root", rootPassword,
),
logger: logger,
client: dockerClient,
containerId: cont.ID,
containerName: containerName,
}

for attempt := 1; ; attempt++ {
Expand Down
12 changes: 8 additions & 4 deletions internal/services/redis/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"time"
)

const PORT = "6379"

type dockerCreator struct {
logger *zap.Logger
dockerClient *client.Client
Expand Down Expand Up @@ -55,9 +57,11 @@ func (r *dockerCreator) CreateRedisServer() services.RedisServerBase {
panic(err)
}

port := utils.NewPortDecision(r.dockerClient, PORT)

cont, err := r.dockerClient.ContainerCreate(context.Background(), &container.Config{
Image: dockerImage,
}, nil, &network.NetworkingConfig{
}, &container.HostConfig{PortBindings: port.Map()}, &network.NetworkingConfig{
EndpointsConfig: map[string]*network.EndpointSettings{
networkName: {
NetworkID: r.dockerNetworkId,
Expand All @@ -82,14 +86,14 @@ func (r *dockerCreator) CreateRedisServer() services.RedisServerBase {

err = r.dockerClient.ContainerStart(context.Background(), cont.ID, types.ContainerStartOptions{})
if err != nil {
logger.Fatal("failed to start container")
logger.Fatal("failed to start container", zap.Error(err))
}
logger.Debug("started container")

s := &dockerServer{
info: info{
host: utils.MustString(utils.DockerContainerAddress(context.Background(), r.dockerClient, cont.ID)),
port: "6379",
host: port.Address(context.Background(), r.dockerClient, cont.ID),
port: port.Port(),
},
redisDocker: r,
logger: logger,
Expand Down
2 changes: 1 addition & 1 deletion it.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func NewIT() *IT {
})
}

if n, err := it.dockerClient.NetworkCreate(context.Background(), it.prefix, types.NetworkCreate{}); err != nil {
if n, err := it.dockerClient.NetworkCreate(context.Background(), it.prefix, types.NetworkCreate{Labels: map[string]string{"icinga": "testing"}}); err != nil {
it.logger.Fatal("failed to create docker network", zap.String("network-name", it.prefix), zap.Error(err))
} else {
it.logger.Debug("created docker network", zap.String("network-name", it.prefix), zap.String("network-id", n.ID))
Expand Down
119 changes: 119 additions & 0 deletions utils/port.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package utils

import (
"context"
"fmt"
"github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
"github.com/pkg/errors"
"net"
"strconv"
)

type PortDecision struct {
dockerHost string
exposed string
port string
remote bool
}

func NewPortDecision(c *client.Client, port string) *PortDecision {
url, err := client.ParseHostURL(c.DaemonHost())
if err != nil {
panic(err)
}
p := &PortDecision{port: port}
if url.Scheme != "unix" {
portNumber, err := GetFreePort()
if err != nil {
panic(err)
}
p.dockerHost = url.Hostname()
p.exposed = strconv.Itoa(portNumber)
p.remote = true
} else {
p.exposed = port
}

return p
}

func (p *PortDecision) Address(ctx context.Context, c *client.Client, id string) string {
if p.remote {
return p.dockerHost
}

return MustString(DockerContainerAddress(ctx, c, id))
}

func (p *PortDecision) Binding(ctx context.Context, c *client.Client, id string) (*PortBinding, error) {
var port string
host := p.Address(ctx, c, id)
if p.remote {
r, err := c.ContainerInspect(context.Background(), id)
if err != nil {
return nil, errors.Wrap(err, "failed to inspect container")
}

defaultPort, err := nat.NewPort(nat.SplitProtoPort(p.port))
if err != nil {
return nil, errors.Wrap(err, "can't parse default port")
}

p, ok := r.NetworkSettings.Ports[defaultPort]
if !ok {
return nil, errors.New(fmt.Sprintf("default port %s not exposed", defaultPort))
}
port = p[0].HostPort
} else {
port = p.port
}

return &PortBinding{
Host: host,
Port: port,
}, nil
}

func (p *PortDecision) Map() nat.PortMap {
if p.remote {
return nat.PortMap{
nat.Port(fmt.Sprintf("%s/tcp", p.port)): []nat.PortBinding{
{
HostIP: "0.0.0.0",
HostPort: p.exposed,
},
},
}
}

return nil
}

func (p *PortDecision) Port() string {
return p.exposed
}

func (p *PortDecision) Remote() bool {
return p.remote
}

func GetFreePort() (int, error) {
addr, err := net.ResolveTCPAddr("tcp", ":0")
if err != nil {
return 0, err
}

l, err := net.ListenTCP("tcp", addr)
if err != nil {
return 0, err
}
defer l.Close()

return l.Addr().(*net.TCPAddr).Port, nil
}

type PortBinding struct {
Host string
Port string
}