From 5e21bf67e8cf33b50d1d5a3620b6c651c8eb181f Mon Sep 17 00:00:00 2001 From: Dan Anglin Date: Sun, 11 Jul 2021 19:39:39 +0100 Subject: [PATCH] feat: add a route to the traefik dashboard --- internal/config/config.go | 7 +-- internal/config/config_test.go | 5 ++- internal/config/testdata/config-valid.json | 3 +- internal/docker/container.go | 43 +++++++++++++------ internal/stacks/docker.go | 27 +++++++++++- .../stacks/templates/traefik/Dockerfile.tmpl | 6 +++ .../traefik/dynamic_dashboard.yaml.tmpl | 9 ++++ .../templates/traefik/entrypoint.sh.tmpl | 26 +++++++++++ .../templates/traefik/traefik.yaml.tmpl | 2 +- 9 files changed, 106 insertions(+), 22 deletions(-) create mode 100644 internal/stacks/templates/traefik/dynamic_dashboard.yaml.tmpl create mode 100644 internal/stacks/templates/traefik/entrypoint.sh.tmpl diff --git a/internal/config/config.go b/internal/config/config.go index 4130b6d..5fe16ab 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -20,7 +20,6 @@ type DockerConfig struct { } // DockerNetworkConfig contains configuration for creating the docker network. - type DockerNetworkConfig struct { Name string `json:"name"` Subnet string `json:"subnet"` @@ -41,10 +40,12 @@ type ServicesConfig struct { // TraefikConfig contains configuration for the Traefik container. type TraefikConfig struct { CheckNewVersion bool `json:"checkNewVersion"` + ContainerIp string `json:"containerIp"` + Domain string `json:"domain"` + GroupId int + LogLevel string `json:"logLevel"` SendAnonymousUsage bool `json:"sendAnonymousUsage"` Version string `json:"version"` - ContainerIp string `json:"containerIp"` - LogLevel string `json:"logLevel"` } // NewConfig creates a new Config value from a given diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 04c0619..b0182c7 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -30,10 +30,11 @@ func TestValidConfig(t *testing.T) { Services: ServicesConfig{ Traefik: TraefikConfig{ CheckNewVersion: true, + ContainerIp: "172.17.1.2", + Domain: "forge.localhost", + LogLevel: "info", SendAnonymousUsage: false, Version: "v2.4.9", - ContainerIp: "172.17.1.2", - LogLevel: "info", }, }, }, diff --git a/internal/config/testdata/config-valid.json b/internal/config/testdata/config-valid.json index d932b43..87054c5 100644 --- a/internal/config/testdata/config-valid.json +++ b/internal/config/testdata/config-valid.json @@ -16,7 +16,8 @@ "sendAnonymousUsage": false, "version": "v2.4.9", "containerIp": "172.17.1.2", - "logLevel": "info" + "logLevel": "info", + "domain": "forge.localhost" } } } diff --git a/internal/docker/container.go b/internal/docker/container.go index ef8f5b2..fea1886 100644 --- a/internal/docker/container.go +++ b/internal/docker/container.go @@ -10,23 +10,32 @@ import ( // DockerContainerInput is the configuration // used to create the Gitea docker container. type DockerContainerInput struct { - Image pulumi.StringInput - Ipv4Address pulumi.StringInput - EnvVars pulumi.StringArray - Name pulumi.StringInput - Network pulumi.StringInput - Volumes []DockerVolumeMount - UniqueLabel string + Image pulumi.StringInput + Ipv4Address pulumi.StringInput + EnvVars pulumi.StringArray + Name pulumi.StringInput + Network pulumi.StringInput + HostPathVolumes []HostPathVolume + DockerVolumes []DockerVolume + UniqueLabel string } -// DockerVolumeMount is the configuration +// HostPathVolume is the configuration // used for mounting a host directory // to a directory inside a container. -type DockerVolumeMount struct { +type HostPathVolume struct { HostPath pulumi.StringInput MountPath pulumi.StringInput } +// DockerVolume is the configuration +// used for mounting a docker volume +// to a directory inside a container. +type DockerVolume struct { + Name pulumi.StringInput + MountPath pulumi.StringInput +} + func CreateContainer(ctx *pulumi.Context, c DockerContainerInput) error { // all containers will mount the host's timezone and localtime files // to ensure the correct time is synced. @@ -43,13 +52,21 @@ func CreateContainer(ctx *pulumi.Context, c DockerContainerInput) error { }, } - // optionally create additional container volumes. - for _, v := range c.Volumes { - vArg := docker.ContainerVolumeArgs{ + // create additional container volumes if specified. + for _, v := range c.HostPathVolumes { + arg := docker.ContainerVolumeArgs{ ContainerPath: v.MountPath, HostPath: v.HostPath, } - volumes = append(volumes, vArg) + volumes = append(volumes, arg) + } + + for _, v := range c.DockerVolumes { + arg := docker.ContainerVolumeArgs{ + ContainerPath: v.MountPath, + VolumeName: v.Name, + } + volumes = append(volumes, arg) } args := docker.ContainerArgs{ diff --git a/internal/stacks/docker.go b/internal/stacks/docker.go index 645d3b9..3180e2c 100644 --- a/internal/stacks/docker.go +++ b/internal/stacks/docker.go @@ -29,6 +29,12 @@ var templateTraefikDockerfile string //go:embed templates/traefik/traefik.yaml.tmpl var templateTraefikStaticConfig string +//go:embed templates/traefik/dynamic_dashboard.yaml.tmpl +var templateTraefikDynamicDashboardConfig string + +//go:embed templates/traefik/entrypoint.sh.tmpl +var templateTraefikEntrypoint 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) @@ -79,6 +85,9 @@ func (c *DockerStack) Destroy(ctx context.Context) error { // 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 { + groupID := 2239 + services.Traefik.GroupId = groupID + return func(ctx *pulumi.Context) error { // TODO: Create the provider when we start playing with remote hosts @@ -96,11 +105,11 @@ func deployDockerStack(project string, dockerConfig config.DockerConfig, service // Create the shared volume sharedVolumeInput := docker.DockerVolumeInput{ - Name: pulumi.String(dockerConfig.SharedVolume.Name), + Name: pulumi.String(dockerConfig.SharedVolume.Name), UniqueLabel: "shared", } - _, err = docker.CreateVolume(ctx, sharedVolumeInput) + sharedVolume, err := docker.CreateVolume(ctx, sharedVolumeInput) if err != nil { return err } @@ -125,6 +134,14 @@ func deployDockerStack(project string, dockerConfig config.DockerConfig, service return fmt.Errorf("unable to generate the Traefik static configuration from template...\n%w", err) } + 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) + } + c := docker.DockerImageInput{ BuildContext: pulumi.String(traefikContextDir), Dockerfile: pulumi.String(filepath.Join(traefikContextDir, "Dockerfile")), @@ -144,6 +161,12 @@ func deployDockerStack(project string, dockerConfig config.DockerConfig, service Name: pulumi.String("helix-traefik"), Network: network.Name, UniqueLabel: "traefik-container", + DockerVolumes: []docker.DockerVolume{ + { + Name: sharedVolume.Name, + MountPath: pulumi.String("/helix/shared"), + }, + }, } if err = docker.CreateContainer(ctx, traefikContainerInput); err != nil { diff --git a/internal/stacks/templates/traefik/Dockerfile.tmpl b/internal/stacks/templates/traefik/Dockerfile.tmpl index 798dc1f..f43e3e6 100644 --- a/internal/stacks/templates/traefik/Dockerfile.tmpl +++ b/internal/stacks/templates/traefik/Dockerfile.tmpl @@ -2,6 +2,12 @@ FROM traefik:{{ .Version }} ADD traefik.yml /helix/traefik/ +ADD entrypoint.sh / + +ADD dynamic_dashboard.yaml /tmp/ + +RUN chmod +x /entrypoint.sh + EXPOSE 22 80 443 CMD ["--configfile=/helix/traefik/traefik.yml"] diff --git a/internal/stacks/templates/traefik/dynamic_dashboard.yaml.tmpl b/internal/stacks/templates/traefik/dynamic_dashboard.yaml.tmpl new file mode 100644 index 0000000..afe8885 --- /dev/null +++ b/internal/stacks/templates/traefik/dynamic_dashboard.yaml.tmpl @@ -0,0 +1,9 @@ +--- +http: + routers: + dashboard: + entryPoints: + - "https" + rule: "Host(`{{ .Domain }}`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))" + service: "api@internal" + tls: {} diff --git a/internal/stacks/templates/traefik/entrypoint.sh.tmpl b/internal/stacks/templates/traefik/entrypoint.sh.tmpl new file mode 100644 index 0000000..f53aa28 --- /dev/null +++ b/internal/stacks/templates/traefik/entrypoint.sh.tmpl @@ -0,0 +1,26 @@ +#!/bin/sh +set -e + +# Create the dynamic config directory in the shared volume. +mkdir -p /helix/shared/traefik/dynamic +chgrp {{ .GroupId }} /helix/shared/traefik/dynamic +chmod a-rwx,u+rwx,g+rwx /helix/shared/traefik/dynamic + +# Move the dashboard config to the new directory. +mv /tmp/dynamic_dashboard.yaml /helix/shared/traefik/dynamic/dashboard.yaml + +# first arg is `-f` or `--some-option` +if [ "${1#-}" != "$1" ]; then + set -- traefik "$@" +fi + +# if our command is a valid Traefik subcommand, let's invoke it through Traefik instead +# (this allows for "docker run traefik version", etc) +if traefik "$1" --help >/dev/null 2>&1 +then + set -- traefik "$@" +else + echo "= '$1' is not a Traefik command: assuming shell execution." 1>&2 +fi + +exec "$@" diff --git a/internal/stacks/templates/traefik/traefik.yaml.tmpl b/internal/stacks/templates/traefik/traefik.yaml.tmpl index cf0acf6..fdfa942 100644 --- a/internal/stacks/templates/traefik/traefik.yaml.tmpl +++ b/internal/stacks/templates/traefik/traefik.yaml.tmpl @@ -22,6 +22,6 @@ entryPoints: providers: file: watch: true - directory: "/helix/traefik/config/dynamic" + directory: "/helix/shared/traefik/dynamic" log: level: "{{ .LogLevel }}"