Browse Source

Add signalling on update

master
Chris Smith 5 years ago
parent
commit
7daea071e8
4 changed files with 59 additions and 3 deletions
  1. 7
    0
      README.adoc
  2. 42
    2
      dotege.go
  3. 7
    0
      model/model.go
  4. 3
    1
      template_generator.go

+ 7
- 0
README.adoc View File

@@ -38,6 +38,13 @@ values are:
38 38
 +
39 39
 The default value is `P384`.
40 40
 
41
+`DOTEGE_SIGNAL_CONTAINER`::
42
+The name of a container that should be sent a signal when the template or certificates
43
+are changed. No signal is sent if not specified.
44
+
45
+`DOTEGE_SIGNAL_TYPE`::
46
+The type of signal to send to the `DOTEGE_SIGNAL_CONTAINER`. Defaults to `HUP`.
47
+
41 48
 `DOTEGE_TEMPLATE_DESTINATION`::
42 49
 Location to write the templated configuration file to. Defaults to `/data/output/haproxy.cfg`.
43 50
 

+ 42
- 2
dotege.go View File

@@ -1,6 +1,7 @@
1 1
 package main
2 2
 
3 3
 import (
4
+	"context"
4 5
 	"fmt"
5 6
 	"github.com/csmith/dotege/model"
6 7
 	"github.com/docker/docker/client"
@@ -27,6 +28,10 @@ const (
27 28
 	envAcmeKeyTypeDefault         = "P384"
28 29
 	envAcmeCacheLocationKey       = "DOTEGE_ACME_CACHE_FILE"
29 30
 	envAcmeCacheLocationDefault   = "/data/config/certs.json"
31
+	envSignalContainerKey         = "DOTEGE_SIGNAL_CONTAINER"
32
+	envSignalContainerDefault     = ""
33
+	envSignalTypeKey              = "DOTEGE_SIGNAL_TYPE"
34
+	envSignalTypeDefault          = "HUP"
30 35
 	envTemplateDestinationKey     = "DOTEGE_TEMPLATE_DESTINATION"
31 36
 	envTemplateDestinationDefault = "/data/output/haproxy.cfg"
32 37
 	envTemplateSourceKey          = "DOTEGE_TEMPLATE_SOURCE"
@@ -37,6 +42,8 @@ var (
37 42
 	logger             *zap.SugaredLogger
38 43
 	certificateManager *CertificateManager
39 44
 	config             *model.Config
45
+	dockerClient       *client.Client
46
+	containers         = make(map[string]*model.Container)
40 47
 )
41 48
 
42 49
 func requiredVar(key string) (value string) {
@@ -81,6 +88,20 @@ func createLogger() *zap.SugaredLogger {
81 88
 	return logger.Sugar()
82 89
 }
83 90
 
91
+func createSignalConfig() []model.ContainerSignal {
92
+	name := optionalVar(envSignalContainerKey, envSignalContainerDefault)
93
+	if name == envSignalContainerDefault {
94
+		return []model.ContainerSignal{}
95
+	} else {
96
+		return []model.ContainerSignal{
97
+			{
98
+				Name:   name,
99
+				Signal: optionalVar(envSignalTypeKey, envSignalTypeDefault),
100
+			},
101
+		}
102
+	}
103
+}
104
+
84 105
 func createConfig() {
85 106
 	config = &model.Config{
86 107
 		Templates: []model.TemplateConfig{
@@ -100,6 +121,7 @@ func createConfig() {
100 121
 			KeyType:       certcrypto.KeyType(optionalVar(envAcmeKeyTypeKey, envAcmeKeyTypeDefault)),
101 122
 			CacheLocation: optionalVar(envAcmeCacheLocationKey, envAcmeCacheLocationDefault),
102 123
 		},
124
+		Signals:                createSignalConfig(),
103 125
 		DefaultCertActions:     model.COMBINE | model.FLATTEN,
104 126
 		DefaultCertDestination: optionalVar(envCertDestinationKey, envCertDestinationDefault),
105 127
 	}
@@ -139,13 +161,13 @@ func main() {
139 161
 
140 162
 	jitterTimer := time.NewTimer(time.Minute)
141 163
 	redeployTimer := time.NewTicker(time.Hour * 24)
142
-	containers := make(map[string]*model.Container)
143 164
 
144 165
 	go func() {
145 166
 		err := monitorContainers(dockerClient, dockerStopChan, func(container *model.Container) {
146 167
 			containers[container.Name] = container
147 168
 			jitterTimer.Reset(100 * time.Millisecond)
148 169
 			deployCertForContainer(container)
170
+			signalContainer()
149 171
 		}, func(name string) {
150 172
 			delete(containers, name)
151 173
 			jitterTimer.Reset(100 * time.Millisecond)
@@ -161,14 +183,18 @@ func main() {
161 183
 			select {
162 184
 			case <-jitterTimer.C:
163 185
 				hostnames := getHostnames(containers, *config)
164
-				templateGenerator.Generate(Context{
186
+				updated := templateGenerator.Generate(Context{
165 187
 					Containers: containers,
166 188
 					Hostnames:  hostnames,
167 189
 				})
190
+				if updated {
191
+					signalContainer()
192
+				}
168 193
 			case <-redeployTimer.C:
169 194
 				logger.Info("Performing periodic certificate refresh")
170 195
 				for _, container := range containers {
171 196
 					deployCertForContainer(container)
197
+					signalContainer()
172 198
 				}
173 199
 			}
174 200
 		}
@@ -183,6 +209,20 @@ func main() {
183 209
 	}
184 210
 }
185 211
 
212
+func signalContainer() {
213
+	for _, s := range config.Signals {
214
+		container, ok := containers[s.Name]
215
+		if ok {
216
+			err := dockerClient.ContainerKill(context.Background(), container.Id, s.Signal)
217
+			if err != nil {
218
+				logger.Errorf("Unable to send signal %s to container %s: %s", s.Signal, s.Name, err.Error())
219
+			}
220
+		} else {
221
+			logger.Warnf("Couldn't signal container %s as it is not running", s.Name)
222
+		}
223
+	}
224
+}
225
+
186 226
 func getHostnamesForContainer(container *model.Container) []string {
187 227
 	if label, ok := container.Labels[config.Labels.Hostnames]; ok {
188 228
 		return strings.Split(strings.Replace(label, ",", " ", -1), " ")

+ 7
- 0
model/model.go View File

@@ -54,6 +54,7 @@ type Hostname struct {
54 54
 // Config is the user-definable configuration for Dotege.
55 55
 type Config struct {
56 56
 	Templates              []TemplateConfig
57
+	Signals                []ContainerSignal
57 58
 	Labels                 LabelConfig
58 59
 	DefaultCertActions     CertActions
59 60
 	DefaultCertDestination string
@@ -66,6 +67,12 @@ type TemplateConfig struct {
66 67
 	Destination string
67 68
 }
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
+
69 76
 // FoundCertificate describes a certificate we've located on disk.
70 77
 type FoundCertificate struct {
71 78
 	Hostname   string

+ 3
- 1
template_generator.go View File

@@ -58,7 +58,7 @@ func (t *TemplateGenerator) AddTemplate(config model.TemplateConfig) {
58 58
 	})
59 59
 }
60 60
 
61
-func (t *TemplateGenerator) Generate(context Context) {
61
+func (t *TemplateGenerator) Generate(context Context) (updated bool) {
62 62
 	for _, tmpl := range t.templates {
63 63
 		t.logger.Debugf("Checking for updates to %s", tmpl.config.Source)
64 64
 		builder := &strings.Builder{}
@@ -67,6 +67,7 @@ func (t *TemplateGenerator) Generate(context Context) {
67 67
 			panic(err)
68 68
 		}
69 69
 		if tmpl.content != builder.String() {
70
+			updated = true
70 71
 			t.logger.Infof("Writing updated template to %s", tmpl.config.Destination)
71 72
 			tmpl.content = builder.String()
72 73
 			err = ioutil.WriteFile(tmpl.config.Destination, []byte(builder.String()), 0666)
@@ -75,4 +76,5 @@ func (t *TemplateGenerator) Generate(context Context) {
75 76
 			}
76 77
 		}
77 78
 	}
79
+	return
78 80
 }

Loading…
Cancel
Save