Dan Anglin
2a761272a0
The shared volume will be used by each container to share their specific traefik dynamic configuration to allow Traefik to dynamically configure the backend.
155 lines
4.6 KiB
Go
155 lines
4.6 KiB
Go
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"
|
|
)
|
|
|
|
// DockerStack is a stack for managing the containers
|
|
// for the forge platform.
|
|
type DockerStack struct {
|
|
Name string
|
|
Stack auto.Stack
|
|
}
|
|
|
|
//go:embed templates/traefik/Dockerfile.tmpl
|
|
var templateTraefikDockerfile string
|
|
|
|
//go:embed templates/traefik/traefik.yaml.tmpl
|
|
var templateTraefikStaticConfig string
|
|
|
|
// newContainerStack creates the ContainerStack value.
|
|
func newDockerStack(ctx context.Context, project, stack string, c config.Config) (*DockerStack, error) {
|
|
deployFunc := deployDockerStack(project, c.Docker, c.Services)
|
|
|
|
s, err := createOrSelectStack(ctx, project, stack, deployFunc)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to initialise the '%s' stack...\n%w", stack, err)
|
|
}
|
|
|
|
d := DockerStack{
|
|
Name: stack,
|
|
Stack: s,
|
|
}
|
|
|
|
return &d, nil
|
|
}
|
|
|
|
// Preview the proposed changes to the container stack.
|
|
func (c *DockerStack) Preview(ctx context.Context) error {
|
|
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.
|
|
func (c *DockerStack) Update(ctx context.Context) error {
|
|
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.
|
|
func (c *DockerStack) Destroy(ctx context.Context) error {
|
|
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
|
|
}
|
|
|
|
// deployDockerStack returns a Pulumi run function
|
|
// that is used to deploy the docker stack.
|
|
func deployDockerStack(project string, dockerConfig config.DockerConfig, services config.ServicesConfig) pulumi.RunFunc {
|
|
return func(ctx *pulumi.Context) error {
|
|
// TODO: Create the provider when we start playing with remote hosts
|
|
|
|
// Create the docker network
|
|
networkConfig := docker.DockerNetworkConfig{
|
|
Name: pulumi.String(dockerConfig.Network.Name),
|
|
Subnet: pulumi.String(dockerConfig.Network.Subnet),
|
|
Driver: pulumi.String(dockerConfig.Network.Driver),
|
|
}
|
|
|
|
network, err := docker.CreateNetwork(ctx, networkConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create the shared volume
|
|
sharedVolumeInput := docker.DockerVolumeInput{
|
|
Name: pulumi.String(dockerConfig.SharedVolume.Name),
|
|
UniqueLabel: "shared",
|
|
}
|
|
|
|
_, err = docker.CreateVolume(ctx, sharedVolumeInput)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
base_cache, err := os.UserCacheDir()
|
|
if err != nil {
|
|
return fmt.Errorf("unable to get the base cache directory...\n%w", err)
|
|
}
|
|
|
|
// Create the Traefik service
|
|
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)
|
|
}
|
|
|
|
if err := generateFile(services.Traefik, templateTraefikDockerfile, "traefikDocker", filepath.Join(traefikContextDir, "Dockerfile")); err != nil {
|
|
return fmt.Errorf("unable to generate the Traefik Dockerfile from template...\n%w", err)
|
|
}
|
|
|
|
if err := generateFile(services.Traefik, templateTraefikStaticConfig, "traefikStaticConf", filepath.Join(traefikContextDir, "traefik.yml")); err != nil {
|
|
return fmt.Errorf("unable to generate the Traefik static configuration from template...\n%w", err)
|
|
}
|
|
|
|
c := docker.DockerImageInput{
|
|
BuildContext: pulumi.String(traefikContextDir),
|
|
Dockerfile: pulumi.String(filepath.Join(traefikContextDir, "Dockerfile")),
|
|
ImageName: pulumi.String("helix-traefik"),
|
|
ImageTag: pulumi.String(services.Traefik.Version),
|
|
UniqueLabel: "traefik-image",
|
|
}
|
|
|
|
traefikImage, err := docker.CreateImage(ctx, c)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
traefikContainerInput := docker.DockerContainerInput{
|
|
Image: traefikImage.ImageName,
|
|
Ipv4Address: pulumi.String(services.Traefik.ContainerIp),
|
|
Name: pulumi.String("helix-traefik"),
|
|
Network: network.Name,
|
|
UniqueLabel: "traefik-container",
|
|
}
|
|
|
|
if err = docker.CreateContainer(ctx, traefikContainerInput); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|