Browse Source

Remove dead code, tidy up

master
Chris Smith 5 years ago
parent
commit
a0ee5f3f04
8 changed files with 168 additions and 549 deletions
  1. 0
    108
      certs/deployer.go
  2. 0
    107
      certs/monitor.go
  3. 118
    0
      config.go
  4. 6
    7
      docker.go
  5. 0
    134
      docker/monitor.go
  6. 31
    93
      dotege.go
  7. 0
    84
      model/model.go
  8. 13
    16
      templates.go

+ 0
- 108
certs/deployer.go View File

@@ -1,108 +0,0 @@
1
-package certs
2
-
3
-import (
4
-	"github.com/csmith/dotege/model"
5
-	"go.uber.org/zap"
6
-	"io/ioutil"
7
-	"os"
8
-	"path"
9
-	"time"
10
-)
11
-
12
-// CertificateDeployer deploys certificates according to their configuration.
13
-type CertificateDeployer struct {
14
-	logger        *zap.SugaredLogger
15
-	certChannel   <-chan model.FoundCertificate
16
-	deployChannel chan bool
17
-	certs         map[string]model.FoundCertificate
18
-	hostnames     map[string]*model.Hostname
19
-}
20
-
21
-// NewCertificateDeployer creates a new CertificateDeployer.
22
-func NewCertificateDeployer(logger *zap.SugaredLogger, channel <-chan model.FoundCertificate) *CertificateDeployer {
23
-	deployer := &CertificateDeployer{
24
-		logger:        logger,
25
-		certChannel:   channel,
26
-		deployChannel: make(chan bool, 1),
27
-		certs:         make(map[string]model.FoundCertificate),
28
-	}
29
-
30
-	go deployer.monitor()
31
-	go deployer.deployAll()
32
-
33
-	return deployer
34
-}
35
-
36
-func (c *CertificateDeployer) monitor() {
37
-	for {
38
-		select {
39
-		case cert := <-c.certChannel:
40
-			c.certs[cert.Hostname] = cert
41
-			c.deployChannel <- true
42
-		}
43
-	}
44
-}
45
-
46
-func (c *CertificateDeployer) deployAll() {
47
-	for {
48
-		select {
49
-		case <-c.deployChannel:
50
-			c.logger.Debug("Checking for certificates requiring deployment")
51
-			for _, hostname := range c.hostnames {
52
-				if cert, ok := c.certs[hostname.Name]; ok {
53
-					c.deploySingle(cert, hostname)
54
-				} else {
55
-					c.logger.Warnf("No certificate found for %s", hostname.Name)
56
-				}
57
-			}
58
-		}
59
-	}
60
-}
61
-
62
-func (c *CertificateDeployer) deploySingle(cert model.FoundCertificate, hostname *model.Hostname) {
63
-	if (hostname.CertActions & model.COMBINE) == model.COMBINE {
64
-		chain := c.readFile(cert.FullChain)
65
-		pkey := c.readFile(cert.PrivateKey)
66
-		c.deployFile("combined.pem", append(chain, pkey...), cert.ModTime, hostname)
67
-	} else {
68
-		c.deployFile("cert.pem", c.readFile(cert.Cert), cert.ModTime, hostname)
69
-		c.deployFile("chain.pem", c.readFile(cert.Chain), cert.ModTime, hostname)
70
-		c.deployFile("fullchain.pem", c.readFile(cert.FullChain), cert.ModTime, hostname)
71
-		c.deployFile("privkey.pem", c.readFile(cert.PrivateKey), cert.ModTime, hostname)
72
-	}
73
-}
74
-
75
-func (c *CertificateDeployer) deployFile(name string, content []byte, modTime time.Time, hostname *model.Hostname) {
76
-	var target string
77
-	if (hostname.CertActions & model.FLATTEN) == model.FLATTEN {
78
-		target = path.Join(hostname.CertDestination, hostname.Name+".pem")
79
-	} else {
80
-		target = path.Join(hostname.CertDestination, hostname.Name, name)
81
-	}
82
-
83
-	info, err := os.Stat(target)
84
-	if err == nil && info.ModTime().After(modTime) {
85
-		c.logger.Debugf("Not writing %s as it was modified more recently than our cert", target)
86
-		return
87
-	}
88
-
89
-	err = ioutil.WriteFile(target, content, 0700)
90
-	if err != nil {
91
-		c.logger.Warnf("Unable to write certificate %s - %s", target, err.Error())
92
-	} else {
93
-		c.logger.Infof("Updated certificate file %s", target)
94
-	}
95
-}
96
-
97
-func (c *CertificateDeployer) readFile(name string) []byte {
98
-	data, err := ioutil.ReadFile(name)
99
-	if err != nil {
100
-		c.logger.Errorf("Unable to read certificate file %s - %s", name, err.Error())
101
-	}
102
-	return data
103
-}
104
-
105
-func (c *CertificateDeployer) UpdateHostnames(hostnames map[string]*model.Hostname) {
106
-	c.hostnames = hostnames
107
-	c.deployChannel <- true
108
-}

