Skip to content

Commit 409a4e5

Browse files
committed
Merge pull request #104 from webner/skip-blank-lines
skip-blank-lines option
2 parents 4a45387 + 480ed4c commit 409a4e5

File tree

5 files changed

+123
-21
lines changed

5 files changed

+123
-21
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ $ docker run -d --name nginx-gen --volumes-from nginx \
7777
### Usage
7878
```
7979
$ docker-gen
80-
Usage: docker-gen [-config file] [-watch=false] [-notify="restart xyz"] [-notify-sighup="nginx-proxy"] [-interval=0] [-endpoint tcp|unix://..] [-tlsverify] [-tlscert file] [-tlskey file] [-tlscacert file] <template> [<dest>]
80+
Usage: docker-gen [-config file] [-watch=false] [-notify="restart xyz"] [-notify-sighup="nginx-proxy"] [-interval=0] [-endpoint tcp|unix://..] [-tlsverify] [-tlscert file] [-tlskey file] [-tlscacert file] [-keep-blank-lines] <template> [<dest>]
8181
```
8282

8383
*Options:*
@@ -92,6 +92,7 @@ Usage: docker-gen [-config file] [-watch=false] [-notify="restart xyz"] [-notify
9292
-notify-sighup="": send HUP signal to container. Equivalent to `docker kill -s HUP container-ID`
9393
-only-exposed=false: only include containers with exposed ports
9494
-only-published=false: only include containers with published ports (implies -only-exposed)
95+
-keep-blank-lines=false: keep blank lines in the output file
9596
-tlscacert="": path to TLS CA certificate file
9697
-tlscert="": path to TLS client certificate file
9798
-tlskey="": path to TLS client key file

docker-gen.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ var (
2929
configFiles stringslice
3030
configs ConfigFile
3131
interval int
32+
keepBlankLines bool
3233
endpoint string
3334
tlsCert string
3435
tlsKey string
@@ -114,6 +115,7 @@ type Config struct {
114115
OnlyExposed bool
115116
OnlyPublished bool
116117
Interval int
118+
KeepBlankLines bool
117119
}
118120

119121
type ConfigFile struct {
@@ -391,6 +393,7 @@ func initFlags() {
391393
"send HUP signal to container. Equivalent to `docker kill -s HUP container-ID`")
392394
flag.Var(&configFiles, "config", "config files with template directives. Config files will be merged if this option is specified multiple times.")
393395
flag.IntVar(&interval, "interval", 0, "notify command interval (secs)")
396+
flag.BoolVar(&keepBlankLines, "keep-blank-lines", false, "keep blank lines in the output file")
394397
flag.StringVar(&endpoint, "endpoint", "", "docker api endpoint (tcp|unix://..). Default unix:///var/run/docker.sock")
395398
flag.StringVar(&tlsCert, "tlscert", filepath.Join(certPath, "cert.pem"), "path to TLS client certificate file")
396399
flag.StringVar(&tlsKey, "tlskey", filepath.Join(certPath, "key.pem"), "path to TLS client key file")
@@ -431,6 +434,7 @@ func main() {
431434
OnlyExposed: onlyExposed,
432435
OnlyPublished: onlyPublished,
433436
Interval: interval,
437+
KeepBlankLines: keepBlankLines,
434438
}
435439
if notifySigHUPContainerID != "" {
436440
config.NotifyContainers[notifySigHUPContainerID] = docker.SIGHUP

template.go

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -362,12 +362,6 @@ func newTemplate(name string) *template.Template {
362362
}
363363

364364
func generateFile(config Config, containers Context) bool {
365-
templatePath := config.Template
366-
tmpl, err := newTemplate(filepath.Base(templatePath)).ParseFiles(templatePath)
367-
if err != nil {
368-
log.Fatalf("unable to parse template: %s", err)
369-
}
370-
371365
filteredContainers := Context{}
372366
if config.OnlyPublished {
373367
for _, container := range containers {
@@ -385,42 +379,41 @@ func generateFile(config Config, containers Context) bool {
385379
filteredContainers = containers
386380
}
387381

388-
dest := os.Stdout
382+
contents := executeTemplate(config.Template, filteredContainers)
383+
384+
if !config.KeepBlankLines {
385+
buf := new(bytes.Buffer)
386+
removeBlankLines(bytes.NewReader(contents), buf)
387+
contents = buf.Bytes()
388+
}
389+
389390
if config.Dest != "" {
390-
dest, err = ioutil.TempFile(filepath.Dir(config.Dest), "docker-gen")
391+
dest, err := ioutil.TempFile(filepath.Dir(config.Dest), "docker-gen")
391392
defer func() {
392393
dest.Close()
393394
os.Remove(dest.Name())
394395
}()
395396
if err != nil {
396397
log.Fatalf("unable to create temp file: %s\n", err)
397398
}
398-
}
399-
400-
var buf bytes.Buffer
401-
multiwriter := io.MultiWriter(dest, &buf)
402-
err = tmpl.ExecuteTemplate(multiwriter, filepath.Base(templatePath), &filteredContainers)
403-
if err != nil {
404-
log.Fatalf("template error: %s\n", err)
405-
}
406399

407-
if config.Dest != "" {
400+
dest.Write(contents)
408401

409-
contents := []byte{}
402+
oldContents := []byte{}
410403
if fi, err := os.Stat(config.Dest); err == nil {
411404
if err := dest.Chmod(fi.Mode()); err != nil {
412405
log.Fatalf("unable to chmod temp file: %s\n", err)
413406
}
414407
if err := dest.Chown(int(fi.Sys().(*syscall.Stat_t).Uid), int(fi.Sys().(*syscall.Stat_t).Gid)); err != nil {
415408
log.Fatalf("unable to chown temp file: %s\n", err)
416409
}
417-
contents, err = ioutil.ReadFile(config.Dest)
410+
oldContents, err = ioutil.ReadFile(config.Dest)
418411
if err != nil {
419412
log.Fatalf("unable to compare current file contents: %s: %s\n", config.Dest, err)
420413
}
421414
}
422415

423-
if bytes.Compare(contents, buf.Bytes()) != 0 {
416+
if bytes.Compare(oldContents, contents) != 0 {
424417
err = os.Rename(dest.Name(), config.Dest)
425418
if err != nil {
426419
log.Fatalf("unable to create dest file %s: %s\n", config.Dest, err)
@@ -429,6 +422,22 @@ func generateFile(config Config, containers Context) bool {
429422
return true
430423
}
431424
return false
425+
} else {
426+
os.Stdout.Write(contents)
432427
}
433428
return true
434429
}
430+
431+
func executeTemplate(templatePath string, containers Context) []byte {
432+
tmpl, err := newTemplate(filepath.Base(templatePath)).ParseFiles(templatePath)
433+
if err != nil {
434+
log.Fatalf("unable to parse template: %s", err)
435+
}
436+
437+
buf := new(bytes.Buffer)
438+
err = tmpl.ExecuteTemplate(buf, filepath.Base(templatePath), &containers)
439+
if err != nil {
440+
log.Fatalf("template error: %s\n", err)
441+
}
442+
return buf.Bytes()
443+
}

utils.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package main
22

33
import (
4+
"bufio"
5+
"io"
46
"os"
57
"strings"
8+
"unicode"
69
)
710

811
func getEndpoint() (string, error) {
@@ -50,3 +53,31 @@ func pathExists(path string) (bool, error) {
5053
}
5154
return false, err
5255
}
56+
57+
func isBlank(str string) bool {
58+
for _, r := range str {
59+
if !unicode.IsSpace(r) {
60+
return false
61+
}
62+
}
63+
return true
64+
}
65+
66+
func removeBlankLines(reader io.Reader, writer io.Writer) {
67+
breader := bufio.NewReader(reader)
68+
bwriter := bufio.NewWriter(writer)
69+
70+
for {
71+
line, err := breader.ReadString('\n')
72+
73+
if !isBlank(line) {
74+
bwriter.WriteString(line)
75+
}
76+
77+
if err != nil {
78+
break
79+
}
80+
}
81+
82+
bwriter.Flush()
83+
}

utils_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package main
22

33
import (
4+
"bytes"
45
"flag"
56
"os"
7+
"strings"
68
"testing"
79
)
810

@@ -87,3 +89,58 @@ func TestSplitKeyValueSlice(t *testing.T) {
8789

8890
}
8991
}
92+
93+
func TestIsBlank(t *testing.T) {
94+
tests := []struct {
95+
input string
96+
expected bool
97+
}{
98+
{"", true},
99+
{" ", true},
100+
{" ", true},
101+
{"\t", true},
102+
{"\t\n\v\f\r\u0085\u00A0", true},
103+
{"a", false},
104+
{" a ", false},
105+
{"a ", false},
106+
{" a", false},
107+
{"日本語", false},
108+
}
109+
110+
for _, i := range tests {
111+
v := isBlank(i.input)
112+
if v != i.expected {
113+
t.Fatalf("expected '%v'. got '%v'", i.expected, v)
114+
}
115+
}
116+
}
117+
118+
func TestRemoveBlankLines(t *testing.T) {
119+
tests := []struct {
120+
input string
121+
expected string
122+
}{
123+
{"", ""},
124+
{"\r\n\r\n", ""},
125+
{"line1\nline2", "line1\nline2"},
126+
{"line1\n\nline2", "line1\nline2"},
127+
{"\n\n\n\nline1\n\nline2", "line1\nline2"},
128+
{"\n\n\n\n\n \n \n \n", ""},
129+
130+
// windows line endings \r\n
131+
{"line1\r\nline2", "line1\r\nline2"},
132+
{"line1\r\n\r\nline2", "line1\r\nline2"},
133+
134+
// keep last new line
135+
{"line1\n", "line1\n"},
136+
{"line1\r\n", "line1\r\n"},
137+
}
138+
139+
for _, i := range tests {
140+
output := new(bytes.Buffer)
141+
removeBlankLines(strings.NewReader(i.input), output)
142+
if output.String() != i.expected {
143+
t.Fatalf("expected '%v'. got '%v'", i.expected, output)
144+
}
145+
}
146+
}

0 commit comments

Comments
 (0)