2021-07-10 12:41:15 +01:00
|
|
|
package stacks
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
_ "embed"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/auto"
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/auto/optdestroy"
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/auto/optpreview"
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/auto/optup"
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
|
|
|
|
"gitlab.com/dananglin/helix/internal/config"
|
|
|
|
"gitlab.com/dananglin/helix/internal/docker"
|
|
|
|
)
|
|
|
|
|
2021-07-11 10:49:53 +01:00
|
|
|
// DockerStack is a stack for managing the containers
|
2021-07-10 12:41:15 +01:00
|
|
|
// for the forge platform.
|
2021-07-11 10:49:53 +01:00
|
|
|
type DockerStack struct {
|
2021-07-10 12:41:15 +01:00
|
|
|
Name string
|
|
|
|
Stack auto.Stack
|
|
|
|
}
|
|
|
|
|
|
|
|
//go:embed templates/traefik/Dockerfile.tmpl
|
|
|
|
var templateTraefikDockerfile string
|
|
|
|
|
|
|
|
//go:embed templates/traefik/traefik.yaml.tmpl
|
|
|
|
var templateTraefikStaticConfig string
|
|
|
|
|
2021-07-11 19:39:39 +01:00
|
|
|
//go:embed templates/traefik/dynamic_dashboard.yaml.tmpl
|
|
|
|
var templateTraefikDynamicDashboardConfig string
|
|
|
|
|
|
|
|
//go:embed templates/traefik/entrypoint.sh.tmpl
|
|
|
|
var templateTraefikEntrypoint string
|
|
|
|
|
2021-07-10 12:41:15 +01:00
|
|
|
// newContainerStack creates the ContainerStack value.
|
2021-07-11 10:49:53 +01:00
|
|
|
func newDockerStack(ctx context.Context, project, stack string, c config.Config) (*DockerStack, error) {
|
2021-07-11 13:32:01 +01:00
|
|
|
deployFunc := deployDockerStack(project, c.Docker, c.Services)
|
2021-07-10 12:41:15 +01:00
|
|
|
|
|
|
|
s, err := createOrSelectStack(ctx, project, stack, deployFunc)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("unable to initialise the '%s' stack...\n%w", stack, err)
|
|
|
|
}
|
|
|
|
|
2021-07-11 10:49:53 +01:00
|
|
|
d := DockerStack{
|
2021-07-10 12:41:15 +01:00
|
|
|
Name: stack,
|
|
|
|
Stack: s,
|
|
|
|
}
|
|
|
|
|
2021-07-11 10:49:53 +01:00
|
|
|
return &d, nil
|
2021-07-10 12:41:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Preview the proposed changes to the container stack.
|
2021-07-11 10:49:53 +01:00
|
|
|
func (c *DockerStack) Preview(ctx context.Context) error {
|
2021-07-10 12:41:15 +01:00
|
|
|
streamer := optpreview.ProgressStreams(os.Stdout)
|
|
|
|
_, err := c.Stack.Preview(ctx, streamer)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to preview the '%s' stack...\n%w", c.Name, err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the container stack.
|
2021-07-11 10:49:53 +01:00
|
|
|
func (c *DockerStack) Update(ctx context.Context) error {
|
2021-07-10 12:41:15 +01:00
|
|
|
streamer := optup.ProgressStreams(os.Stdout)
|
|
|
|
_, err := c.Stack.Up(ctx, streamer)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to update '%s' stack...\n%w", c.Name, err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Destroy the container stack.
|
2021-07-11 10:49:53 +01:00
|
|
|
func (c *DockerStack) Destroy(ctx context.Context) error {
|
2021-07-10 12:41:15 +01:00
|
|
|
streamer := optdestroy.ProgressStreams(os.Stdout)
|
|
|
|
_, err := c.Stack.Destroy(ctx, streamer)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to destroy '%s' stack...\n%w", c.Name, err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-07-11 10:49:53 +01:00
|
|
|
// deployDockerStack returns a Pulumi run function
|
|
|
|
// that is used to deploy the docker stack.
|
2021-07-11 13:32:01 +01:00
|
|
|
func deployDockerStack(project string, dockerConfig config.DockerConfig, services config.ServicesConfig) pulumi.RunFunc {
|
2021-07-11 19:39:39 +01:00
|
|
|
groupID := 2239
|
|
|
|
services.Traefik.GroupId = groupID
|
|
|
|
|
2021-07-10 12:41:15 +01:00
|
|
|
return func(ctx *pulumi.Context) error {
|
2021-07-11 10:49:53 +01:00
|
|
|
// TODO: Create the provider when we start playing with remote hosts
|
|
|
|
|
|
|
|
// Create the docker network
|
2021-07-11 13:32:01 +01:00
|
|
|
networkConfig := docker.DockerNetworkConfig{
|
|
|
|
Name: pulumi.String(dockerConfig.Network.Name),
|
|
|
|
Subnet: pulumi.String(dockerConfig.Network.Subnet),
|
|
|
|
Driver: pulumi.String(dockerConfig.Network.Driver),
|
2021-07-11 10:49:53 +01:00
|
|
|
}
|
|
|
|
|
2021-07-11 13:32:01 +01:00
|
|
|
network, err := docker.CreateNetwork(ctx, networkConfig)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the shared volume
|
|
|
|
sharedVolumeInput := docker.DockerVolumeInput{
|
2021-07-11 19:39:39 +01:00
|
|
|
Name: pulumi.String(dockerConfig.SharedVolume.Name),
|
2021-07-11 13:32:01 +01:00
|
|
|
UniqueLabel: "shared",
|
|
|
|
}
|
|
|
|
|
2021-07-11 19:39:39 +01:00
|
|
|
sharedVolume, err := docker.CreateVolume(ctx, sharedVolumeInput)
|
2021-07-11 10:49:53 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-07-10 12:41:15 +01:00
|
|
|
base_cache, err := os.UserCacheDir()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to get the base cache directory...\n%w", err)
|
|
|
|
}
|
|
|
|
|
2021-07-11 10:49:53 +01:00
|
|
|
// Create the Traefik service
|
2021-07-10 12:41:15 +01:00
|
|
|
traefikContextDir := filepath.Join(base_cache, "helix", project, "traefik")
|
|
|
|
|
|
|
|
if err := os.MkdirAll(traefikContextDir, 0700); err != nil {
|
|
|
|
return fmt.Errorf("unable to make the cache directory for traefik...\n%w", err)
|
|
|
|
}
|
|
|
|
|
2021-07-11 10:49:53 +01:00
|
|
|
if err := generateFile(services.Traefik, templateTraefikDockerfile, "traefikDocker", filepath.Join(traefikContextDir, "Dockerfile")); err != nil {
|
2021-07-10 12:41:15 +01:00
|
|
|
return fmt.Errorf("unable to generate the Traefik Dockerfile from template...\n%w", err)
|
|
|
|
}
|
|
|
|
|
2021-07-11 10:49:53 +01:00
|
|
|
if err := generateFile(services.Traefik, templateTraefikStaticConfig, "traefikStaticConf", filepath.Join(traefikContextDir, "traefik.yml")); err != nil {
|
2021-07-10 12:41:15 +01:00
|
|
|
return fmt.Errorf("unable to generate the Traefik static configuration from template...\n%w", err)
|
|
|
|
}
|
|
|
|
|
2021-07-11 19:39:39 +01:00
|
|
|
if err := generateFile(services.Traefik, templateTraefikDynamicDashboardConfig, "traefikDashboardConf", filepath.Join(traefikContextDir, "dynamic_dashboard.yaml")); err != nil {
|
|
|
|
return fmt.Errorf("unable to generate the Traefik dashboard configuration from template...\n%w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := generateFile(services.Traefik, templateTraefikEntrypoint, "traefikEntrypoint", filepath.Join(traefikContextDir, "entrypoint.sh")); err != nil {
|
|
|
|
return fmt.Errorf("unable to generate the Traefik entrypoint script from template...\n%w", err)
|
|
|
|
}
|
|
|
|
|
2021-07-10 12:41:15 +01:00
|
|
|
c := docker.DockerImageInput{
|
|
|
|
BuildContext: pulumi.String(traefikContextDir),
|
|
|
|
Dockerfile: pulumi.String(filepath.Join(traefikContextDir, "Dockerfile")),
|
|
|
|
ImageName: pulumi.String("helix-traefik"),
|
2021-07-11 10:49:53 +01:00
|
|
|
ImageTag: pulumi.String(services.Traefik.Version),
|
2021-07-10 12:41:15 +01:00
|
|
|
UniqueLabel: "traefik-image",
|
|
|
|
}
|
|
|
|
|
2021-07-11 13:32:01 +01:00
|
|
|
traefikImage, err := docker.CreateImage(ctx, c)
|
2021-07-10 12:41:15 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
traefikContainerInput := docker.DockerContainerInput{
|
|
|
|
Image: traefikImage.ImageName,
|
2021-07-11 10:49:53 +01:00
|
|
|
Ipv4Address: pulumi.String(services.Traefik.ContainerIp),
|
2021-07-10 12:41:15 +01:00
|
|
|
Name: pulumi.String("helix-traefik"),
|
2021-07-11 10:49:53 +01:00
|
|
|
Network: network.Name,
|
2021-07-10 12:41:15 +01:00
|
|
|
UniqueLabel: "traefik-container",
|
2021-07-11 19:39:39 +01:00
|
|
|
DockerVolumes: []docker.DockerVolume{
|
|
|
|
{
|
|
|
|
Name: sharedVolume.Name,
|
|
|
|
MountPath: pulumi.String("/helix/shared"),
|
|
|
|
},
|
|
|
|
},
|
2021-07-10 12:41:15 +01:00
|
|
|
}
|
|
|
|
|
2021-07-11 13:32:01 +01:00
|
|
|
if err = docker.CreateContainer(ctx, traefikContainerInput); err != nil {
|
2021-07-10 12:41:15 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|