212 lines
4.2 KiB
Go
212 lines
4.2 KiB
Go
package deployer
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types/container"
|
|
"github.com/docker/docker/api/types/filters"
|
|
"github.com/docker/docker/api/types/swarm"
|
|
"github.com/docker/docker/client"
|
|
"github.com/docker/go-connections/nat"
|
|
"github.com/rs/zerolog/log"
|
|
|
|
localcli "localenv/client"
|
|
"localenv/services"
|
|
)
|
|
|
|
const (
|
|
SwarmImageName string = "localenv-swarm:latest"
|
|
SwarmContainerName string = "localenv-swarm"
|
|
)
|
|
|
|
var (
|
|
ErrSwarmNotFound = errors.New("unable to find the swarm")
|
|
ErrSwarmClientInit = errors.New("swarm client is not initialized")
|
|
|
|
ImagesDeps = []string{
|
|
services.PostgresImageName,
|
|
services.RabbitMQImageName,
|
|
services.NginxImageName,
|
|
services.MailhogImageName,
|
|
}
|
|
)
|
|
|
|
type Deployer struct {
|
|
swarm Swarm
|
|
cli *client.Client
|
|
}
|
|
|
|
func NewDeployer(ctx context.Context, cli *client.Client) (Deployer, error) {
|
|
var deployer Deployer
|
|
deployer.cli = cli
|
|
|
|
if err := deployer.init(ctx); err != nil {
|
|
return deployer, err
|
|
}
|
|
|
|
return deployer, nil
|
|
}
|
|
|
|
func (d Deployer) getSwarm(ctx context.Context) (string, error) {
|
|
filterArgs := filters.NewArgs()
|
|
filterArgs.Add("name", SwarmContainerName)
|
|
filterArgs.Add("ancestor", SwarmImageName)
|
|
|
|
options := types.ContainerListOptions{
|
|
Filters: filterArgs,
|
|
All: true,
|
|
}
|
|
|
|
containers, err := d.cli.ContainerList(ctx, options)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if len(containers) == 0 {
|
|
return "", ErrSwarmNotFound
|
|
}
|
|
|
|
return containers[0].ID, nil
|
|
}
|
|
|
|
func (d Deployer) createSwarm(ctx context.Context) (string, error) {
|
|
containerConfig := container.Config{
|
|
Image: SwarmImageName,
|
|
ExposedPorts: nat.PortSet{
|
|
"4523/tcp": struct{}{},
|
|
"4443/tcp": struct{}{},
|
|
"15672/tcp": struct{}{},
|
|
},
|
|
}
|
|
|
|
hostConfig := container.HostConfig{
|
|
Runtime: "sysbox-runc",
|
|
PortBindings: nat.PortMap{
|
|
// swarm dockerd
|
|
"4523/tcp": []nat.PortBinding{
|
|
{
|
|
HostIP: "0.0.0.0",
|
|
HostPort: "4523",
|
|
},
|
|
},
|
|
// nginx ssl
|
|
"4443/tcp": []nat.PortBinding{
|
|
{
|
|
HostIP: "0.0.0.0",
|
|
HostPort: "4443",
|
|
},
|
|
},
|
|
// rabbitmq admin interface
|
|
"15672/tcp": []nat.PortBinding{
|
|
{
|
|
HostIP: "0.0.0.0",
|
|
HostPort: "15672",
|
|
},
|
|
},
|
|
},
|
|
NetworkMode: "bridge",
|
|
}
|
|
|
|
resp, err := d.cli.ContainerCreate(ctx, &containerConfig, &hostConfig, nil, nil, SwarmContainerName)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
log.Info().Msg("swarm created successfully")
|
|
return resp.ID, nil
|
|
}
|
|
|
|
func (d Deployer) initSwarm(ctx context.Context) error {
|
|
req := swarm.InitRequest{
|
|
ListenAddr: "0.0.0.0:2377",
|
|
AdvertiseAddr: "127.0.0.1",
|
|
}
|
|
|
|
if _, err := d.swarm.cli.SwarmInit(ctx, req); err != nil {
|
|
if strings.Contains(err.Error(), "part of a swarm") {
|
|
log.Info().Msg("swarm already initialized")
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d Deployer) StopSwarm(ctx context.Context) error {
|
|
if d.swarm.ID == "" {
|
|
log.Warn().Msg("no swarm registered, can't stop")
|
|
return nil
|
|
}
|
|
|
|
options := container.StopOptions{}
|
|
|
|
if err := d.cli.ContainerStop(ctx, d.swarm.ID, options); err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Info().Msg("swarm stopped successfully")
|
|
return nil
|
|
}
|
|
|
|
// TODO(rmanach): get a child context instead
|
|
func (d *Deployer) init(ctx context.Context) error {
|
|
swarmID, err := d.getSwarm(ctx)
|
|
if err != nil {
|
|
if !errors.Is(err, ErrSwarmNotFound) {
|
|
return err
|
|
}
|
|
}
|
|
|
|
s := Swarm{swarmID, nil}
|
|
|
|
if swarmID == "" {
|
|
id, err := d.createSwarm(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.ID = id
|
|
}
|
|
|
|
if err := d.cli.ContainerStart(ctx, s.ID, types.ContainerStartOptions{}); err != nil {
|
|
return err
|
|
}
|
|
|
|
cli, errInit := localcli.GetSwarmClient()
|
|
if errInit != nil {
|
|
if err := d.StopSwarm(ctx); err != nil {
|
|
log.Err(err).Msg("unable to stop the swarm")
|
|
}
|
|
return errInit
|
|
}
|
|
|
|
s.cli = cli
|
|
d.swarm = s
|
|
|
|
if err := d.initSwarm(ctx); err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Info().Msg("deployer successfully initialized")
|
|
return nil
|
|
}
|
|
|
|
func (d *Deployer) Deploy(ctx context.Context) error {
|
|
if err := d.swarm.deployServices(ctx); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *Deployer) GetSwarmCLI() (*client.Client, error) {
|
|
if d.swarm.cli == nil {
|
|
return nil, ErrSwarmClientInit
|
|
}
|
|
|
|
return d.swarm.cli, nil
|
|
}
|