Skip to content

Commit d01d46b

Browse files
committed
Templates
1 parent fdffd1b commit d01d46b

File tree

7 files changed

+133
-69
lines changed

7 files changed

+133
-69
lines changed

Dockerfile

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ RUN apk add --no-cache --virtual .build-deps curl unzip && \
1111
RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
1212
RUN mkdir -p /cfg/tmpl
1313
RUN mkdir /consul_templates
14+
RUN mkdir /templates
1415
RUN mkdir -p /certs
1516

1617
ENV CONSUL_ADDRESS="" \

README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ The following query arguments can be used to send as a *reconfigure* request to
7575
|Query |Description |Required|Default|Example |
7676
|-------------|--------------------------------------------------------------------------------|--------|-------|-------------|
7777
|aclName |ACLs are ordered alphabetically by their names. If not specified, serviceName is used instead.|No||05-go-demo-acl|
78-
|consulTemplateBePath|The path to the Consul Template representing a snippet of the backend configuration. If specified, the proxy template will be loaded from the specified file.|||/consul_templates/tmpl/go-demo-be.tmpl|
79-
|consulTemplateFePath|The path to the Consul Template representing a snippet of the frontend configuration. If specified, the proxy template will be loaded from the specified file.|||/consul_templates/tmpl/go-demo-fe.tmpl|
78+
|consulTemplateBePath|The path to the Consul Template representing a snippet of the backend configuration. If specified, the proxy template will be loaded from the specified file.|||/consul_templates/go-demo-be.tmpl|
79+
|consulTemplateFePath|The path to the Consul Template representing a snippet of the frontend configuration. If specified, the proxy template will be loaded from the specified file.|||/consul_templates/go-demo-fe.tmpl|
8080
|distribute |Whether to distribute a request to all the instances of the proxy. Used only in the *swarm* mode.|No|false|true|
8181
|pathType |The ACL derivative. Defaults to *path_beg*. See [HAProxy path](https://cbonte.github.io/haproxy-dconv/configuration-1.5.html#7.3.6-path) for more info.|No||path_beg|
8282
|port |The internal port of a service that should be reconfigured. The port is used only in the *swarm* mode|Only in *swarm* mode|||8080|
@@ -85,6 +85,8 @@ The following query arguments can be used to send as a *reconfigure* request to
8585
|serviceDomain|The domain of the service. If specified, the proxy will allow access only to requests coming to that domain. Multiple domains should be separated with comma (`,`).|No||ecme.com|
8686
|serviceName |The name of the service. It must match the name of the Swarm service or the one stored in Consul.|Yes | |go-demo |
8787
|servicePath |The URL path of the service. Multiple values should be separated with comma (`,`).|Yes (unless consulTemplatePath is present)||/api/v1/books|
88+
|templateBePath|The path to the template representing a snippet of the backend configuration. If specified, the backend template will be loaded from the specified file. If specified, `templateFePath` must be set as well|||/templates/go-demo-be.tmpl|
89+
|templateFePath|The path to the template representing a snippet of the frontend configuration. If specified, the frontend template will be loaded from the specified file. If specified, `templateBePath` must be set as well|||/templates/go-demo-fe.tmpl|
8890
|skipCheck |Whether to skip adding proxy checks. This option is used only in the *default* mode.|No |false |true |
8991
|users |A comma-separated list of credentials(<user>:<pass>) for HTTP basic auth, which applies only to the service that will be reconfigured.|No||user1:pass1,user2:pass2|
9092

TODO.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# TODO
22

3-
## Template
3+
## Logging
44

55
[ ] Implement
66
[ ] Add integration tests

reconfigure.go

+19-7
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ type ServiceReconfigure struct {
5959
LookupRetryInterval int
6060
ReqRepSearch string
6161
ReqRepReplace string
62+
TemplateFePath string
63+
TemplateBePath string
6264
}
6365

6466
type BaseReconfigure struct {
@@ -271,7 +273,17 @@ func (m *Reconfigure) putToConsul(addresses []string, sr ServiceReconfigure, ins
271273
}
272274

273275
func (m *Reconfigure) GetTemplates(sr ServiceReconfigure) (front, back string, err error) {
274-
if len(sr.ConsulTemplateFePath) > 0 && len(sr.ConsulTemplateBePath) > 0 {
276+
if len(sr.TemplateFePath) > 0 && len(sr.TemplateBePath) > 0 {
277+
feTmpl, err := readTemplateFile(sr.TemplateFePath)
278+
if err != nil {
279+
return "", "", err
280+
}
281+
beTmpl, err := readTemplateFile(sr.TemplateBePath)
282+
if err != nil {
283+
return "", "", err
284+
}
285+
front, back = m.parseTemplate(string(feTmpl), string(beTmpl), sr)
286+
} else if len(sr.ConsulTemplateFePath) > 0 && len(sr.ConsulTemplateBePath) > 0 {
275287
front, err = m.getConsulTemplateFromFile(sr.ConsulTemplateFePath)
276288
if err != nil {
277289
return "", "", err
@@ -311,10 +323,6 @@ func (m *Reconfigure) getTemplateFromGo(sr ServiceReconfigure) (frontend, backen
311323
use_backend {{.AclName}}-be if url_{{.ServiceName}}{{.AclCondition}}`,
312324
sr.Acl,
313325
)
314-
// if len(sr.ReqRepSearch) > 0 && len(sr.ReqRepReplace) > 0 {
315-
// srcFront += `
316-
// reqrep {{.ReqRepSearch}} {{.ReqRepReplace}} if url_{{.ServiceName}}`
317-
// }
318326
srcBack := ""
319327
if len(sr.Users) > 0 {
320328
srcBack += `userlist {{.ServiceName}}Users{{range .Users}}
@@ -346,8 +354,12 @@ func (m *Reconfigure) getTemplateFromGo(sr ServiceReconfigure) (frontend, backen
346354
acl defaultUsersAcl http_auth(defaultUsers)
347355
http-request auth realm defaultRealm if !defaultUsersAcl`
348356
}
349-
tmplFront, _ := template.New("consulTemplate").Parse(srcFront)
350-
tmplBack, _ := template.New("consulTemplate").Parse(srcBack)
357+
return m.parseTemplate(srcFront, srcBack, sr)
358+
}
359+
360+
func (m *Reconfigure) parseTemplate(front, back string, sr ServiceReconfigure) (pFront, pBack string) {
361+
tmplFront, _ := template.New("consulTemplate").Parse(front)
362+
tmplBack, _ := template.New("consulTemplate").Parse(back)
351363
var ctFront bytes.Buffer
352364
var ctBack bytes.Buffer
353365
tmplFront.Execute(&ctFront, sr)

reconfigure_test.go

+76-59
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,14 @@ func (s *ReconfigureTestSuite) SetupTest() {
6666

6767
// GetTemplate
6868

69-
func (s ReconfigureTestSuite) Test_GetTemplate_ReturnsFormattedContent() {
69+
func (s ReconfigureTestSuite) Test_GetTemplates_ReturnsFormattedContent() {
7070
front, back, _ := s.reconfigure.GetTemplates(s.reconfigure.ServiceReconfigure)
7171

7272
s.Equal(s.ConsulTemplateFe, front)
7373
s.Equal(s.ConsulTemplateBe, back)
7474
}
7575

76-
func (s ReconfigureTestSuite) Test_GetTemplate_AddsHttpAuth_WhenUsersEnvIsPresent() {
76+
func (s ReconfigureTestSuite) Test_GetTemplates_AddsHttpAuth_WhenUsersEnvIsPresent() {
7777
usersOrig := os.Getenv("USERS")
7878
defer func() { os.Setenv("USERS", usersOrig) }()
7979
os.Setenv("USERS", "anything")
@@ -90,7 +90,7 @@ func (s ReconfigureTestSuite) Test_GetTemplate_AddsHttpAuth_WhenUsersEnvIsPresen
9090
s.Equal(expected, back)
9191
}
9292

93-
func (s ReconfigureTestSuite) Test_GetTemplate_AddsHttpAuth_WhenUsersIsPresent() {
93+
func (s ReconfigureTestSuite) Test_GetTemplates_AddsHttpAuth_WhenUsersIsPresent() {
9494
s.reconfigure.Users = []User{
9595
{ Username: "user-1", Password: "pass-1" },
9696
{ Username: "user-2", Password: "pass-2" },
@@ -112,7 +112,7 @@ backend myService-be
112112
s.Equal(expected, back)
113113
}
114114

115-
func (s ReconfigureTestSuite) Test_GetTemplate_ReturnsFormattedContent_WhenModeIsSwarm() {
115+
func (s ReconfigureTestSuite) Test_GetTemplates_ReturnsFormattedContent_WhenModeIsSwarm() {
116116
modes := []string{"service", "sWARm"}
117117
for _, mode := range modes {
118118
s.reconfigure.ServiceReconfigure.Mode = mode
@@ -127,7 +127,7 @@ func (s ReconfigureTestSuite) Test_GetTemplate_ReturnsFormattedContent_WhenModeI
127127
}
128128
}
129129

130-
func (s ReconfigureTestSuite) Test_GetTemplate_AddsHttpAuth_WhenModeIsSwarmAndUsersEnvIsPresent() {
130+
func (s ReconfigureTestSuite) Test_GetTemplates_AddsHttpAuth_WhenModeIsSwarmAndUsersEnvIsPresent() {
131131
usersOrig := os.Getenv("USERS")
132132
defer func() { os.Setenv("USERS", usersOrig) }()
133133
os.Setenv("USERS", "anything")
@@ -144,7 +144,7 @@ func (s ReconfigureTestSuite) Test_GetTemplate_AddsHttpAuth_WhenModeIsSwarmAndUs
144144
s.Equal(expected, actual)
145145
}
146146

147-
func (s ReconfigureTestSuite) Test_GetTemplate_AddsHttpAuth_WhenModeIsSwarmAndUsersIsPresent() {
147+
func (s ReconfigureTestSuite) Test_GetTemplates_AddsHttpAuth_WhenModeIsSwarmAndUsersIsPresent() {
148148
s.reconfigure.Users = []User{
149149
{ Username: "user-1", Password: "pass-1" },
150150
{ Username: "user-2", Password: "pass-2" },
@@ -166,7 +166,7 @@ backend myService-be
166166
s.Equal(expected, actual)
167167
}
168168

169-
func (s ReconfigureTestSuite) Test_GetTemplate_AddsHosts() {
169+
func (s ReconfigureTestSuite) Test_GetTemplates_AddsHosts() {
170170
s.ConsulTemplateFe = `
171171
acl url_myService path_beg path/to/my/service/api path_beg path/to/my/other/service/api
172172
acl domain_myService hdr_dom(host) -i my-domain.com my-other-domain.com
@@ -177,26 +177,7 @@ func (s ReconfigureTestSuite) Test_GetTemplate_AddsHosts() {
177177
s.Equal(s.ConsulTemplateFe, actual)
178178
}
179179

180-
//func (s ReconfigureTestSuite) Test_GetTemplate_AddsReqRep_WhenReqRepSearchAndReqRepReplaceArePresent() {
181-
// s.reconfigure.ReqRepSearch = "this"
182-
// s.reconfigure.ReqRepReplace = "that"
183-
// expected := fmt.Sprintf(`
184-
// acl url_%s path_beg path/to/my/service/api path_beg path/to/my/other/service/api
185-
// use_backend %s-be if url_myService
186-
// reqrep %s %s if url_%s`,
187-
// s.reconfigure.ServiceName,
188-
// s.reconfigure.ServiceName,
189-
// s.reconfigure.ReqRepSearch,
190-
// s.reconfigure.ReqRepReplace,
191-
// s.reconfigure.ServiceName,
192-
// )
193-
//
194-
// front, _, _ := s.reconfigure.GetTemplates(s.reconfigure.ServiceReconfigure)
195-
//
196-
// s.Equal(expected, front)
197-
//}
198-
199-
func (s ReconfigureTestSuite) Test_GetTemplate_AddsReqRep_WhenReqRepSearchAndReqRepReplaceArePresent() {
180+
func (s ReconfigureTestSuite) Test_GetTemplates_AddsReqRep_WhenReqRepSearchAndReqRepReplaceArePresent() {
200181
s.reconfigure.ReqRepSearch = "this"
201182
s.reconfigure.ReqRepReplace = "that"
202183
expected := fmt.Sprintf(`backend myService-be
@@ -216,7 +197,7 @@ func (s ReconfigureTestSuite) Test_GetTemplate_AddsReqRep_WhenReqRepSearchAndReq
216197
}
217198

218199

219-
func (s ReconfigureTestSuite) Test_GetTemplate_UsesAclNameForFrontEnd() {
200+
func (s ReconfigureTestSuite) Test_GetTemplates_UsesAclNameForFrontEnd() {
220201
s.reconfigure.AclName = "my-acl"
221202
s.ConsulTemplateFe = `
222203
acl url_myService path_beg path/to/my/service/api path_beg path/to/my/other/service/api
@@ -226,15 +207,15 @@ func (s ReconfigureTestSuite) Test_GetTemplate_UsesAclNameForFrontEnd() {
226207
s.Equal(s.ConsulTemplateFe, actual)
227208
}
228209

229-
func (s ReconfigureTestSuite) Test_GetTemplate_UsesPathReg() {
210+
func (s ReconfigureTestSuite) Test_GetTemplates_UsesPathReg() {
230211
s.ConsulTemplateFe = strings.Replace(s.ConsulTemplateFe, "path_beg", "path_reg", -1)
231212
s.reconfigure.PathType = "path_reg"
232213
front, _, _ := s.reconfigure.GetTemplates(s.reconfigure.ServiceReconfigure)
233214

234215
s.Equal(s.ConsulTemplateFe, front)
235216
}
236217

237-
func (s ReconfigureTestSuite) Test_GetTemplate_AddsColor() {
218+
func (s ReconfigureTestSuite) Test_GetTemplates_AddsColor() {
238219
s.reconfigure.ServiceColor = "black"
239220
expected := fmt.Sprintf(`service "%s-%s"`, s.ServiceName, s.reconfigure.ServiceColor)
240221

@@ -243,15 +224,15 @@ func (s ReconfigureTestSuite) Test_GetTemplate_AddsColor() {
243224
s.Contains(actual, expected)
244225
}
245226

246-
func (s ReconfigureTestSuite) Test_GetTemplate_DoesNotSetCheckWhenSkipCheckIsTrue() {
227+
func (s ReconfigureTestSuite) Test_GetTemplates_DoesNotSetCheckWhenSkipCheckIsTrue() {
247228
s.ConsulTemplateBe = strings.Replace(s.ConsulTemplateBe, " check", "", -1)
248229
s.reconfigure.SkipCheck = true
249230
_, actual, _ := s.reconfigure.GetTemplates(s.reconfigure.ServiceReconfigure)
250231

251232
s.Equal(s.ConsulTemplateBe, actual)
252233
}
253234

254-
func (s ReconfigureTestSuite) Test_GetTemplate_ReturnsFileContent_WhenConsulTemplatePathIsSet() {
235+
func (s ReconfigureTestSuite) Test_GetTemplates_ReturnsFileContent_WhenConsulTemplatePathIsSet() {
255236
expected := "This is content of a template"
256237
readTemplateFileOrig := readTemplateFile
257238
defer func() { readTemplateFile = readTemplateFileOrig }()
@@ -266,10 +247,71 @@ func (s ReconfigureTestSuite) Test_GetTemplate_ReturnsFileContent_WhenConsulTemp
266247
s.Equal(expected, actual)
267248
}
268249

269-
func (s ReconfigureTestSuite) Test_GetTemplate_ReturnsError_WhenConsulTemplateFileIsNotAvailable() {
250+
func (s ReconfigureTestSuite) Test_GetTemplates_ProcessesTemplateFromTemplatePath_WhenSpecified() {
251+
expectedFeFile := "/path/to/my/fe/template"
252+
expectedBeFile := "/path/to/my/be/template"
253+
expectedFe := fmt.Sprintf("This is service %s", s.reconfigure.ServiceName)
254+
expectedBe := fmt.Sprintf("This is path %s", s.reconfigure.ServicePath)
270255
readTemplateFileOrig := readTemplateFile
271256
defer func() { readTemplateFile = readTemplateFileOrig }()
272-
readTemplateFile = func(dirname string) ([]byte, error) {
257+
readTemplateFile = func(filename string) ([]byte, error) {
258+
if filename == expectedFeFile {
259+
return []byte("This is service {{.ServiceName}}"), nil
260+
} else if filename == expectedBeFile {
261+
return []byte("This is path {{.ServicePath}}"), nil
262+
}
263+
return []byte(""), fmt.Errorf("This is an error")
264+
}
265+
s.ServiceReconfigure.TemplateFePath = expectedFeFile
266+
s.ServiceReconfigure.TemplateBePath = expectedBeFile
267+
268+
actualFe, actualBe, _ := s.reconfigure.GetTemplates(s.ServiceReconfigure)
269+
270+
s.Equal(expectedFe, actualFe)
271+
s.Equal(expectedBe, actualBe)
272+
}
273+
274+
func (s ReconfigureTestSuite) Test_GetTemplates_ReturnsError_WhenTemplateFePathIsNotPresent() {
275+
testFilename := "/path/to/my/template"
276+
readTemplateFileOrig := readTemplateFile
277+
defer func() { readTemplateFile = readTemplateFileOrig }()
278+
readTemplateFile = func(filename string) ([]byte, error) {
279+
if filename == testFilename {
280+
return []byte(""), fmt.Errorf("This is an error")
281+
}
282+
return []byte(""), nil
283+
}
284+
s.ServiceReconfigure.TemplateFePath = testFilename
285+
s.ServiceReconfigure.TemplateBePath = "not/under/test"
286+
287+
_, _, err := s.reconfigure.GetTemplates(s.ServiceReconfigure)
288+
289+
s.Error(err)
290+
}
291+
292+
func (s ReconfigureTestSuite) Test_GetTemplates_ReturnsError_WhenTemplateBePathIsNotPresent() {
293+
testFilename := "/path/to/my/template"
294+
readTemplateFileOrig := readTemplateFile
295+
defer func() { readTemplateFile = readTemplateFileOrig }()
296+
readTemplateFile = func(filename string) ([]byte, error) {
297+
if filename == testFilename {
298+
return []byte(""), fmt.Errorf("This is an error")
299+
}
300+
return []byte(""), nil
301+
}
302+
303+
s.ServiceReconfigure.TemplateFePath = "not/under/test"
304+
s.ServiceReconfigure.TemplateBePath = testFilename
305+
306+
_, _, err := s.reconfigure.GetTemplates(s.ServiceReconfigure)
307+
308+
s.Error(err)
309+
}
310+
311+
func (s ReconfigureTestSuite) Test_GetTemplates_ReturnsError_WhenConsulTemplateFileIsNotAvailable() {
312+
readTemplateFileOrig := readTemplateFile
313+
defer func() { readTemplateFile = readTemplateFileOrig }()
314+
readTemplateFile = func(filename string) ([]byte, error) {
273315
return nil, fmt.Errorf("This is an error")
274316
}
275317
s.ServiceReconfigure.ConsulTemplateFePath = "/path/to/my/consul/fe/template"
@@ -630,31 +672,6 @@ func (s *ReconfigureTestSuite) Test_ReloadAllServices_ReturnsError_WhenFail() {
630672
s.Error(err)
631673
}
632674

633-
// TODO: Remove
634-
//func (s *ReconfigureTestSuite) Test_ReloadAllServices_WritesTemplateToFile() {
635-
// mockObj := getRegistrarableMock("")
636-
// registryInstanceOrig := registryInstance
637-
// defer func() { registryInstance = registryInstanceOrig }()
638-
// registryInstance = mockObj
639-
// s.ConsulTemplateBe = `backend myService-be
640-
// mode http
641-
// {{range $i, $e := service "myService-orange" "any"}}
642-
// server {{$e.Node}}_{{$i}}_{{$e.Port}} {{$e.Address}}:{{$e.Port}} check
643-
// {{end}}`
644-
//
645-
// expectedArgs := registry.CreateConfigsArgs{
646-
// Addresses: []string{s.Server.URL},
647-
// TemplatesPath: s.TemplatesPath,
648-
// FeFile: ServiceTemplateFeFilename,
649-
// FeTemplate: s.ConsulTemplateFe,
650-
// BeFile: ServiceTemplateBeFilename,
651-
// BeTemplate: s.ConsulTemplateBe,
652-
// ServiceName: s.ServiceName,
653-
// }
654-
//
655-
// mockObj.AssertCalled(s.T(), "CreateConfigs", &expectedArgs)
656-
//}
657-
658675
func (s *ReconfigureTestSuite) Test_ReloadAllServices_InvokesProxyCreateConfigFromTemplates() {
659676
mockObj := getProxyMock("")
660677
proxyOrig := haproxy.Instance

server.go

+6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ type Response struct {
5050
Users []User
5151
ReqRepSearch string
5252
ReqRepReplace string
53+
TemplateFePath string
54+
TemplateBePath string
5355
}
5456

5557
func (m *Serve) Execute(args []string) error {
@@ -132,6 +134,8 @@ func (m *Serve) reconfigure(w http.ResponseWriter, req *http.Request) {
132134
Mode: m.Mode,
133135
ReqRepSearch: req.URL.Query().Get("reqRepSearch"),
134136
ReqRepReplace: req.URL.Query().Get("reqRepReplace"),
137+
TemplateFePath: req.URL.Query().Get("templateFePath"),
138+
TemplateBePath: req.URL.Query().Get("templateBePath"),
135139
}
136140
if len(req.URL.Query().Get("servicePath")) > 0 {
137141
sr.ServicePath = strings.Split(req.URL.Query().Get("servicePath"), ",")
@@ -169,6 +173,8 @@ func (m *Serve) reconfigure(w http.ResponseWriter, req *http.Request) {
169173
Users: sr.Users,
170174
ReqRepSearch: sr.ReqRepSearch,
171175
ReqRepReplace: sr.ReqRepReplace,
176+
TemplateFePath: sr.TemplateFePath,
177+
TemplateBePath: sr.TemplateBePath,
172178
}
173179
if m.isValidReconf(sr.ServiceName, sr.ServicePath, sr.ServiceDomain, sr.ConsulTemplateFePath) {
174180
if (strings.EqualFold("service", m.Mode) || strings.EqualFold("swarm", m.Mode)) && len(sr.Port) == 0 {

server_test.go

+26
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,32 @@ func (s *ServerTestSuite) Test_ServeHTTP_ReturnsJsonWithReqRep_WhenPresent() {
438438
s.ResponseWriter.AssertCalled(s.T(), "Write", []byte(expected))
439439
}
440440

441+
func (s *ServerTestSuite) Test_ServeHTTP_ReturnsJsonWithTemplatePaths_WhenPresent() {
442+
templateFePath := "something"
443+
templateBePath := "else"
444+
url := fmt.Sprintf(
445+
"%s&templateFePath=%s&templateBePath=%s",
446+
s.ReconfigureUrl,
447+
templateFePath,
448+
templateBePath,
449+
)
450+
req, _ := http.NewRequest("GET", url, nil)
451+
expected, _ := json.Marshal(Response{
452+
Status: "OK",
453+
ServiceName: s.ServiceName,
454+
ServiceColor: s.ServiceColor,
455+
ServicePath: s.ServicePath,
456+
ServiceDomain: s.ServiceDomain,
457+
TemplateFePath: templateFePath,
458+
TemplateBePath: templateBePath,
459+
})
460+
461+
srv := Serve{}
462+
srv.ServeHTTP(s.ResponseWriter, req)
463+
464+
s.ResponseWriter.AssertCalled(s.T(), "Write", []byte(expected))
465+
}
466+
441467
func (s *ServerTestSuite) Test_ServeHTTP_ReturnsJsonWithUsers_WhenPresent() {
442468
users := []User {
443469
{ Username: "user1", Password: "pass1" },

0 commit comments

Comments
 (0)