Skip to content

Commit ddc6f3c

Browse files
committed
[docker-up] Add tests for parsing of DOCKERD_ARGS
Tool: gitpod/catfood.gitpod.cloud
1 parent 1858576 commit ddc6f3c

File tree

3 files changed

+209
-95
lines changed

3 files changed

+209
-95
lines changed

components/docker-up/docker-up/main.go

+3-95
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@ package main
99

1010
import (
1111
"archive/tar"
12-
"bufio"
1312
"bytes"
1413
"compress/gzip"
1514
"context"
1615
"embed"
1716
"encoding/base64"
18-
"encoding/json"
1917
"fmt"
2018
"io"
2119
"os"
@@ -27,6 +25,7 @@ import (
2725
"syscall"
2826
"time"
2927

28+
"github.com/gitpod-io/gitpod/docker-up/dockerd"
3029
"github.com/rootless-containers/rootlesskit/pkg/sigproxy"
3130
sigproxysignal "github.com/rootless-containers/rootlesskit/pkg/sigproxy/signal"
3231
"github.com/sirupsen/logrus"
@@ -121,7 +120,8 @@ func runWithinNetns() (err error) {
121120
)
122121
}
123122

124-
userArgs, err := userArgs()
123+
userArgsValue, _ := os.LookupEnv(DaemonArgs)
124+
userArgs, err := dockerd.ParseUserArgs(log, userArgsValue)
125125
if err != nil {
126126
return xerrors.Errorf("cannot add user supplied docker args: %w", err)
127127
}
@@ -270,98 +270,6 @@ func tryAuthenticateForAllHosts(imageAuth string) {
270270
}
271271
}
272272

