Browse code

builder-next: make stub executor generic

The current executor is only tested on Linux, so let's be honest about
that. Stubbing this correctly helps avoid incorrectly trying to call
into Linux-only code in e.g. libnetwork.

Signed-off-by: Bjorn Neergaard <bjorn.neergaard@docker.com>

Bjorn Neergaard authored on 2023/07/14 07:48:01
Showing 4 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,163 @@
0
+package buildkit
1
+
2
+import (
3
+	"context"
4
+	"os"
5
+	"path/filepath"
6
+	"strconv"
7
+	"sync"
8
+
9
+	"github.com/containerd/containerd/log"
10
+	"github.com/docker/docker/daemon/config"
11
+	"github.com/docker/docker/libnetwork"
12
+	"github.com/docker/docker/pkg/idtools"
13
+	"github.com/docker/docker/pkg/stringid"
14
+	"github.com/moby/buildkit/executor"
15
+	"github.com/moby/buildkit/executor/oci"
16
+	"github.com/moby/buildkit/executor/runcexecutor"
17
+	"github.com/moby/buildkit/identity"
18
+	"github.com/moby/buildkit/solver/pb"
19
+	"github.com/moby/buildkit/util/network"
20
+	specs "github.com/opencontainers/runtime-spec/specs-go"
21
+)
22
+
23
+const networkName = "bridge"
24
+
25
+func newExecutor(root, cgroupParent string, net *libnetwork.Controller, dnsConfig *oci.DNSConfig, rootless bool, idmap idtools.IdentityMapping, apparmorProfile string) (executor.Executor, error) {
26
+	netRoot := filepath.Join(root, "net")
27
+	networkProviders := map[pb.NetMode]network.Provider{
28
+		pb.NetMode_UNSET: &bridgeProvider{Controller: net, Root: netRoot},
29
+		pb.NetMode_HOST:  network.NewHostProvider(),
30
+		pb.NetMode_NONE:  network.NewNoneProvider(),
31
+	}
32
+
33
+	// make sure net state directory is cleared from previous state
34
+	fis, err := os.ReadDir(netRoot)
35
+	if err == nil {
36
+		for _, fi := range fis {
37
+			fp := filepath.Join(netRoot, fi.Name())
38
+			if err := os.RemoveAll(fp); err != nil {
39
+				log.G(context.TODO()).WithError(err).Errorf("failed to delete old network state: %v", fp)
40
+			}
41
+		}
42
+	}
43
+
44
+	// Returning a non-nil but empty *IdentityMapping breaks BuildKit:
45
+	// https://github.com/moby/moby/pull/39444
46
+	pidmap := &idmap
47
+	if idmap.Empty() {
48
+		pidmap = nil
49
+	}
50
+
51
+	return runcexecutor.New(runcexecutor.Opt{
52
+		Root:                filepath.Join(root, "executor"),
53
+		CommandCandidates:   []string{"runc"},
54
+		DefaultCgroupParent: cgroupParent,
55
+		Rootless:            rootless,
56
+		NoPivot:             os.Getenv("DOCKER_RAMDISK") != "",
57
+		IdentityMapping:     pidmap,
58
+		DNS:                 dnsConfig,
59
+		ApparmorProfile:     apparmorProfile,
60
+	}, networkProviders)
61
+}
62
+
63
+type bridgeProvider struct {
64
+	*libnetwork.Controller
65
+	Root string
66
+}
67
+
68
+func (p *bridgeProvider) New(ctx context.Context, hostname string) (network.Namespace, error) {
69
+	n, err := p.NetworkByName(networkName)
70
+	if err != nil {
71
+		return nil, err
72
+	}
73
+
74
+	iface := &lnInterface{ready: make(chan struct{}), provider: p}
75
+	iface.Once.Do(func() {
76
+		go iface.init(p.Controller, n)
77
+	})
78
+
79
+	return iface, nil
80
+}
81
+
82
+func (p *bridgeProvider) Close() error {
83
+	return nil
84
+}
85
+
86
+type lnInterface struct {
87
+	ep  *libnetwork.Endpoint
88
+	sbx *libnetwork.Sandbox
89
+	sync.Once
90
+	err      error
91
+	ready    chan struct{}
92
+	provider *bridgeProvider
93
+}
94
+
95
+func (iface *lnInterface) init(c *libnetwork.Controller, n *libnetwork.Network) {
96
+	defer close(iface.ready)
97
+	id := identity.NewID()
98
+
99
+	ep, err := n.CreateEndpoint(id, libnetwork.CreateOptionDisableResolution())
100
+	if err != nil {
101
+		iface.err = err
102
+		return
103
+	}
104
+
105
+	sbx, err := c.NewSandbox(id, libnetwork.OptionUseExternalKey(), libnetwork.OptionHostsPath(filepath.Join(iface.provider.Root, id, "hosts")),
106
+		libnetwork.OptionResolvConfPath(filepath.Join(iface.provider.Root, id, "resolv.conf")))
107
+	if err != nil {
108
+		iface.err = err
109
+		return
110
+	}
111
+
112
+	if err := ep.Join(sbx); err != nil {
113
+		iface.err = err
114
+		return
115
+	}
116
+
117
+	iface.sbx = sbx
118
+	iface.ep = ep
119
+}
120
+
121
+func (iface *lnInterface) Set(s *specs.Spec) error {
122
+	<-iface.ready
123
+	if iface.err != nil {
124
+		log.G(context.TODO()).WithError(iface.err).Error("failed to set networking spec")
125
+		return iface.err
126
+	}
127
+	shortNetCtlrID := stringid.TruncateID(iface.provider.Controller.ID())
128
+	// attach netns to bridge within the container namespace, using reexec in a prestart hook
129
+	s.Hooks = &specs.Hooks{
130
+		Prestart: []specs.Hook{{
131
+			Path: filepath.Join("/proc", strconv.Itoa(os.Getpid()), "exe"),
132
+			Args: []string{"libnetwork-setkey", "-exec-root=" + iface.provider.Config().ExecRoot, iface.sbx.ContainerID(), shortNetCtlrID},
133
+		}},
134
+	}
135
+	return nil
136
+}
137
+
138
+func (iface *lnInterface) Close() error {
139
+	<-iface.ready
140
+	if iface.sbx != nil {
141
+		go func() {
142
+			if err := iface.sbx.Delete(); err != nil {
143
+				log.G(context.TODO()).WithError(err).Errorf("failed to delete builder network sandbox")
144
+			}
145
+			if err := os.RemoveAll(filepath.Join(iface.provider.Root, iface.sbx.ContainerID())); err != nil {
146
+				log.G(context.TODO()).WithError(err).Errorf("failed to delete builder sandbox directory")
147
+			}
148
+		}()
149
+	}
150
+	return iface.err
151
+}
152
+
153
+func getDNSConfig(cfg config.DNSConfig) *oci.DNSConfig {
154
+	if cfg.DNS != nil || cfg.DNSSearch != nil || cfg.DNSOptions != nil {
155
+		return &oci.DNSConfig{
156
+			Nameservers:   cfg.DNS,
157
+			SearchDomains: cfg.DNSSearch,
158
+			Options:       cfg.DNSOptions,
159
+		}
160
+	}
161
+	return nil
162
+}
0 163
new file mode 100644
... ...
@@ -0,0 +1,33 @@
0
+//go:build !linux
1
+
2
+package buildkit
3
+
4
+import (
5
+	"context"
6
+	"errors"
7
+	"runtime"
8
+
9
+	"github.com/docker/docker/daemon/config"
10
+	"github.com/docker/docker/libnetwork"
11
+	"github.com/docker/docker/pkg/idtools"
12
+	"github.com/moby/buildkit/executor"
13
+	"github.com/moby/buildkit/executor/oci"
14
+)
15
+
16
+func newExecutor(_, _ string, _ *libnetwork.Controller, _ *oci.DNSConfig, _ bool, _ idtools.IdentityMapping, _ string) (executor.Executor, error) {
17
+	return &stubExecutor{}, nil
18
+}
19
+
20
+type stubExecutor struct{}
21
+
22
+func (w *stubExecutor) Run(ctx context.Context, id string, root executor.Mount, mounts []executor.Mount, process executor.ProcessInfo, started chan<- struct{}) (err error) {
23
+	return errors.New("buildkit executor not implemented for "+runtime.GOOS)
24
+}
25
+
26
+func (w *stubExecutor) Exec(ctx context.Context, id string, process executor.ProcessInfo) error {
27
+	return errors.New("buildkit executor not implemented for "+runtime.GOOS)
28
+}
29
+
30
+func getDNSConfig(config.DNSConfig) *oci.DNSConfig {
31
+	return nil
32
+}
0 33
deleted file mode 100644
... ...
@@ -1,165 +0,0 @@
1
-//go:build !windows
2
-
3
-package buildkit
4
-
5
-import (
6
-	"context"
7
-	"os"
8
-	"path/filepath"
9
-	"strconv"
10
-	"sync"
11
-
12
-	"github.com/containerd/containerd/log"
13
-	"github.com/docker/docker/daemon/config"
14
-	"github.com/docker/docker/libnetwork"
15
-	"github.com/docker/docker/pkg/idtools"
16
-	"github.com/docker/docker/pkg/stringid"
17
-	"github.com/moby/buildkit/executor"
18
-	"github.com/moby/buildkit/executor/oci"
19
-	"github.com/moby/buildkit/executor/runcexecutor"
20
-	"github.com/moby/buildkit/identity"
21
-	"github.com/moby/buildkit/solver/pb"
22
-	"github.com/moby/buildkit/util/network"
23
-	specs "github.com/opencontainers/runtime-spec/specs-go"
24
-)
25
-
26
-const networkName = "bridge"
27
-
28
-func newExecutor(root, cgroupParent string, net *libnetwork.Controller, dnsConfig *oci.DNSConfig, rootless bool, idmap idtools.IdentityMapping, apparmorProfile string) (executor.Executor, error) {
29
-	netRoot := filepath.Join(root, "net")
30
-	networkProviders := map[pb.NetMode]network.Provider{
31
-		pb.NetMode_UNSET: &bridgeProvider{Controller: net, Root: netRoot},
32
-		pb.NetMode_HOST:  network.NewHostProvider(),
33
-		pb.NetMode_NONE:  network.NewNoneProvider(),
34
-	}
35
-
36
-	// make sure net state directory is cleared from previous state
37
-	fis, err := os.ReadDir(netRoot)
38
-	if err == nil {
39
-		for _, fi := range fis {
40
-			fp := filepath.Join(netRoot, fi.Name())
41
-			if err := os.RemoveAll(fp); err != nil {
42
-				log.G(context.TODO()).WithError(err).Errorf("failed to delete old network state: %v", fp)
43
-			}
44
-		}
45
-	}
46
-
47
-	// Returning a non-nil but empty *IdentityMapping breaks BuildKit:
48
-	// https://github.com/moby/moby/pull/39444
49
-	pidmap := &idmap
50
-	if idmap.Empty() {
51
-		pidmap = nil
52
-	}
53
-
54
-	return runcexecutor.New(runcexecutor.Opt{
55
-		Root:                filepath.Join(root, "executor"),
56
-		CommandCandidates:   []string{"runc"},
57
-		DefaultCgroupParent: cgroupParent,
58
-		Rootless:            rootless,
59
-		NoPivot:             os.Getenv("DOCKER_RAMDISK") != "",
60
-		IdentityMapping:     pidmap,
61
-		DNS:                 dnsConfig,
62
-		ApparmorProfile:     apparmorProfile,
63
-	}, networkProviders)
64
-}
65
-
66
-type bridgeProvider struct {
67
-	*libnetwork.Controller
68
-	Root string
69
-}
70
-
71
-func (p *bridgeProvider) New(ctx context.Context, hostname string) (network.Namespace, error) {
72
-	n, err := p.NetworkByName(networkName)
73
-	if err != nil {
74
-		return nil, err
75
-	}
76
-
77
-	iface := &lnInterface{ready: make(chan struct{}), provider: p}
78
-	iface.Once.Do(func() {
79
-		go iface.init(p.Controller, n)
80
-	})
81
-
82
-	return iface, nil
83
-}
84
-
85
-func (p *bridgeProvider) Close() error {
86
-	return nil
87
-}
88
-
89
-type lnInterface struct {
90
-	ep  *libnetwork.Endpoint
91
-	sbx *libnetwork.Sandbox
92
-	sync.Once
93
-	err      error
94
-	ready    chan struct{}
95
-	provider *bridgeProvider
96
-}
97
-
98
-func (iface *lnInterface) init(c *libnetwork.Controller, n *libnetwork.Network) {
99
-	defer close(iface.ready)
100
-	id := identity.NewID()
101
-
102
-	ep, err := n.CreateEndpoint(id, libnetwork.CreateOptionDisableResolution())
103
-	if err != nil {
104
-		iface.err = err
105
-		return
106
-	}
107
-
108
-	sbx, err := c.NewSandbox(id, libnetwork.OptionUseExternalKey(), libnetwork.OptionHostsPath(filepath.Join(iface.provider.Root, id, "hosts")),
109
-		libnetwork.OptionResolvConfPath(filepath.Join(iface.provider.Root, id, "resolv.conf")))
110
-	if err != nil {
111
-		iface.err = err
112
-		return
113
-	}
114
-
115
-	if err := ep.Join(sbx); err != nil {
116
-		iface.err = err
117
-		return
118
-	}
119
-
120
-	iface.sbx = sbx
121
-	iface.ep = ep
122
-}
123
-
124
-func (iface *lnInterface) Set(s *specs.Spec) error {
125
-	<-iface.ready
126
-	if iface.err != nil {
127
-		log.G(context.TODO()).WithError(iface.err).Error("failed to set networking spec")
128
-		return iface.err
129
-	}
130
-	shortNetCtlrID := stringid.TruncateID(iface.provider.Controller.ID())
131
-	// attach netns to bridge within the container namespace, using reexec in a prestart hook
132
-	s.Hooks = &specs.Hooks{
133
-		Prestart: []specs.Hook{{
134
-			Path: filepath.Join("/proc", strconv.Itoa(os.Getpid()), "exe"),
135
-			Args: []string{"libnetwork-setkey", "-exec-root=" + iface.provider.Config().ExecRoot, iface.sbx.ContainerID(), shortNetCtlrID},
136
-		}},
137
-	}
138
-	return nil
139
-}
140
-
141
-func (iface *lnInterface) Close() error {
142
-	<-iface.ready
143
-	if iface.sbx != nil {
144
-		go func() {
145
-			if err := iface.sbx.Delete(); err != nil {
146
-				log.G(context.TODO()).WithError(err).Errorf("failed to delete builder network sandbox")
147
-			}
148
-			if err := os.RemoveAll(filepath.Join(iface.provider.Root, iface.sbx.ContainerID())); err != nil {
149
-				log.G(context.TODO()).WithError(err).Errorf("failed to delete builder sandbox directory")
150
-			}
151
-		}()
152
-	}
153
-	return iface.err
154
-}
155
-
156
-func getDNSConfig(cfg config.DNSConfig) *oci.DNSConfig {
157
-	if cfg.DNS != nil || cfg.DNSSearch != nil || cfg.DNSOptions != nil {
158
-		return &oci.DNSConfig{
159
-			Nameservers:   cfg.DNS,
160
-			SearchDomains: cfg.DNSSearch,
161
-			Options:       cfg.DNSOptions,
162
-		}
163
-	}
164
-	return nil
165
-}
166 1
deleted file mode 100644
... ...
@@ -1,30 +0,0 @@
1
-package buildkit
2
-
3
-import (
4
-	"context"
5
-	"errors"
6
-
7
-	"github.com/docker/docker/daemon/config"
8
-	"github.com/docker/docker/libnetwork"
9
-	"github.com/docker/docker/pkg/idtools"
10
-	"github.com/moby/buildkit/executor"
11
-	"github.com/moby/buildkit/executor/oci"
12
-)
13
-
14
-func newExecutor(_, _ string, _ *libnetwork.Controller, _ *oci.DNSConfig, _ bool, _ idtools.IdentityMapping, _ string) (executor.Executor, error) {
15
-	return &winExecutor{}, nil
16
-}
17
-
18
-type winExecutor struct{}
19
-
20
-func (w *winExecutor) Run(ctx context.Context, id string, root executor.Mount, mounts []executor.Mount, process executor.ProcessInfo, started chan<- struct{}) (err error) {
21
-	return errors.New("buildkit executor not implemented for windows")
22
-}
23
-
24
-func (w *winExecutor) Exec(ctx context.Context, id string, process executor.ProcessInfo) error {
25
-	return errors.New("buildkit executor not implemented for windows")
26
-}
27
-
28
-func getDNSConfig(config.DNSConfig) *oci.DNSConfig {
29
-	return nil
30
-}