Browse code

Add shim config for custom runtimes for plugins

This fixes a panic when an admin specifies a custom default runtime,
when a plugin is started the shim config is nil.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
(cherry picked from commit 2903863a1d7313118255dc8cc0664cdfe3e6a379)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Brian Goff authored on 2021/01/05 04:43:19
Showing 5 changed files
... ...
@@ -965,8 +965,12 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
965 965
 		}
966 966
 
967 967
 		var rt types.Runtime
968
-		if runtime := config.GetRuntime(config.GetDefaultRuntimeName()); runtime != nil {
969
-			rt = *runtime
968
+		if runtime.GOOS != "windows" {
969
+			rtPtr, err := d.getRuntime(config.GetDefaultRuntimeName())
970
+			if err != nil {
971
+				return nil, err
972
+			}
973
+			rt = *rtPtr
970 974
 		}
971 975
 		return pluginexec.New(ctx, getPluginExecRoot(config.Root), pluginCli, config.ContainerdPluginNamespace, m, rt)
972 976
 	}
... ...
@@ -10,10 +10,12 @@ import (
10 10
 	"path/filepath"
11 11
 	"strings"
12 12
 
13
+	"github.com/containerd/cgroups"
13 14
 	"github.com/containerd/containerd/runtime/linux/runctypes"
14 15
 	v2runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
15 16
 	"github.com/docker/docker/api/types"
16 17
 	"github.com/docker/docker/daemon/config"
18
+	"github.com/docker/docker/errdefs"
17 19
 	"github.com/docker/docker/pkg/ioutils"
18 20
 	"github.com/pkg/errors"
19 21
 	"github.com/sirupsen/logrus"
... ...
@@ -96,14 +98,15 @@ func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error
96 96
 	}()
97 97
 