+ 0
- 107
certs/monitor.go View File

@@ -1,107 +0,0 @@
1
-package certs
2
-
3
-import (
4
-	"github.com/csmith/dotege/model"
5
-	"go.uber.org/zap"
6
-	"io/ioutil"
7
-	"path"
8
-	"strings"
9
-)
10
-
11
-// CertificateMonitor handles scanning for new/updated certificates
12
-type CertificateMonitor struct {
13
-	logger      *zap.SugaredLogger
14
-	channel     chan<- model.FoundCertificate
15
-	directories []string
16
-	certs       map[string]*model.FoundCertificate
17
-}
18
-
19
-// NewCertificateManager creates a new CertificateMonitor.
20
-func NewCertificateManager(logger *zap.SugaredLogger, channel chan<- model.FoundCertificate) *CertificateMonitor {
21
-	return &CertificateMonitor{
22
-		logger:  logger,
23
-		channel: channel,
24
-		certs:   make(map[string]*model.FoundCertificate),
25
-	}
26
-}
27
-
28
-// AddDirectory adds a new directory to monitor
29
-func (c *CertificateMonitor) AddDirectory(directory string) {
30
-	c.logger.Infof("Adding certificate directory %s", directory)
31
-	c.directories = append(c.directories, directory)
32
-	go c.scanForFolders(directory)
33
-}
34
-
35
-func (c *CertificateMonitor) scanForFolders(dir string) {
36
-	c.logger.Debugf("Scanning folder %s for certificates", dir)
37
-	dirs, err := ioutil.ReadDir(dir)
38
-	if err != nil {
39
-		c.logger.Errorf("Unable to read directory %s - %s", dir, err.Error())
40
-		return
41
-	}
42
-
43
-	for _, d := range dirs {
44
-		if d.IsDir() {
45
-			c.scanForCerts(d.Name(), path.Join(dir, d.Name()))
46
-		}
47
-	}
48
-}
49
-
50
-func (c *CertificateMonitor) scanForCerts(vhost string, dir string) {
51
-	c.logger.Debugf("Scanning folder %s for certificates", dir)
52
-	files, err := ioutil.ReadDir(dir)
53
-	if err != nil {
54
-		c.logger.Errorf("Unable to read directory %s - %s", dir, err.Error())
55
-		return
56
-	}
57
-
58
-	cert := model.FoundCertificate{
59
-		Hostname: vhost,
60
-	}
61
-	for _, f := range files {
62
-		ext := path.Ext(f.Name())
63
-		base := path.Base(f.Name())
64
-		base = base[:len(base)-len(ext)]
65
-		if ext == ".pem" {
66
-			prefix := strings.Split(base, "-")[0]
67
-			added := maybeAddPart(&cert, prefix, path.Join(dir, f.Name()))
68
-			if added && f.ModTime().After(cert.ModTime) {
69
-				cert.ModTime = f.ModTime()
70
-			}
71
-		}
72
-	}
73
-
74
-	c.maybeAddCert(vhost, cert)
75
-}
76
-
77
-func maybeAddPart(cert *model.FoundCertificate, part string, path string) bool {
78
-	switch part {
79
-	case "cert":
80
-		cert.Cert = path
81
-	case "chain":
82
-		cert.Chain = path
83
-	case "fullchain":
84
-		cert.FullChain = path
85
-	case "privkey":
86
-		cert.PrivateKey = path
87
-	default:
88
-		return false
89
-	}
90
-	return true
91
-}
92
-
93
-func (c *CertificateMonitor) maybeAddCert(vhost string, cert model.FoundCertificate) {
94
-	if len(cert.Cert) > 0 && len(cert.Chain) > 0 && len(cert.FullChain) > 0 && len(cert.PrivateKey) > 0 {
95
-		if existing, ok := c.certs[vhost]; ok {
96
-			if cert.ModTime.After(existing.ModTime) {
97
-				c.logger.Debugf("Found newer certificate files for %s in %s", vhost, path.Dir(cert.Cert))
98
-				c.certs[vhost] = &cert
99
-				c.channel <- cert
100
-			}
101
-		} else {
102
-			c.logger.Debugf("Found new certificate files for %s in %s", vhost, path.Dir(cert.Cert))
103
-			c.certs[vhost] = &cert
104
-			c.channel <- cert
105
-		}
106
-	}
107
-}

+ 118
- 0
config.go View File

