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" ) // ContainerStack is a stack for managing the containers // for the forge platform. type ContainerStack 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 newContainerStack(ctx context.Context, project, stack, dockerNetwork string, conf config.TraefikConfig) (*ContainerStack, error) { deployFunc := deployContainerStack(project, dockerNetwork, conf) s, err := createOrSelectStack(ctx, project, stack, deployFunc) if err != nil { return nil, fmt.Errorf("unable to initialise the '%s' stack...\n%w", stack, err) } c := ContainerStack{ Name: stack, Stack: s, } return &c, nil } // Preview the proposed changes to the container stack. func (c *ContainerStack) 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 *ContainerStack) 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 *ContainerStack) 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 } // deployContainerStack returns a Pulumi run function // that is used to deploy the container stack. func deployContainerStack(project, dockerNetwork string, t config.TraefikConfig) pulumi.RunFunc { return func(ctx *pulumi.Context) error { base_cache, err := os.UserCacheDir() if err != nil { return fmt.Errorf("unable to get the base cache directory...\n%w", err) } 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(t, 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(t, 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(t.Version), UniqueLabel: "traefik-image", } traefikImage, err := docker.CreateDockerImage(ctx, c) if err != nil { return err } traefikContainerInput := docker.DockerContainerInput{ Image: traefikImage.ImageName, Ipv4Address: pulumi.String(t.ContainerIp), Name: pulumi.String("helix-traefik"), Network: pulumi.String(dockerNetwork), UniqueLabel: "traefik-container", } if err = docker.CreateDockerContainer(ctx, traefikContainerInput); err != nil { return err } return nil } }