Skip to content

Commit 60af6e5

Browse files
committed
[docker-up] Minor fixes and add tests
Tool: gitpod/catfood.gitpod.cloud
1 parent ba2f367 commit 60af6e5

File tree

6 files changed

+252
-130
lines changed

6 files changed

+252
-130
lines changed

components/docker-up/BUILD.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ packages:
55
- go.mod
66
- go.sum
77
- "docker-up/**"
8+
- "dockerd/**"
89
- dependencies.sh
910
deps:
1011
- components/common-go:lib

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

Lines changed: 10 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,9 @@ package main
99

1010
import (
1111
"archive/tar"
12-
"bufio"
1312
"compress/gzip"
1413
"context"
1514
"embed"
16-
"encoding/json"
1715
"fmt"
1816
"io"
1917
"os"
@@ -25,6 +23,7 @@ import (
2523
"syscall"
2624
"time"
2725

26+
"github.com/gitpod-io/gitpod/docker-up/dockerd"
2827
"github.com/rootless-containers/rootlesskit/pkg/sigproxy"
2928
sigproxysignal "github.com/rootless-containers/rootlesskit/pkg/sigproxy/signal"
3029
"github.com/sirupsen/logrus"
@@ -45,6 +44,7 @@ var opts struct {
4544
UserAccessibleSocket bool
4645
Verbose bool
4746
DontWrapNetNS bool
47+
AutoLogin bool
4848
}
4949

5050
//go:embed docker.tgz
@@ -58,6 +58,7 @@ var aptUpdated = false
5858
const (
5959
dockerSocketFN = "/var/run/docker.sock"
6060
gitpodUserId = 33333
61+
gitpodGroupId = 33333
6162
containerIf = "eth0"
6263
)
6364

@@ -73,6 +74,7 @@ func main() {
7374
pflag.BoolVar(&opts.AutoInstall, "auto-install", true, "auto-install prerequisites (docker)")
7475
pflag.BoolVar(&opts.UserAccessibleSocket, "user-accessible-socket", true, "chmod the Docker socket to make it user accessible")
7576
pflag.BoolVar(&opts.DontWrapNetNS, "dont-wrap-netns", os.Getenv("WORKSPACEKIT_WRAP_NETNS") == "true", "wrap the Docker daemon in a network namespace")
77+
pflag.BoolVar(&opts.AutoLogin, "auto-login", false, "use content of GITPOD_IMAGE_AUTH to automatically login with the docker daemon")
7678
pflag.Parse()
7779

7880
logger := logrus.New()
@@ -118,7 +120,8 @@ func runWithinNetns() (err error) {
118120
)
119121
}
120122

121-
userArgs, err := userArgs()
123+
userArgsValue, _ := os.LookupEnv(DaemonArgs)
124+
userArgs, err := dockerd.ParseUserArgs(log, userArgsValue)
122125
if err != nil {
123126
return xerrors.Errorf("cannot add user supplied docker args: %w", err)
124127
}
@@ -192,98 +195,6 @@ func runWithinNetns() (err error) {
192195
return nil
193196
}
194197

195-
type ConvertUserArg func(arg, value string) ([]string, error)
196-
197-
var allowedDockerArgs = map[string]ConvertUserArg{
198-
"remap-user": convertRemapUser,
199-
}
200-
201-
func userArgs() ([]string, error) {
202-
userArgs, exists := os.LookupEnv(DaemonArgs)
203-
args := []string{}
204-
if !exists {
205-
return args, nil
206-
}
207-
208-
var providedDockerArgs map[string]string
209-
if err := json.Unmarshal([]byte(userArgs), &providedDockerArgs); err != nil {
210-
return nil, xerrors.Errorf("unable to deserialize docker args: %w", err)
211-
}
212-
213-
for userArg, userValue := range providedDockerArgs {
214-
converter, exists := allowedDockerArgs[userArg]
215-
if !exists {
216-
continue
217-
}
218-
219-
if converter != nil {
220-
cargs, err := converter(userArg, userValue)
221-
if err != nil {
222-
return nil, xerrors.Errorf("could not convert %v - %v: %w", userArg, userValue, err)
223-
}
224-
args = append(args, cargs...)
225-
226-
} else {
227-
args = append(args, "--"+userArg, userValue)
228-
}
229-
}
230-
231-
return args, nil
232-
}
233-
234-
func convertRemapUser(arg, value string) ([]string, error) {
235-
id, err := strconv.Atoi(value)
236-
if err != nil {
237-
return nil, err
238-
}
239-
240-
for _, f := range []string{"/etc/subuid", "/etc/subgid"} {
241-
err := adaptSubid(f, id)
242-
if err != nil {
243-
return nil, xerrors.Errorf("could not adapt subid files: %w", err)
244-
}
245-
}
246-
247-
return []string{"--userns-remap", "gitpod"}, nil
248-
}
249-
250-
func adaptSubid(oldfile string, id int) error {
251-
uid, err := os.Open(oldfile)
252-
if err != nil {
253-
return err
254-
}
255-
256-
newfile, err := os.Create(oldfile + ".new")
257-
if err != nil {
258-
return err
259-
}
260-
261-
mappingFmt := func(username string, id int, size int) string { return fmt.Sprintf("%s:%d:%d\n", username, id, size) }
262-
263-
if id != 0 {
264-
newfile.WriteString(mappingFmt("gitpod", 1, id))
265-
newfile.WriteString(mappingFmt("gitpod", gitpodUserId, 1))
266-
} else {
267-
newfile.WriteString(mappingFmt("gitpod", gitpodUserId, 1))
268-
newfile.WriteString(mappingFmt("gitpod", 1, gitpodUserId-1))
269-
newfile.WriteString(mappingFmt("gitpod", gitpodUserId+1, 32200)) // map rest of user ids in the user namespace
270-
}
271-
272-
uidScanner := bufio.NewScanner(uid)
273-
for uidScanner.Scan() {
274-
l := uidScanner.Text()
275-
if !strings.HasPrefix(l, "gitpod") {
276-
newfile.WriteString(l + "\n")
277-
}
278-
}
279-
280-
if err = os.Rename(newfile.Name(), oldfile); err != nil {
281-
return err
282-
}
283-
284-
return nil
285-
}
286-
287198
var prerequisites = map[string]func() error{
288199
"dockerd": installDocker,
289200
"docker-compose": installDockerCompose,
@@ -353,7 +264,8 @@ func installDocker() error {
353264
}
354265

355266
switch hdr.Typeflag {
356-
case tar.TypeReg, tar.TypeRegA:
267+
268+
case tar.TypeReg, tar.TypeRegA: //lint:ignore SA1019 backwards compatibility
357269
file, err := os.OpenFile(dstpath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, mode)
358270
if err != nil {
359271
return xerrors.Errorf("unable to create file: %v", err)
@@ -480,12 +392,12 @@ func detectRuncVersion(output string) (major, minor int, err error) {
480392

481393
major, err = strconv.Atoi(n[0])
482394
if err != nil {
483-
return 0, 0, xerrors.Errorf("could not parse major %s: %w", n[0])
395+
return 0, 0, xerrors.Errorf("could not parse major %s: %w", n[0], err)
484396
}
485397

486398
minor, err = strconv.Atoi(n[1])
487399
if err != nil {
488-
return 0, 0, xerrors.Errorf("could not parse minor %s: %w", n[1])
400+
return 0, 0, xerrors.Errorf("could not parse minor %s: %w", n[1], err)
489401
}
490402

491403
return major, minor, nil

components/docker-up/dockerd/args.go

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