@@ -0,0 +1,118 @@
1
+package main
2
+
3
+import (
4
+	"fmt"
5
+	"github.com/xenolf/lego/certcrypto"
6
+	"github.com/xenolf/lego/lego"
7
+	"os"
8
+)
9
+
10
+const (
11
+	envCertDestinationKey         = "DOTEGE_CERT_DESTINATION"
12
+	envCertDestinationDefault     = "/data/certs/"
13
+	envDnsProviderKey             = "DOTEGE_DNS_PROVIDER"
14
+	envAcmeEmailKey               = "DOTEGE_ACME_EMAIL"
15
+	envAcmeEndpointKey            = "DOTEGE_ACME_ENDPOINT"
16
+	envAcmeKeyTypeKey             = "DOTEGE_ACME_KEY_TYPE"
17
+	envAcmeKeyTypeDefault         = "P384"
18
+	envAcmeCacheLocationKey       = "DOTEGE_ACME_CACHE_FILE"
19
+	envAcmeCacheLocationDefault   = "/data/config/certs.json"
20
+	envSignalContainerKey         = "DOTEGE_SIGNAL_CONTAINER"
21
+	envSignalContainerDefault     = ""
22
+	envSignalTypeKey              = "DOTEGE_SIGNAL_TYPE"
23
+	envSignalTypeDefault          = "HUP"
24
+	envTemplateDestinationKey     = "DOTEGE_TEMPLATE_DESTINATION"
25
+	envTemplateDestinationDefault = "/data/output/haproxy.cfg"
26
+	envTemplateSourceKey          = "DOTEGE_TEMPLATE_SOURCE"
27
+	envTemplateSourceDefault      = "./templates/haproxy.cfg.tpl"
28
+)
29
+
30
+// Config is the user-definable configuration for Dotege.
31
+type Config struct {
32
+	Templates              []TemplateConfig
33
+	Signals                []ContainerSignal
34
+	Labels                 LabelConfig
35
+	DefaultCertDestination string
36
+	Acme                   AcmeConfig
37
+}
38
+
39
+// TemplateConfig configures a single template for the generator.
40
+type TemplateConfig struct {
41
+	Source      string
42
+	Destination string
43
+}
44
+
45
+// ContainerSignal describes a container that should be sent a signal when the config/certs change.
46
+type ContainerSignal struct {
47
+	Name   string
48
+	Signal string
49
+}
50
+
51
+// LabelConfig describes the labels used for various properties.
52
+type LabelConfig struct {
53
+	Hostnames   string
54
+	RequireAuth string
55
+}
56
+
57
+// AcmeConfig describes the configuration to use for getting certs using ACME.
58
+type AcmeConfig struct {
59
+	Email         string
60
+	DnsProvider   string
61
+	Endpoint      string
62
+	KeyType       certcrypto.KeyType
63
+	CacheLocation string
64
+}
65
+
66
+func requiredVar(key string) (value string) {
67
+	value, ok := os.LookupEnv(key)
68
+	if !ok {
69
+		panic(fmt.Errorf("required environmental variable not defined: %s", key))
70
+	}
71
+	return
72
+}
73
+
74
+func optionalVar(key string, fallback string) (value string) {
75
+	value, ok := os.LookupEnv(key)
76
+	if !ok {
77
+		value = fallback
78
+	}
79
+	return
80
+}
81
+
82
+func createSignalConfig() []ContainerSignal {
83
+	name := optionalVar(envSignalContainerKey, envSignalContainerDefault)
84
+	if name == envSignalContainerDefault {
85
+		return []ContainerSignal{}
86
+	} else {
87
+		return []ContainerSignal{
88
+			{
89
+				Name:   name,
90
+				Signal: optionalVar(envSignalTypeKey, envSignalTypeDefault),
91
+			},
92
+		}
93
+	}
94
+}
95
+
96
+func createConfig() *Config {
97
+	return &Config{
98
+		Templates: []TemplateConfig{
99
+			{
100
+				Source:      optionalVar(envTemplateSourceKey, envTemplateSourceDefault),
101
+				Destination: optionalVar(envTemplateDestinationKey, envTemplateDestinationDefault),
102
+			},
103
+		},
104
+		Labels: LabelConfig{
105
+			Hostnames:   "com.chameth.vhost",
106
+			RequireAuth: "com.chameth.auth",
107
+		},
108
+		Acme: AcmeConfig{
109
+			DnsProvider:   requiredVar(envDnsProviderKey),
110
+			Email:         requiredVar(envAcmeEmailKey),
111
+			Endpoint:      optionalVar(envAcmeEndpointKey, lego.LEDirectoryProduction),
112
+			KeyType:       certcrypto.KeyType(optionalVar(envAcmeKeyTypeKey, envAcmeKeyTypeDefault)),
113
+			CacheLocation: optionalVar(envAcmeCacheLocationKey, envAcmeCacheLocationDefault),
114
+		},
115
+		Signals:                createSignalConfig(),
116
+		DefaultCertDestination: optionalVar(envCertDestinationKey, envCertDestinationDefault),
117
+	}
118
+}

+ 6
- 7
docker.go View File

@@ -2,7 +2,6 @@ package main
2 2
 