98 98
 	for name, rt := range runtimes {
99
-		if len(rt.Args) == 0 {
100
-			continue
99
+		if len(rt.Args) > 0 {
100
+			script := filepath.Join(tmpDir, name)
101
+			content := fmt.Sprintf("#!/bin/sh\n%s %s $@\n", rt.Path, strings.Join(rt.Args, " "))
102
+			if err := ioutil.WriteFile(script, []byte(content), 0700); err != nil {
103
+				return err
104
+			}
101 105
 		}
102
-
103
-		script := filepath.Join(tmpDir, name)
104
-		content := fmt.Sprintf("#!/bin/sh\n%s %s $@\n", rt.Path, strings.Join(rt.Args, " "))
105
-		if err := ioutil.WriteFile(script, []byte(content), 0700); err != nil {
106
-			return err
106
+		if rt.Shim == nil {
107
+			rt.Shim = defaultV2ShimConfig(daemon.configStore, rt.Path)
107 108
 		}
108 109
 	}
109 110
 	return nil
... ...
@@ -124,3 +127,32 @@ func (daemon *Daemon) rewriteRuntimePath(name, p string, args []string) (string,
124 124
 
125 125
 	return filepath.Join(daemon.configStore.Root, "runtimes", name), nil
126 126
 }
127
+
128
+func (daemon *Daemon) getRuntime(name string) (*types.Runtime, error) {
129
+	rt := daemon.configStore.GetRuntime(name)
130
+	if rt == nil {
131
+		return nil, errdefs.InvalidParameter(errors.Errorf("runtime not found in config: %s", name))
132
+	}
133
+
134
+	if len(rt.Args) > 0 {
135
+		p, err := daemon.rewriteRuntimePath(name, rt.Path, rt.Args)
136
+		if err != nil {
137
+			return nil, err
138
+		}
139
+		rt.Path = p
140
+		rt.Args = nil
141
+	}
142
+
143
+	if rt.Shim == nil {
144
+		rt.Shim = defaultV2ShimConfig(daemon.configStore, rt.Path)
145
+	}
146
+
147
+	if rt.Shim.Binary == linuxShimV1 {
148
+		if cgroups.Mode() == cgroups.Unified {
149
+			return nil, errdefs.InvalidParameter(errors.Errorf("runtime %q is not supported while cgroups v2 (unified hierarchy) is being used", name))
150
+		}
151
+		logrus.Warnf("Configured runtime %q is deprecated and will be removed in the next release", name)
152
+	}
153
+
154
+	return rt, nil
155
+}
127 156
new file mode 100644
... ...
@@ -0,0 +1,10 @@
0
+package daemon
1
+
2
+import (
3
+	"github.com/docker/docker/api/types"
4
+	"github.com/pkg/errors"
5
+)
6
+
7
+func (daemon *Daemon) getRuntime(name string) (*types.Runtime, error) {
8
+	return nil, errors.New("not implemented")
9
+}
... ...
@@ -3,11 +3,7 @@
3 3
 package daemon // import "github.com/docker/docker/daemon"
4 4
 
5 5
 import (
6
-	"github.com/containerd/cgroups"
7 6
 	"github.com/docker/docker/container"
8
-	"github.com/docker/docker/errdefs"
9
-	"github.com/pkg/errors"
10
-	"github.com/sirupsen/logrus"
11 7
 )
12 8
 
13 9
 // getLibcontainerdCreateOptions callers must hold a lock on the container
... ...
@@ -18,19 +14,9 @@ func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Contain
18 18
 		container.CheckpointTo(daemon.containersReplica)
19 19
 	}
20 20
 
21
-	rt := daemon.configStore.GetRuntime(container.HostConfig.Runtime)
22
-	if rt.Shim == nil {
23
-		p, err := daemon.rewriteRuntimePath(container.HostConfig.Runtime, rt.Path, rt.Args)
24
-		if err != nil {
25
-			return "", nil, translateContainerdStartErr(container.Path, container.SetExitCode, err)
26
-		}
27
-		rt.Shim = defaultV2ShimConfig(daemon.configStore, p)
28
-	}
29
-	if rt.Shim.Binary == linuxShimV1 {
30
-		if cgroups.Mode() == cgroups.Unified {
31
-			return "", nil, errdefs.InvalidParameter(errors.Errorf("runtime %q is not supported while cgroups v2 (unified hierarchy) is being used", container.HostConfig.Runtime))
32
-		}
33
-		logrus.Warnf("Configured runtime %q is deprecated and will be removed in the next release", container.HostConfig.Runtime)
21
+	rt, err := daemon.getRuntime(container.HostConfig.Runtime)
22
+	if err != nil {
23
+		return "", nil, translateContainerdStartErr(container.Path, container.SetExitCode, err)
34 24
 	}
35 25
 
36 26
 	return rt.Shim.Binary, rt.Shim.Opts, nil
... ...
@@ -4,11 +4,14 @@ import (
4 4
 	"context"
5 5
 	"encoding/base64"
6 6
 	"encoding/json"
7
+	"fmt"
7 8
 	"io"
8 9
 	"io/ioutil"
9 10
 	"net"
10 11
 	"net/http"
12
+	"os"
11 13
 	"path"
14
+	"path/filepath"
12 15
 	"strings"
13 16
 	"testing"
14 17
 
... ...
@@ -157,3 +160,66 @@ func TestPluginInstall(t *testing.T) {
157 157
 	})
158 158
 	// TODO: test insecure registry with https
159 159
 }
160
+
161
+func TestPluginsWithRuntimes(t *testing.T) {
162
+	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
163
+	skip.If(t, testEnv.IsRootless, "Test not supported on rootless due to buggy daemon setup in rootless mode due to daemon restart")
164
+	skip.If(t, testEnv.OSType == "windows")
165
+
166
+	dir, err := ioutil.TempDir("", t.Name())
167
+	assert.NilError(t, err)
168
+	defer os.RemoveAll(dir)
169
+
170
+	d := daemon.New(t)
171
+	defer d.Cleanup(t)
172
+
173
+	d.Start(t)
174
+	defer d.Stop(t)
175
+
176
+	ctx := context.Background()
177
+	client := d.NewClientT(t)
178
+
179
+	assert.NilError(t, plugin.Create(ctx, client, "test:latest"))
180
+	defer client.PluginRemove(ctx, "test:latest", types.PluginRemoveOptions{Force: true})
181
+
182
+	assert.NilError(t, client.PluginEnable(ctx, "test:latest", types.PluginEnableOptions{Timeout: 30}))
183
+
184
+	p := filepath.Join(dir, "myrt")
185
+	script := fmt.Sprintf(`#!/bin/sh
186
+	file="%s/success"
187
+	if [ "$1" = "someArg" ]; then
188
+		shift
189
+		file="${file}_someArg"
190
+	fi
191
+
192
+	touch $file
193
+	exec runc $@
194
+	`, dir)
195
+
196
+	assert.NilError(t, ioutil.WriteFile(p, []byte(script), 0777))
197
+
198
+	type config struct {
199
+		Runtimes map[string]types.Runtime `json:"runtimes"`
200
+	}
201
+
202
+	cfg, err := json.Marshal(config{
203
+		Runtimes: map[string]types.Runtime{
204
+			"myrt":     {Path: p},
205
+			"myrtArgs": {Path: p, Args: []string{"someArg"}},
206
+		},
207
+	})
208
+	configPath := filepath.Join(dir, "config.json")
209
+	ioutil.WriteFile(configPath, cfg, 0644)
210
+
211
+	t.Run("No Args", func(t *testing.T) {
212
+		d.Restart(t, "--default-runtime=myrt", "--config-file="+configPath)
213
+		_, err = os.Stat(filepath.Join(dir, "success"))
214
+		assert.NilError(t, err)
215
+	})
216
+
217
+	t.Run("With Args", func(t *testing.T) {
218
+		d.Restart(t, "--default-runtime=myrtArgs", "--config-file="+configPath)
219
+		_, err = os.Stat(filepath.Join(dir, "success_someArg"))
220
+		assert.NilError(t, err)
221
+	})
222
+}