273-
type ConvertUserArg func(arg, value string) ([]string, error)
274-
275-
var allowedDockerArgs = map[string]ConvertUserArg{
276-
"remap-user": convertRemapUser,
277-
}
278-
279-
func userArgs() ([]string, error) {
280-
userArgs, exists := os.LookupEnv(DaemonArgs)
281-
args := []string{}
282-
if !exists {
283-
return args, nil
284-
}
285-
286-
var providedDockerArgs map[string]string
287-
if err := json.Unmarshal([]byte(userArgs), &providedDockerArgs); err != nil {
288-
return nil, xerrors.Errorf("unable to deserialize docker args: %w", err)
289-
}
290-
291-
for userArg, userValue := range providedDockerArgs {
292-
converter, exists := allowedDockerArgs[userArg]
293-
if !exists {
294-
continue
295-
}
296-
297-
if converter != nil {
298-
cargs, err := converter(userArg, userValue)
299-
if err != nil {
300-
return nil, xerrors.Errorf("could not convert %v - %v: %w", userArg, userValue, err)
301-
}
302-
args = append(args, cargs...)
303-
304-
} else {
305-
args = append(args, "--"+userArg, userValue)
306-
}
307-
}
308-
309-
return args, nil
310-
}
311-
312-
func convertRemapUser(arg, value string) ([]string, error) {
313-
id, err := strconv.Atoi(value)
314-
if err != nil {
315-
return nil, err
316-
}
317-
318-
for _, f := range []string{"/etc/subuid", "/etc/subgid"} {
319-
err := adaptSubid(f, id)
320-
if err != nil {
321-
return nil, xerrors.Errorf("could not adapt subid files: %w", err)
322-
}
323-
}
324-
325-
return []string{"--userns-remap", "gitpod"}, nil
326-
}
327-
328-
func adaptSubid(oldfile string, id int) error {
329-
uid, err := os.Open(oldfile)
330-
if err != nil {
331-
return err
332-
}
333-
334-
newfile, err := os.Create(oldfile + ".new")
335-
if err != nil {
336-
return err
337-
}
338-
339-
mappingFmt := func(username string, id int, size int) string { return fmt.Sprintf("%s:%d:%d\n", username, id, size) }
340-
341-
if id != 0 {
342-
newfile.WriteString(mappingFmt("gitpod", 1, id))
343-
newfile.WriteString(mappingFmt("gitpod", gitpodUserId, 1))
344-
} else {
345-
newfile.WriteString(mappingFmt("gitpod", gitpodUserId, 1))
346-
newfile.WriteString(mappingFmt("gitpod", 1, gitpodUserId-1))
347-
newfile.WriteString(mappingFmt("gitpod", gitpodUserId+1, 32200)) // map rest of user ids in the user namespace
348-
}
349-
350-
uidScanner := bufio.NewScanner(uid)
351-
for uidScanner.Scan() {
352-
l := uidScanner.Text()
353-
if !strings.HasPrefix(l, "gitpod") {
354-
newfile.WriteString(l + "\n")
355-
}
356-
}
357-
358-
if err = os.Rename(newfile.Name(), oldfile); err != nil {
359-
return err
360-
}
361-
362-
return nil
363-
}
364-
365273
var prerequisites = map[string]func() error{
366274
"dockerd": installDocker,
367275
"docker-compose": installDockerCompose,

components/docker-up/dockerd/args.go

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package dockerd
2+
3+
import (
4+
"bufio"
5+
"encoding/json"
6+
"fmt"
7+
"os"
8+
"strconv"
9+
"strings"
10+
11+
"github.com/sirupsen/logrus"
12+
"golang.org/x/xerrors"
13+
)
14+
15+
const (
16+
gitpodUserId = 33333
17+
)
18+
19+
type ConvertUserArg func(arg string, value interface{}) ([]string, error)
20+
21+
var allowedDockerArgs = map[string]ConvertUserArg{
22+
"remap-user": convertRemapUser,
23+
// TODO(gpl): Why this allow-list instead of a converter lookup only?
24+
"proxies": nil,
25+
"http-proxy": nil,
26+
"https-proxy": nil,
27+
}
28+
29+
func ParseUserArgs(log *logrus.Entry, userArgs string) ([]string, error) {
30+
if userArgs == "" {
31+
return nil, nil
32+
}
33+
34+
var providedDockerArgs map[string]interface{}
35+
if err := json.Unmarshal([]byte(userArgs), &providedDockerArgs); err != nil {
36+
return nil, xerrors.Errorf("unable to deserialize docker args: %w", err)
37+
}
38+
39+
return mapUserArgs(log, providedDockerArgs)
40+
}
41+
42+
func mapUserArgs(log *logrus.Entry, jsonObj map[string]interface{}) ([]string, error) {
43+
args := []string{}
44+
for userArg, userValue := range jsonObj {
45+
converter, exists := allowedDockerArgs[userArg]
46+
if !exists {
47+
// TODO(gpl): Why this allow-list instead of a converter lookup only?
48+
continue
49+
}
50+
51+
if converter != nil {
52+
cargs, err := converter(userArg, userValue)
53+
if err != nil {
54+
return nil, xerrors.Errorf("could not convert %v - %v: %w", userArg, userValue, err)
55+
}
56+
args = append(args, cargs...)
57+
continue
58+
}
59+
60+
strValue, ok := (userValue).(string)
61+
if ok {
62+
args = append(args, fmt.Sprintf("--%s=%s", userArg, strValue))
63+
continue
64+
}
65+
66+
bValue, ok := (userValue).(bool)
67+
if ok {
68+
args = append(args, fmt.Sprintf("--%s=%t", userArg, bValue))
69+
continue
70+
}
71+
72+
obj, ok := (userValue).(map[string]interface{})
73+
if ok {
74+
nestedArgs, err := mapUserArgs(log, obj)
75+
if err != nil {
76+
return nil, xerrors.Errorf("could not convert nested arg %v - %v: %w", userArg, userValue, err)
77+
}
78+
args = append(args, nestedArgs...)
79+
continue
80+
}
81+
82+
log.WithField("arg", userArg).WithField("value", userValue).Warn("could not map userArg to dockerd argument, skipping.")
83+
}
84+
85+
return args, nil
86+
}
87+
88+
func convertRemapUser(arg string, value interface{}) ([]string, error) {
89+
v, ok := (value).(string)
90+
if !ok {
91+
return nil, xerrors.Errorf("userns-remap expects a string argument")
92+
}
93+
94+
id, err := strconv.Atoi(v)
95+
if err != nil {
96+
return nil, err
97+
}
98+
99+
for _, f := range []string{"/etc/subuid", "/etc/subgid"} {
100+
err := adaptSubid(f, id)
101+
if err != nil {
102+
return nil, xerrors.Errorf("could not adapt subid files: %w", err)
103+
}
104+
}
105+
106+
return []string{"--userns-remap", "gitpod"}, nil
107+
}
108+
109+
func adaptSubid(oldfile string, id int) error {
110+
uid, err := os.Open(oldfile)
111+
if err != nil {
112+
return err
113+
}
114+
115+
newfile, err := os.Create(oldfile + ".new")
116+
if err != nil {
117+
return err
118+
}
119+
120+
mappingFmt := func(username string, id int, size int) string { return fmt.Sprintf("%s:%d:%d\n", username, id, size) }
121+
122+
if id != 0 {
123+
newfile.WriteString(mappingFmt("gitpod", 1, id))
124+
newfile.WriteString(mappingFmt("gitpod", gitpodUserId, 1))
125+
} else {
126+
newfile.WriteString(mappingFmt("gitpod", gitpodUserId, 1))
127+
newfile.WriteString(mappingFmt("gitpod", 1, gitpodUserId-1))
128+
newfile.WriteString(mappingFmt("gitpod", gitpodUserId+1, 32200)) // map rest of user ids in the user namespace
129+
}
130+
131+
uidScanner := bufio.NewScanner(uid)
132+
for uidScanner.Scan() {
133+
l := uidScanner.Text()
134+
if !strings.HasPrefix(l, "gitpod") {
135+
newfile.WriteString(l + "\n")
136+
}
137+
}
138+
139+
if err = os.Rename(newfile.Name(), oldfile); err != nil {
140+
return err
141+
}
142+
143+
return nil
144+
}
+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) 2020 Gitpod GmbH. All rights reserved.
2+
// Licensed under the GNU Affero General Public License (AGPL).
3+
// See License.AGPL.txt in the project root for license information.
4+
5+
package dockerd
6+
7+
import (
8+
"reflect"
9+
"sort"
10+
"testing"
11+
12+
"github.com/sirupsen/logrus"
13+
)
14+
15+
func TestMapUserArgs(t *testing.T) {
16+
tests := []struct {
17+
name string
18+
input map[string]interface{}
19+
expected []string
20+
wantErr bool
21+
}{
22+
{
23+
name: "empty input",
24+
input: map[string]interface{}{},
25+
expected: []string{},
26+
wantErr: false,
27+
},
28+
{
29+
name: "tls and proxy settings",
30+
input: map[string]interface{}{
31+
"proxies": map[string]interface{}{
32+
"http-proxy": "localhost:38080",
33+
"https-proxy": "localhost:38081",
34+
},
35+
},
36+
expected: []string{
37+
"--http-proxy=localhost:38080",
38+
"--https-proxy=localhost:38081",
39+
},
40+
wantErr: false,
41+
},
42+
}
43+
44+
for _, tt := range tests {
45+
t.Run(tt.name, func(t *testing.T) {
46+
log := logrus.New().WithField("test", t.Name())
47+
got, err := mapUserArgs(log, tt.input)
48+
if (err != nil) != tt.wantErr {
49+
t.Errorf("mapUserArgs() error = %v, wantErr %v", err, tt.wantErr)
50+
return
51+
}
52+
if !tt.wantErr {
53+
sort.Strings(got)
54+
sort.Strings(tt.expected)
55+
// Sort both slices to ensure consistent comparison
56+
if !reflect.DeepEqual(got, tt.expected) {
57+
t.Errorf("mapUserArgs() = %v, want %v", got, tt.expected)
58+
}
59+
}
60+
})
61+
}
62+
}

0 commit comments

Comments
 (0)