From e3cd27264d962df502353bc97903a34cda9d6be8 Mon Sep 17 00:00:00 2001 From: Jan Janik <11janci@seznam.cz> Date: Wed, 27 Mar 2019 22:05:20 +1300 Subject: [PATCH] =?UTF-8?q?Escape=20systemd=20special=20chars=20in=20?= =?UTF-8?q?=E2=80=94docker-env?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/provision/buildroot.go | 17 ++++++++++++++++ pkg/util/utils.go | 23 ++++++++++++++++++++++ pkg/util/utils_test.go | 40 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) diff --git a/pkg/provision/buildroot.go b/pkg/provision/buildroot.go index 50c701580d..052fb2fcd9 100644 --- a/pkg/provision/buildroot.go +++ b/pkg/provision/buildroot.go @@ -21,6 +21,7 @@ import ( "fmt" "path" "path/filepath" + "strings" "text/template" "time" @@ -47,6 +48,9 @@ type BuildrootProvisioner struct { provision.SystemdProvisioner } +// for escaping systemd template specifiers (e.g. '%i'), which are not supported by minikube +var systemdSpecifierEscaper = strings.NewReplacer("%", "%%") + func init() { provision.Register("Buildroot", &provision.RegisteredProvisioner{ New: NewBuildrootProvisioner, @@ -64,6 +68,17 @@ func (p *BuildrootProvisioner) String() string { return "buildroot" } +// escapeSystemdDirectives escapes special characters in the input variables used to create the +// systemd unit file, which would otherwise be interpreted as systemd directives. An example +// are template specifiers (e.g. '%i') which are predefined variables that get evaluated dynamically +// (see systemd man pages for more info). This is not supported by minikube, thus needs to be escaped. +func escapeSystemdDirectives(engineConfigContext *provision.EngineConfigContext) { + // escape '%' in Environment option so that it does not evaluate into a template specifier + engineConfigContext.EngineOptions.Env = util.ReplaceChars(engineConfigContext.EngineOptions.Env, systemdSpecifierEscaper) + // input might contain whitespaces, wrap it in quotes + engineConfigContext.EngineOptions.Env = util.ConcatStrings(engineConfigContext.EngineOptions.Env, "\"", "\"") +} + // GenerateDockerOptions generates the *provision.DockerOptions for this provisioner func (p *BuildrootProvisioner) GenerateDockerOptions(dockerPort int) (*provision.DockerOptions, error) { var engineCfg bytes.Buffer @@ -127,6 +142,8 @@ WantedBy=multi-user.target EngineOptions: p.EngineOptions, } + escapeSystemdDirectives(&engineConfigContext) + if err := t.Execute(&engineCfg, engineConfigContext); err != nil { return nil, err } diff --git a/pkg/util/utils.go b/pkg/util/utils.go index 7637e2a588..cf81251219 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -250,3 +250,26 @@ func TeePrefix(prefix string, r io.Reader, w io.Writer, logger func(format strin } return nil } + +// ReplaceChars returns a copy of the src slice with each string modified by the replacer +func ReplaceChars(src []string, replacer *strings.Replacer) []string { + ret := make([]string, len(src)) + for i, s := range src { + ret[i] = replacer.Replace(s) + } + return ret +} + +// ConcatStrings concatenates each string in the src slice with prefix and postfix and returns a new slice +func ConcatStrings(src []string, prefix string, postfix string) []string { + var buf bytes.Buffer + ret := make([]string, len(src)) + for i, s := range src { + buf.WriteString(prefix) + buf.WriteString(s) + buf.WriteString(postfix) + ret[i] = buf.String() + buf.Reset() + } + return ret +} diff --git a/pkg/util/utils_test.go b/pkg/util/utils_test.go index f7d1c7c372..864a540095 100644 --- a/pkg/util/utils_test.go +++ b/pkg/util/utils_test.go @@ -197,3 +197,43 @@ func TestTeePrefix(t *testing.T) { t.Errorf("log=%q, want: %q", gotLog, wantLog) } } + +func TestReplaceChars(t *testing.T) { + testData := []struct { + src []string + replacer *strings.Replacer + expectedRes []string + }{ + {[]string{"abc%def", "%Y%"}, strings.NewReplacer("%", "X"), []string{"abcXdef", "XYX"}}, + } + + for _, tt := range testData { + res := ReplaceChars(tt.src, tt.replacer) + for i, val := range res { + if val != tt.expectedRes[i] { + t.Fatalf("Expected '%s' but got '%s'", tt.expectedRes, res) + } + } + } +} + +func TestConcatStrings(t *testing.T) { + testData := []struct { + src []string + prefix string + postfix string + expectedRes []string + }{ + {[]string{"abc", ""}, "xx", "yy", []string{"xxabcyy", "xxyy"}}, + {[]string{"abc", ""}, "", "", []string{"abc", ""}}, + } + + for _, tt := range testData { + res := ConcatStrings(tt.src, tt.prefix, tt.postfix) + for i, val := range res { + if val != tt.expectedRes[i] { + t.Fatalf("Expected '%s' but got '%s'", tt.expectedRes, res) + } + } + } +}