Browse Source

Initial import

Generates two hardcoded templates to stdout
master
Chris Smith 5 years ago
commit
3e81490ae2
6 changed files with 264 additions and 0 deletions
  1. 1
    0
      .gitignore
  2. 180
    0
      dotege.go
  3. 13
    0
      go.mod
  4. 27
    0
      go.sum
  5. 5
    0
      templates/domains.txt.tpl
  6. 38
    0
      templates/haproxy.cfg.tpl

+ 1
- 0
.gitignore View File

@@ -0,0 +1 @@
1
+/.idea

+ 180
- 0
dotege.go View File

@@ -0,0 +1,180 @@
1
+package main
2
+
3
+import (
4
+	"fmt"
5
+	"github.com/docker/distribution/context"
6
+	"github.com/docker/docker/api/types"
7
+	"github.com/docker/docker/api/types/filters"
8
+	"github.com/docker/docker/client"
9
+	"os"
10
+	"os/signal"
11
+	"sort"
12
+	"strings"
13
+	"syscall"
14
+	"text/template"
15
+	"time"
16
+)
17
+
18
+type Container struct {
19
+	Id     string
20
+	Name   string
21
+	Labels map[string]string
22
+}
23
+
24
+type Context struct {
25
+	Containers map[string]Container
26
+}
27
+
28
+var funcMap = template.FuncMap{
29
+	"replace": func(from, to, input string) string { return strings.Replace(input, from, to, -1) },
30
+	"split":   func(sep, input string) []string { return strings.Split(input, sep) },
31
+	"join":    func(sep string, input []string) string { return strings.Join(input, sep) },
32
+	"sortlines": func(input string) string {
33
+		lines := strings.Split(input, "\n")
34
+		sort.Strings(lines)
35
+		return strings.Join(lines, "\n")
36
+	},
37
+}
38
+
39
+func main() {
40
+	sigs := make(chan os.Signal, 1)
41
+	done := make(chan bool, 1)
42
+	containerChan := make(chan Container, 1)
43
+	expiryChan := make(chan string, 1)
44
+
45
+	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
46
+
47
+	cli, err := client.NewEnvClient()
48
+	if err != nil {
49
+		panic(err)
50
+	}
51
+
52
+	go func() {
53
+		sig := <-sigs
54
+		fmt.Printf("Received %s signal\n", sig)
55
+		err := cli.Close()
56
+		if err != nil {
57
+			panic(err)
58
+		}
59
+		done <- true
60
+	}()
61
+
62
+	go func() {
63
+		const deletionTime = 10 * time.Second
64
+
65
+		expiryTimes := make(map[string]time.Time)
66
+		expiryTimer := time.NewTimer(time.Hour)
67
+		nextExpiry := time.Now()
68
+		expiryTimer.Stop()
69
+
70
+		args := filters.NewArgs()
71
+		args.Add("type", "container")
72
+		args.Add("event", "create")
73
+		args.Add("event", "destroy")
74
+		eventsChan, errChan := cli.Events(context.Background(), types.EventsOptions{Filters: args})
75
+
76
+		containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{})
77
+		if err != nil {
78
+			panic(err)
79
+		}
80
+
81
+		for _, container := range containers {
82
+			containerChan <- Container{
83
+				Id:     container.ID,
84
+				Name:   container.Names[0][1:],
85
+				Labels: container.Labels,
86
+			}
87
+		}
88
+
89
+		for {
90
+			select {
91
+			case event := <-eventsChan:
92
+				if event.Action == "create" {
93
+					container, err := cli.ContainerInspect(context.Background(), event.Actor.ID)
94
+					if err != nil {
95
+						panic(err)
96
+					}
97
+					containerChan <- Container{
98
+						Id:     container.ID,
99
+						Name:   container.Name[1:],
100
+						Labels: container.Config.Labels,
101
+					}
102
+				} else {
103
+					now := time.Now()
104
+					expiryTime := now.Add(deletionTime)
105
+					expiryTimes[event.Actor.Attributes["name"]] = expiryTime
106
+					fmt.Printf("Scheduling expiry timer for %s\n", event.Actor.Attributes["name"])
107
+					if nextExpiry.Before(now) || nextExpiry.After(expiryTime) {
108
+						fmt.Printf("Starting expiry timer with default duration\n")
109
+						expiryTimer.Reset(deletionTime + 1*time.Second)
110
+						nextExpiry = expiryTime
111
+					}
112
+				}
113
+
114
+			case <-expiryTimer.C:
115
+				now := time.Now()
116
+				next := 0 * time.Second
117
+
118
+				for name, expiryTime := range expiryTimes {
119
+					if expiryTime.Before(now) {
120
+						fmt.Printf("Expiring %s\n", name)
121
+						delete(expiryTimes, name)
122
+						expiryChan <- name
123
+					} else if next == 0 || expiryTime.Sub(now) < next {
124
+						next = expiryTime.Sub(now)
125
+					}
126
+				}
127
+
128
+				if next > 0 {
129
+					fmt.Printf("Starting expiry timer with duration %s\n", next)
130
+					expiryTimer.Reset(next + 1*time.Second)
131
+					nextExpiry = now.Add(next)
132
+				}
133
+
134
+			case err := <-errChan:
135
+				panic(err)
136
+			}
137
+		}
138
+	}()
139
+
140
+	go func() {
141
+		var templates []*template.Template
142
+		containers := make(map[string]Container)
143
+		timer := time.NewTimer(time.Hour)
144
+		timer.Stop()
145
+
146
+		tmpl, err := template.New("domains.txt.tpl").Funcs(funcMap).ParseFiles("./templates/domains.txt.tpl")
147
+		if err != nil {
148
+			panic(err)
149
+		}
150
+		templates = append(templates, tmpl)
151
+
152
+		tmpl, err = template.New("haproxy.cfg.tpl").Funcs(funcMap).ParseFiles("./templates/haproxy.cfg.tpl")
153
+		if err != nil {
154
+			panic(err)
155
+		}
156
+		templates = append(templates, tmpl)
157
+
158
+		for {
159
+			select {
160
+			case container := <-containerChan:
161
+				containers[container.Name] = container
162
+				timer.Reset(100 * time.Millisecond)
163
+			case name := <-expiryChan:
164
+				delete(containers, name)
165
+				timer.Reset(100 * time.Millisecond)
166
+			case <-timer.C:
167
+				for _, tmpl := range templates {
168
+					fmt.Printf("--- Writing %s ---\n", tmpl.Name())
169
+					err = tmpl.Execute(os.Stdout, Context{Containers: containers})
170
+					fmt.Printf("--- / writing %s ---\n", tmpl.Name())
171
+					if err != nil {
172
+						panic(err)
173
+					}
174
+				}
175
+			}
176
+		}
177
+	}()
178
+
179
+	<-done
180
+}