3 3
 import (
4 4
 	"fmt"
5
-	"github.com/csmith/dotege/model"
6 5
 	"github.com/docker/docker/api/types"
7 6
 	"github.com/docker/docker/api/types/events"
8 7
 	"github.com/docker/docker/api/types/filters"
@@ -15,7 +14,7 @@ type DockerClient interface {
15 14
 	ContainerInspect(ctx context.Context, containerID string) (types.ContainerJSON, error)
16 15
 }
17 16
 
18
-func monitorContainers(client DockerClient, stop <-chan struct{}, addedFn func(*model.Container), removedFn func(string)) error {
17
+func monitorContainers(client DockerClient, stop <-chan struct{}, addedFn func(*Container), removedFn func(string)) error {
19 18
 	ctx, cancel := context.WithCancel(context.Background())
20 19
 	stream, errors := startEventStream(client, ctx)
21 20
 
@@ -57,14 +56,14 @@ func startEventStream(client DockerClient, ctx context.Context) (<-chan events.M
57 56
 	return client.Events(ctx, types.EventsOptions{Filters: args})
58 57
 }
59 58
 
60
-func publishExistingContainers(client DockerClient, ctx context.Context, addedFn func(*model.Container)) error {
59
+func publishExistingContainers(client DockerClient, ctx context.Context, addedFn func(*Container)) error {
61 60
 	containers, err := client.ContainerList(ctx, types.ContainerListOptions{})
62 61
 	if err != nil {
63 62
 		return fmt.Errorf("unable to list containers: %s", err.Error())
64 63
 	}
65 64
 
66 65
 	for _, container := range containers {
67
-		addedFn(&model.Container{
66
+		addedFn(&Container{
68 67
 			Id:     container.ID,
69 68
 			Name:   container.Names[0][1:],
70 69
 			Labels: container.Labels,
@@ -73,13 +72,13 @@ func publishExistingContainers(client DockerClient, ctx context.Context, addedFn
73 72
 	return nil
74 73
 }
75 74
 
76
-func inspectContainer(client DockerClient, ctx context.Context, id string) (error, model.Container) {
75
+func inspectContainer(client DockerClient, ctx context.Context, id string) (error, Container) {
77 76
 	container, err := client.ContainerInspect(ctx, id)
78 77
 	if err != nil {
79
-		return err, model.Container{}
78
+		return err, Container{}
80 79
 	}
81 80
 
82
-	return nil, model.Container{
81
+	return nil, Container{
83 82
 		Id:     container.ID,
84 83
 		Name:   container.Name[1:],
85 84
 		Labels: container.Config.Labels,

+ 0
- 134
docker/monitor.go View File

@@ -1,134 +0,0 @@
1
-package docker
2
-
3
-import (
4
-	"context"
5
-	"github.com/csmith/dotege/model"
6
-	"github.com/docker/docker/api/types"
7
-	"github.com/docker/docker/api/types/filters"
8
-	"github.com/docker/docker/client"
9
-	"go.uber.org/zap"
10
-	"time"
11
-)
12
-
13
-// ContainerMonitor watches for newly created and destroyed containers, and emits their information on a channel.
14
-// Destroyed containers are given a grace period before being announced, to allow for restarts etc to be less
15
-// disruptive.
16
-type ContainerMonitor struct {
17
-	logger             *zap.SugaredLogger
18
-	newContainers      chan<- model.Container
19
-	goneContainerNames chan<- string
20
-	client             *client.Client
21
-	expiryTimes        map[string]time.Time
22
-	deletionTime       time.Duration
23
-	nextExpiry         time.Time
24
-	expiryTimer        *time.Timer
25
-}
26
-
27
-// NewContainerMonitor creates a new container monitor
28
-func NewContainerMonitor(logger *zap.SugaredLogger, client *client.Client, newContainerChannel chan<- model.Container, goneContainerChannel chan<- string) *ContainerMonitor {
29
-	timer := time.NewTimer(time.Hour)
30
-	timer.Stop()
31
-
32
-	return &ContainerMonitor{
33
-		logger:             logger,
34
-		newContainers:      newContainerChannel,
35
-		goneContainerNames: goneContainerChannel,
36
-		client:             client,
37
-		expiryTimes:        make(map[string]time.Time),
38
-		deletionTime:       10 * time.Second,
39
-		expiryTimer:        timer,
40
-		nextExpiry:         time.Now(),
41
-	}
42
-}
43
-
44
-// Monitor starts monitoring for changes, and publishes info on any pre-existing containers. It blocks indefinitely,
45
-// and should be run from a goroutine.
46
-func (c *ContainerMonitor) Monitor() {
47
-	args := filters.NewArgs()
48
-	args.Add("type", "container")
49
-	args.Add("event", "create")
50
-	args.Add("event", "destroy")
51
-	eventsChan, errChan := c.client.Events(context.Background(), types.EventsOptions{Filters: args})
52
-
53
-	c.publishExistingContainers()
54
-
55
-	for {
56
-		select {
57
-		case event := <-eventsChan:
58
-			if event.Action == "create" {
59
-				c.publishNewContainer(event.Actor.ID)
60
-			} else {
61
-				c.scheduleExpiry(event.Actor.Attributes["name"])
62
-			}
63
-
64
-		case <-c.expiryTimer.C:
65
-			c.publishExpiredContainers()
66
-
67
-		case err := <-errChan:
68
-			c.logger.Fatal("Error received from docker events API", err)
69
-		}
70
-	}
71
-}
72
-
73
-func (c *ContainerMonitor) publishExistingContainers() {
74
-	containers, err := c.client.ContainerList(context.Background(), types.ContainerListOptions{})
75
-	if err != nil {
76
-		c.logger.Fatal("Error received trying to list containers", err)
77
-	}
78
-
79
-	for _, container := range containers {
80
-		c.logger.Infof("Found existing container %s", container.Names[0][1:])
81
-		c.newContainers <- model.Container{
82
-			Id:     container.ID,
83
-			Name:   container.Names[0][1:],
84
-			Labels: container.Labels,
85
-		}
86
-	}
87
-}
88
-
89
-func (c *ContainerMonitor) publishNewContainer(id string) {
90
-	container, err := c.client.ContainerInspect(context.Background(), id)
91
-	if err != nil {
92
-		c.logger.Fatal("Error received trying to inspect container", err)
93
-	}
94
-	c.newContainers <- model.Container{
95
-		Id:     container.ID,
96
-		Name:   container.Name[1:],
97
-		Labels: container.Config.Labels,
98
-	}
99
-	c.logger.Info("Found new container %s", container.Name[1:])
100
-	delete(c.expiryTimes, container.Name[1:])
101
-}
102
-
103
-func (c *ContainerMonitor) scheduleExpiry(name string) {
104
-	now := time.Now()
105
-	expiryTime := now.Add(c.deletionTime)
106
-	c.expiryTimes[name] = expiryTime
107
-	c.logger.Infof("Scheduling expiry timer for %s", name)
108
-	if c.nextExpiry.Before(now) || c.nextExpiry.After(expiryTime) {
109
-		c.logger.Debugf("Starting expiry timer with default duration")
110
-		c.expiryTimer.Reset(c.deletionTime + 1*time.Second)
111
-		c.nextExpiry = expiryTime
112
-	}
113
-}
114
-
115
-func (c *ContainerMonitor) publishExpiredContainers() {
116
-	now := time.Now()
117
-	next := 0 * time.Second
118
-
119
-	for name, expiryTime := range c.expiryTimes {
120
-		if expiryTime.Before(now) {
121
-			c.logger.Infof("Expiring %s", name)
122
-			delete(c.expiryTimes, name)
123
-			c.goneContainerNames <- name
124
-		} else if next == 0 || expiryTime.Sub(now) < next {
125
-			next = expiryTime.Sub(now)
126
-		}
127
-	}
128
-
129
-	if next > 0 {
130
-		c.logger.Debugf("Starting expiry timer with duration %s\n", next)
131
-		c.expiryTimer.Reset(next + 1*time.Second)
132
-		c.nextExpiry = now.Add(next)
133
-	}
134
-}

+ 31
- 93
dotege.go View File

@@ -4,10 +4,7 @@ import (
4 4
 	"bytes"
5 5
 	"context"
6 6
 	"fmt"
7
-	"github.com/csmith/dotege/model"
8 7
 	"github.com/docker/docker/client"
9
-	"github.com/xenolf/lego/certcrypto"
10
-	"github.com/xenolf/lego/lego"
11 8
 	"go.uber.org/zap"
12 9
 	"go.uber.org/zap/zapcore"
13 10
 	"io/ioutil"
@@ -19,50 +16,31 @@ import (
19 16
 	"time"
20 17
 )
21 18
 
22
-const (
23
-	envCertDestinationKey         = "DOTEGE_CERT_DESTINATION"
24
-	envCertDestinationDefault     = "/data/certs/"
25
-	envDnsProviderKey             = "DOTEGE_DNS_PROVIDER"
26
-	envAcmeEmailKey               = "DOTEGE_ACME_EMAIL"
27
-	envAcmeEndpointKey            = "DOTEGE_ACME_ENDPOINT"
28
-	envAcmeKeyTypeKey             = "DOTEGE_ACME_KEY_TYPE"
29
-	envAcmeKeyTypeDefault         = "P384"
30
-	envAcmeCacheLocationKey       = "DOTEGE_ACME_CACHE_FILE"
31
-	envAcmeCacheLocationDefault   = "/data/config/certs.json"
32
-	envSignalContainerKey         = "DOTEGE_SIGNAL_CONTAINER"
33
-	envSignalContainerDefault     = ""
34
-	envSignalTypeKey              = "DOTEGE_SIGNAL_TYPE"
35
-	envSignalTypeDefault          = "HUP"
36
-	envTemplateDestinationKey     = "DOTEGE_TEMPLATE_DESTINATION"
37
-	envTemplateDestinationDefault = "/data/output/haproxy.cfg"
38
-	envTemplateSourceKey          = "DOTEGE_TEMPLATE_SOURCE"
39
-	envTemplateSourceDefault      = "./templates/haproxy.cfg.tpl"
40
-)
19
+// Container models a docker container that is running on the system.
20
+type Container struct {
21
+	Id     string
22
+	Name   string
23
+	Labels map[string]string
24
+}
25
+
26
+// Hostname describes a DNS name used for proxying, retrieving certificates, etc.
27
+type Hostname struct {
28
+	Name            string
29
+	Alternatives    map[string]bool
30
+	Containers      []*Container
31
+	CertDestination string
32
+	RequiresAuth    bool
33
+	AuthGroup       string
34
+}
41 35
 
42 36
 var (
43 37
 	logger             *zap.SugaredLogger
44 38
 	certificateManager *CertificateManager
45
-	config             *model.Config
39
+	config             *Config
46 40
 	dockerClient       *client.Client
47
-	containers         = make(map[string]*model.Container)
41
+	containers         = make(map[string]*Container)
48 42
 )
49 43
 
50
-func requiredVar(key string) (value string) {
51
-	value, ok := os.LookupEnv(key)
52
-	if !ok {
53
-		panic(fmt.Errorf("required environmental variable not defined: %s", key))
54
-	}
55
-	return
56
-}
57
-
58
-func optionalVar(key string, fallback string) (value string) {
59
-	value, ok := os.LookupEnv(key)
60
-	if !ok {
61
-		value = fallback
62
-	}
63
-	return
64
-}
65
-
66 44
 func monitorSignals() <-chan bool {
67 45
 	signals := make(chan os.Signal, 1)
68 46
 	done := make(chan bool, 1)
@@ -89,54 +67,15 @@ func createLogger() *zap.SugaredLogger {
89 67
 	return logger.Sugar()
90 68
 }
91 69
 
92
-func createSignalConfig() []model.ContainerSignal {
93
-	name := optionalVar(envSignalContainerKey, envSignalContainerDefault)
94
-	if name == envSignalContainerDefault {
95
-		return []model.ContainerSignal{}
96
-	} else {
97
-		return []model.ContainerSignal{
98
-			{
99
-				Name:   name,
100
-				Signal: optionalVar(envSignalTypeKey, envSignalTypeDefault),
101
-			},
102
-		}
103
-	}
104
-}
105
-
106
-func createConfig() {
107
-	config = &model.Config{
108
-		Templates: []model.TemplateConfig{
109
-			{
110
-				Source:      optionalVar(envTemplateSourceKey, envTemplateSourceDefault),
111
-				Destination: optionalVar(envTemplateDestinationKey, envTemplateDestinationDefault),
112
-			},
113
-		},
114
-		Labels: model.LabelConfig{
115
-			Hostnames:   "com.chameth.vhost",
116
-			RequireAuth: "com.chameth.auth",
117
-		},
118
-		Acme: model.AcmeConfig{
119
-			DnsProvider:   requiredVar(envDnsProviderKey),
120
-			Email:         requiredVar(envAcmeEmailKey),
121
-			Endpoint:      optionalVar(envAcmeEndpointKey, lego.LEDirectoryProduction),
122
-			KeyType:       certcrypto.KeyType(optionalVar(envAcmeKeyTypeKey, envAcmeKeyTypeDefault)),
123
-			CacheLocation: optionalVar(envAcmeCacheLocationKey, envAcmeCacheLocationDefault),
124
-		},
125
-		Signals:                createSignalConfig(),
126
-		DefaultCertActions:     model.COMBINE | model.FLATTEN,
127
-		DefaultCertDestination: optionalVar(envCertDestinationKey, envCertDestinationDefault),
128
-	}
129
-}
130
-
131
-func createTemplateGenerator(templates []model.TemplateConfig) *TemplateGenerator {
132
-	templateGenerator := NewTemplateGenerator(logger)
70
+func createTemplateGenerator(templates []TemplateConfig) *TemplateGenerator {
71
+	templateGenerator := NewTemplateGenerator()
133 72
 	for _, template := range templates {
134 73
 		templateGenerator.AddTemplate(template)
135 74
 	}
136 75
 	return templateGenerator
137 76
 }
138 77
 
139
-func createCertificateManager(config model.AcmeConfig) {
78
+func createCertificateManager(config AcmeConfig) {
140 79
 	certificateManager = NewCertificateManager(logger, config.Endpoint, config.KeyType, config.DnsProvider, config.CacheLocation)
141 80
 	err := certificateManager.Init(config.Email)
142 81
 	if err != nil {
@@ -163,10 +102,10 @@ func main() {
163 102
 
164 103
 	jitterTimer := time.NewTimer(time.Minute)
165 104
 	redeployTimer := time.NewTicker(time.Hour * 24)
166
-	updatedContainers := make(map[string]*model.Container)
105
+	updatedContainers := make(map[string]*Container)
167 106
 
168 107
 	go func() {
169
-		err := monitorContainers(dockerClient, dockerStopChan, func(container *model.Container) {
108
+		err := monitorContainers(dockerClient, dockerStopChan, func(container *Container) {
170 109
 			containers[container.Name] = container
171 110
 			updatedContainers[container.Name] = container
172 111
 			jitterTimer.Reset(100 * time.Millisecond)
@@ -185,7 +124,7 @@ func main() {
185 124
 		for {
186 125
 			select {
187 126
 			case <-jitterTimer.C:
188
-				hostnames := getHostnames(containers, *config)
127
+				hostnames := getHostnames(containers)
189 128
 				updated := templateGenerator.Generate(Context{
190 129
 					Containers: containers,
191 130
 					Hostnames:  hostnames,
@@ -234,7 +173,7 @@ func signalContainer() {
234 173
 	}
235 174
 }
236 175
 
237
-func getHostnamesForContainer(container *model.Container) []string {
176
+func getHostnamesForContainer(container *Container) []string {
238 177
 	if label, ok := container.Labels[config.Labels.Hostnames]; ok {
239 178
 		return strings.Split(strings.Replace(label, ",", " ", -1), " ")
240 179
 	} else {
@@ -242,19 +181,18 @@ func getHostnamesForContainer(container *model.Container) []string {
242 181
 	}
243 182
 }
244 183
 
245
-func getHostnames(containers map[string]*model.Container, config model.Config) (hostnames map[string]*model.Hostname) {
246
-	hostnames = make(map[string]*model.Hostname)
184
+func getHostnames(containers map[string]*Container) (hostnames map[string]*Hostname) {
185
+	hostnames = make(map[string]*Hostname)
247 186
 	for _, container := range containers {
248 187
 		if label, ok := container.Labels[config.Labels.Hostnames]; ok {
249 188
 			names := strings.Split(strings.Replace(label, ",", " ", -1), " ")
250 189
 			if hostname, ok := hostnames[names[0]]; ok {
251 190
 				hostname.Containers = append(hostname.Containers, container)
252 191
 			} else {
253
-				hostnames[names[0]] = &model.Hostname{
192
+				hostnames[names[0]] = &Hostname{
254 193
 					Name:            names[0],
255 194
 					Alternatives:    make(map[string]bool),
256
-					Containers:      []*model.Container{container},
257
-					CertActions:     config.DefaultCertActions,
195
+					Containers:      []*Container{container},
258 196
 					CertDestination: config.DefaultCertDestination,
259 197
 				}
260 198
 			}
@@ -269,13 +207,13 @@ func getHostnames(containers map[string]*model.Container, config model.Config) (
269 207
 	return
270 208
 }
271 209
 
272
-func addAlternatives(hostname *model.Hostname, alternatives []string) {
210
+func addAlternatives(hostname *Hostname, alternatives []string) {
273 211
 	for _, alternative := range alternatives {
274 212
 		hostname.Alternatives[alternative] = true
275 213
 	}
276 214
 }
277 215
 
278
-func deployCertForContainer(container *model.Container) bool {
216
+func deployCertForContainer(container *Container) bool {
279 217
 	hostnames := getHostnamesForContainer(container)
280 218
 	if len(hostnames) == 0 {
281 219
 		logger.Debugf("No labels found for container %s", container.Name)

+ 0
- 84
model/model.go View File

@@ -1,84 +0,0 @@
1
-package model
2
-
3
-import (
4
-	"github.com/xenolf/lego/certcrypto"
5
-	"time"
6
-)
7
-
8
-// CertActions define what will be done with a certificate
9
-type CertActions uint8
10
-
11
-// constants defining CertActions
12
-const (
13
-	// COMBINE the full chain and private key into one file
14
-	COMBINE CertActions = 1 << iota
15
-	// FLATTEN the directory structure so all files are in one dir
16
-	FLATTEN
17
-	// CHMOD the files so they are world readable (potentially dangerous!)
18
-	CHMOD
19
-)
20
-
21
-// Container models a docker container that is running on the system.
22
-type Container struct {
23
-	Id     string
24
-	Name   string
25
-	Labels map[string]string
26
-}
27
-
28
-// LabelConfig describes the labels used for various properties.
29
-type LabelConfig struct {
30
-	Hostnames   string
31
-	RequireAuth string
32
-}
33
-
34
-// AcmeConfig describes the configuration to use for getting certs using ACME.
35
-type AcmeConfig struct {
36
-	Email         string
37
-	DnsProvider   string
38
-	Endpoint      string
39
-	KeyType       certcrypto.KeyType
40
-	CacheLocation string
41
-}
42
-
43
-// Hostname describes a DNS name used for proxying, retrieving certificates, etc.
44
-type Hostname struct {
45
-	Name            string
46
-	Alternatives    map[string]bool
47
-	Containers      []*Container
48
-	CertActions     CertActions
49
-	CertDestination string
50
-	RequiresAuth    bool
51
-	AuthGroup       string
52
-}
53
-
54
-// Config is the user-definable configuration for Dotege.
55
-type Config struct {
56
-	Templates              []TemplateConfig
57
-	Signals                []ContainerSignal
58
-	Labels                 LabelConfig
59
-	DefaultCertActions     CertActions
60
-	DefaultCertDestination string
61
-	Acme                   AcmeConfig
62
-}
63
-
64
-// TemplateConfig configures a single template for the generator.
65
-type TemplateConfig struct {
66
-	Source      string
67
-	Destination string
68
-}
69
-
70
-// ContainerSignal describes a container that should be sent a signal when the config/certs change.
71
-type ContainerSignal struct {
72
-	Name   string
73
-	Signal string
74
-}
75
-
76
-// FoundCertificate describes a certificate we've located on disk.
77
-type FoundCertificate struct {
78
-	Hostname   string
79
-	Cert       string
80
-	Chain      string
81
-	FullChain  string
82
-	PrivateKey string
83
-	ModTime    time.Time
84
-}

template_generator.go → templates.go View File

@@ -1,8 +1,6 @@
1 1
 package main
2 2
 
3 3
 import (
4
-	"github.com/csmith/dotege/model"
5
-	"go.uber.org/zap"
6 4
 	"io/ioutil"
7 5
 	"path"
8 6
 	"sort"
@@ -11,18 +9,17 @@ import (
11 9
 )
12 10
 
13 11
 type Context struct {
14
-	Containers map[string]*model.Container
15
-	Hostnames  map[string]*model.Hostname
12
+	Containers map[string]*Container
13
+	Hostnames  map[string]*Hostname
16 14
 }
17 15
 
18 16
 type Template struct {
19
-	config   model.TemplateConfig
17
+	config   TemplateConfig
20 18
 	content  string
21 19
 	template *template.Template
22 20
 }
23 21
 
24 22
 type TemplateGenerator struct {
25
-	logger    *zap.SugaredLogger
26 23
 	templates []*Template
27 24
 }
28 25
 
@@ -37,17 +34,15 @@ var funcMap = template.FuncMap{
37 34
 	},
38 35
 }
39 36
 
40
-func NewTemplateGenerator(logger *zap.SugaredLogger) *TemplateGenerator {
41
-	return &TemplateGenerator{
42
-		logger: logger,
43
-	}
37
+func NewTemplateGenerator() *TemplateGenerator {
38
+	return &TemplateGenerator{}
44 39
 }
45 40
 
46
-func (t *TemplateGenerator) AddTemplate(config model.TemplateConfig) {
47
-	t.logger.Infof("Adding template from %s, writing to %s", config.Source, config.Destination)
41
+func (t *TemplateGenerator) AddTemplate(config TemplateConfig) {
42
+	logger.Infof("Registered template from %s, writing to %s", config.Source, config.Destination)
48 43
 	tmpl, err := template.New(path.Base(config.Source)).Funcs(funcMap).ParseFiles(config.Source)
49 44
 	if err != nil {
50
-		t.logger.Fatal("Unable to parse template", err)
45
+		logger.Fatal("Unable to parse template", err)
51 46
 	}
52 47
 
53 48
 	buf, _ := ioutil.ReadFile(config.Destination)
@@ -60,7 +55,7 @@ func (t *TemplateGenerator) AddTemplate(config model.TemplateConfig) {
60 55
 
61 56
 func (t *TemplateGenerator) Generate(context Context) (updated bool) {
62 57
 	for _, tmpl := range t.templates {
63
-		t.logger.Debugf("Checking for updates to %s", tmpl.config.Source)
58
+		logger.Debugf("Checking for updates to %s", tmpl.config.Source)
64 59
 		builder := &strings.Builder{}
65 60
 		err := tmpl.template.Execute(builder, context)
66 61
 		if err != nil {
@@ -68,12 +63,14 @@ func (t *TemplateGenerator) Generate(context Context) (updated bool) {
68 63
 		}
69 64
 		if tmpl.content != builder.String() {
70 65
 			updated = true
71
-			t.logger.Infof("Writing updated template to %s", tmpl.config.Destination)
66
+			logger.Infof("Writing updated template to %s", tmpl.config.Destination)
72 67
 			tmpl.content = builder.String()
73 68
 			err = ioutil.WriteFile(tmpl.config.Destination, []byte(builder.String()), 0666)
74 69
 			if err != nil {
75
-				t.logger.Fatal("Unable to write template", err)
70
+				logger.Fatal("Unable to write template", err)
76 71
 			}
72
+		} else {
73
+			logger.Debugf("Not writing template to %s as content is the same", tmpl.config.Destination)
77 74
 		}
78 75
 	}
79 76
 	return

Loading…
Cancel
Save