Skip to content

Commit 94ce57e

Browse files
committed
Escape systemd special chars in —docker-env
1 parent a30092a commit 94ce57e

File tree

4 files changed

+62
-8
lines changed

4 files changed

+62
-8
lines changed

Diff for: pkg/provision/buildroot.go

+15
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"fmt"
2222
"path"
2323
"path/filepath"
24+
"strings"
2425
"text/template"
2526
"time"
2627

@@ -47,6 +48,9 @@ type BuildrootProvisioner struct {
4748
provision.SystemdProvisioner
4849
}
4950

51+
// for escaping systemd template specifiers (e.g. '%i'), which are not supported by minikube
52+
var systemdSpecifierEscaper = strings.NewReplacer("%", "%%")
53+
5054
func init() {
5155
provision.Register("Buildroot", &provision.RegisteredProvisioner{
5256
New: NewBuildrootProvisioner,
@@ -64,6 +68,15 @@ func (p *BuildrootProvisioner) String() string {
6468
return "buildroot"
6569
}
6670

71+
// escapeSystemdDirectives escapes special characters in the input variables used to create the
72+
// systemd unit file, which would otherwise be interpreted as systemd directives. An example
73+
// are template specifiers (e.g. '%i') which are predefined variables that get evaluated dynamically
74+
// (see systemd man pages for more info). This is not supported by minikube, thus needs to be escaped.
75+
func escapeSystemdDirectives(engineConfigContext provision.EngineConfigContext) {
76+
// escape '%' in Environment option so that it does not evaluate into a template specifier
77+
engineConfigContext.EngineOptions.Env = util.ReplaceChars(engineConfigContext.EngineOptions.Env, systemdSpecifierEscaper)
78+
}
79+
6780
// GenerateDockerOptions generates the *provision.DockerOptions for this provisioner
6881
func (p *BuildrootProvisioner) GenerateDockerOptions(dockerPort int) (*provision.DockerOptions, error) {
6982
var engineCfg bytes.Buffer
@@ -127,6 +140,8 @@ WantedBy=multi-user.target
127140
EngineOptions: p.EngineOptions,
128141
}
129142

143+
escapeSystemdDirectives(engineConfigContext)
144+
130145
if err := t.Execute(&engineCfg, engineConfigContext); err != nil {
131146
return nil, err
132147
}

Diff for: pkg/util/utils.go

+9
Original file line numberDiff line numberDiff line change
@@ -250,3 +250,12 @@ func TeePrefix(prefix string, r io.Reader, w io.Writer, logger func(format strin
250250
}
251251
return nil
252252
}
253+
254+
// ReplaceChars returns a copy of the src slice with each string modified by the replacer
255+
func ReplaceChars(src []string, replacer *strings.Replacer) []string {
256+
ret := make([]string, len(src))
257+
for i, s := range src {
258+
ret[i] = replacer.Replace(s)
259+
}
260+
return ret
261+
}

Diff for: pkg/util/utils_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,22 @@ func TestTeePrefix(t *testing.T) {
197197
t.Errorf("log=%q, want: %q", gotLog, wantLog)
198198
}
199199
}
200+
201+
func TestReplaceChars(t *testing.T) {
202+
testData := []struct {
203+
src []string
204+
replacer *strings.Replacer
205+
expectedRes []string
206+
}{
207+
{[]string{"abc%def", "%Y%"}, strings.NewReplacer("%", "X"), []string{"abcXdef", "XYX"}},
208+
}
209+
210+
for _, tt := range testData {
211+
res := ReplaceChars(tt.src, tt.replacer)
212+
for i, val := range res {
213+
if val != tt.expectedRes[i] {
214+
t.Fatalf("Expected '%s' but got '%s'", tt.expectedRes, res)
215+
}
216+
}
217+
}
218+
}

Diff for: test/integration/start_stop_delete_test.go

+19-8
Original file line numberDiff line numberDiff line change
@@ -19,42 +19,44 @@ limitations under the License.
1919
package integration
2020

2121
import (
22-
"fmt"
2322
"net"
2423
"strings"
2524
"testing"
2625
"time"
2726

2827
"github.com/docker/machine/libmachine/state"
29-
"k8s.io/minikube/pkg/minikube/constants"
3028
"k8s.io/minikube/test/integration/util"
3129
)
3230

3331
func TestStartStop(t *testing.T) {
3432
tests := []struct {
35-
name string
36-
args []string
33+
name string
34+
args []string
35+
assertCustom func(t *testing.T, r *util.MinikubeRunner)
3736
}{
3837
{"nocache_oldest", []string{
3938
"--cache-images=false",
4039
fmt.Sprintf("--kubernetes-version=%s", constants.OldestKubernetesVersion),
41-
}},
40+
}, null},
4241
{"feature_gates_newest_cni", []string{
4342
"--feature-gates",
4443
"ServerSideApply=true",
4544
"--network-plugin=cni",
4645
"--extra-config=kubelet.network-plugin=cni",
4746
fmt.Sprintf("--kubernetes-version=%s", constants.NewestKubernetesVersion),
48-
}},
47+
}, null},
4948
{"containerd", []string{
5049
"--container-runtime=containerd",
5150
"--docker-opt containerd=/var/run/containerd/containerd.sock",
52-
}},
51+
}, null},
5352
{"crio_ignore_preflights", []string{
5453
"--container-runtime=crio",
5554
"--extra-config",
5655
"kubeadm.ignore-preflight-errors=SystemVerification",
57-
}},
56+
}, null},
57+
{"docker_env_special_char", []string{
58+
"--docker-env TEST_VAR=encoded%40space",
59+
}, assertDockerEnv},
5860
}
5961

6062
for _, test := range tests {
@@ -70,6 +72,10 @@ func TestStartStop(t *testing.T) {
7072
r.Start(test.args...)
7173
r.CheckStatus(state.Running.String())
7274

75+
if test.assertCustom != nil {
76+
test.assertCustom(t, &r)
77+
}
78+
7379
ip := r.RunCommand("ip", true)
7480
ip = strings.TrimRight(ip, "\n")
7581
if net.ParseIP(ip) == nil {
@@ -93,3 +99,8 @@ func TestStartStop(t *testing.T) {
9399
})
94100
}
95101
}
102+
103+
func assertDockerEnv(t *testing.T, r *util.MinikubeRunner) {
104+
out, _ := r.SSH("systemctl show --property=Environment docker")
105+
t.Fatalf("========> %s", out)
106+
}

0 commit comments

Comments
 (0)