+ 13
- 0
go.mod View File

@@ -0,0 +1,13 @@
1
+module github.com/csmith/dotege
2
+
3
+require (
4
+	github.com/docker/distribution v2.7.1+incompatible // indirect
5
+	github.com/docker/docker v1.13.1
6
+	github.com/docker/go-connections v0.4.0 // indirect
7
+	github.com/docker/go-units v0.3.3 // indirect
8
+	github.com/gorilla/mux v1.7.0 // indirect
9
+	github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
10
+	github.com/pkg/errors v0.8.1 // indirect
11
+	github.com/sirupsen/logrus v1.3.0 // indirect
12
+	golang.org/x/net v0.0.0-20190206173232-65e2d4e15006 // indirect
13
+)

+ 27
- 0
go.sum View File

@@ -0,0 +1,27 @@
1
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2
+github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
3
+github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
4
+github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
5
+github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
6
+github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
7
+github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
8
+github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
9
+github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
10
+github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U=
11
+github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
12
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
13
+github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
14
+github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
15
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
16
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
17
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
18
+github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
19
+github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
20
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
21
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
22
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
23
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
24
+golang.org/x/net v0.0.0-20190206173232-65e2d4e15006 h1:bfLnR+k0tq5Lqt6dflRLcZiz6UaXCMt3vhYJ1l4FQ80=
25
+golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
26
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
27
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

+ 5
- 0
templates/domains.txt.tpl View File

@@ -0,0 +1,5 @@
1
+{{ range .Containers }}
2
+    {{- with index .Labels "com.chameth.vhost" -}}
3
+        {{ . | replace "," " " }}{{ "\n" }}
4
+    {{- end -}}
5
+{{ end }}

+ 38
- 0
templates/haproxy.cfg.tpl View File

@@ -0,0 +1,38 @@
1
+global
2
+    ssl-default-bind-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
3
+    ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
4
+    ssl-default-server-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
5
+    ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
6
+
7
+resolvers docker_resolver
8
+    nameserver dns 127.0.0.11:53
9
+
10
+defaults
11
+    log global
12
+    mode    http
13
+    timeout connect 5000
14
+    timeout client 5000
15
+    timeout server 5000
16
+    compression algo gzip
17
+    compression type text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript
18
+
19
+frontend main
20
+    mode    http
21
+    bind    :443 ssl strict-sni alpn h2,http/1.1 crt /certs/certs/chameth.com/combined.pem
22
+    bind    :80
23
+    redirect scheme https code 301 if !{ ssl_fc }
24
+    http-response set-header Strict-Transport-Security max-age=15768000
25
+{{ range .Containers }}
26
+    {{- if index .Labels "com.chameth.port" -}}
27
+        {{- if index .Labels "com.chameth.vhost" }}
28
+    use_backend {{ .Name }} if { hdr(host) -i {{ index .Labels "com.chameth.vhost" | split "," | join " || hdr(host) -i " }} }
29
+        {{- end -}}
30
+    {{- end -}}
31
+{{ end }}
32
+{{ range .Containers }}
33
+    {{- if index .Labels "com.chameth.port" }}
34
+backend {{ .Name }}
35
+    mode http
36
+    server server1 {{ .Name }}:{{ index .Labels "com.chameth.port" }} check resolvers docker_resolver
37
+    {{- end -}}
38
+{{ end }}

Loading…
Cancel
Save