github.com/lacework-dev/go-moby@v20.10.12+incompatible/integration/plugin/common/plugin_test.go (about) 1 package common // import "github.com/docker/docker/integration/plugin/common" 2 3 import ( 4 "context" 5 "encoding/base64" 6 "encoding/json" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "net" 11 "net/http" 12 "os" 13 "path" 14 "path/filepath" 15 "strings" 16 "testing" 17 18 "github.com/containerd/containerd/images" 19 "github.com/containerd/containerd/remotes/docker" 20 "github.com/docker/docker/api/types" 21 "github.com/docker/docker/pkg/jsonmessage" 22 "github.com/docker/docker/testutil/daemon" 23 "github.com/docker/docker/testutil/fixtures/plugin" 24 "github.com/docker/docker/testutil/registry" 25 "github.com/docker/docker/testutil/request" 26 v1 "github.com/opencontainers/image-spec/specs-go/v1" 27 "gotest.tools/v3/assert" 28 "gotest.tools/v3/assert/cmp" 29 is "gotest.tools/v3/assert/cmp" 30 "gotest.tools/v3/skip" 31 ) 32 33 func TestPluginInvalidJSON(t *testing.T) { 34 defer setupTest(t)() 35 36 endpoints := []string{"/plugins/foobar/set"} 37 38 for _, ep := range endpoints { 39 t.Run(ep, func(t *testing.T) { 40 t.Parallel() 41 42 res, body, err := request.Post(ep, request.RawString("{invalid json"), request.JSON) 43 assert.NilError(t, err) 44 assert.Equal(t, res.StatusCode, http.StatusBadRequest) 45 46 buf, err := request.ReadBody(body) 47 assert.NilError(t, err) 48 assert.Check(t, is.Contains(string(buf), "invalid character 'i' looking for beginning of object key string")) 49 50 res, body, err = request.Post(ep, request.JSON) 51 assert.NilError(t, err) 52 assert.Equal(t, res.StatusCode, http.StatusBadRequest) 53 54 buf, err = request.ReadBody(body) 55 assert.NilError(t, err) 56 assert.Check(t, is.Contains(string(buf), "got EOF while reading request body")) 57 }) 58 } 59 } 60 61 func TestPluginInstall(t *testing.T) { 62 skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon") 63 skip.If(t, testEnv.OSType == "windows") 64 skip.If(t, testEnv.IsRootless, "rootless mode has different view of localhost") 65 66 ctx := context.Background() 67 client := testEnv.APIClient() 68 69 t.Run("no auth", func(t *testing.T) { 70 defer setupTest(t)() 71 72 reg := registry.NewV2(t) 73 defer reg.Close() 74 75 name := "test-" + strings.ToLower(t.Name()) 76 repo := path.Join(registry.DefaultURL, name+":latest") 77 assert.NilError(t, plugin.CreateInRegistry(ctx, repo, nil)) 78 79 rdr, err := client.PluginInstall(ctx, repo, types.PluginInstallOptions{Disabled: true, RemoteRef: repo}) 80 assert.NilError(t, err) 81 defer rdr.Close() 82 83 _, err = io.Copy(ioutil.Discard, rdr) 84 assert.NilError(t, err) 85 86 _, _, err = client.PluginInspectWithRaw(ctx, repo) 87 assert.NilError(t, err) 88 }) 89 90 t.Run("with htpasswd", func(t *testing.T) { 91 defer setupTest(t)() 92 93 reg := registry.NewV2(t, registry.Htpasswd) 94 defer reg.Close() 95 96 name := "test-" + strings.ToLower(t.Name()) 97 repo := path.Join(registry.DefaultURL, name+":latest") 98 auth := &types.AuthConfig{ServerAddress: registry.DefaultURL, Username: "testuser", Password: "testpassword"} 99 assert.NilError(t, plugin.CreateInRegistry(ctx, repo, auth)) 100 101 authEncoded, err := json.Marshal(auth) 102 assert.NilError(t, err) 103 104 rdr, err := client.PluginInstall(ctx, repo, types.PluginInstallOptions{ 105 RegistryAuth: base64.URLEncoding.EncodeToString(authEncoded), 106 Disabled: true, 107 RemoteRef: repo, 108 }) 109 assert.NilError(t, err) 110 defer rdr.Close() 111 112 _, err = io.Copy(ioutil.Discard, rdr) 113 assert.NilError(t, err) 114 115 _, _, err = client.PluginInspectWithRaw(ctx, repo) 116 assert.NilError(t, err) 117 }) 118 t.Run("with insecure", func(t *testing.T) { 119 skip.If(t, !testEnv.IsLocalDaemon()) 120 121 addrs, err := net.InterfaceAddrs() 122 assert.NilError(t, err) 123 124 var bindTo string 125 for _, addr := range addrs { 126 ip, ok := addr.(*net.IPNet) 127 if !ok { 128 continue 129 } 130 if ip.IP.IsLoopback() || ip.IP.To4() == nil { 131 continue 132 } 133 bindTo = ip.IP.String() 134 } 135 136 if bindTo == "" { 137 t.Skip("No suitable interface to bind registry to") 138 } 139 140 regURL := bindTo + ":5000" 141 142 d := daemon.New(t) 143 defer d.Stop(t) 144 145 d.Start(t, "--insecure-registry="+regURL) 146 defer d.Stop(t) 147 148 reg := registry.NewV2(t, registry.URL(regURL)) 149 defer reg.Close() 150 151 name := "test-" + strings.ToLower(t.Name()) 152 repo := path.Join(regURL, name+":latest") 153 assert.NilError(t, plugin.CreateInRegistry(ctx, repo, nil, plugin.WithInsecureRegistry(regURL))) 154 155 client := d.NewClientT(t) 156 rdr, err := client.PluginInstall(ctx, repo, types.PluginInstallOptions{Disabled: true, RemoteRef: repo}) 157 assert.NilError(t, err) 158 defer rdr.Close() 159 160 _, err = io.Copy(ioutil.Discard, rdr) 161 assert.NilError(t, err) 162 163 _, _, err = client.PluginInspectWithRaw(ctx, repo) 164 assert.NilError(t, err) 165 }) 166 // TODO: test insecure registry with https 167 } 168 169 func TestPluginsWithRuntimes(t *testing.T) { 170 skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon") 171 skip.If(t, testEnv.IsRootless, "Test not supported on rootless due to buggy daemon setup in rootless mode due to daemon restart") 172 skip.If(t, testEnv.OSType == "windows") 173 174 dir, err := ioutil.TempDir("", t.Name()) 175 assert.NilError(t, err) 176 defer os.RemoveAll(dir) 177 178 d := daemon.New(t) 179 defer d.Cleanup(t) 180 181 d.Start(t) 182 defer d.Stop(t) 183 184 ctx := context.Background() 185 client := d.NewClientT(t) 186 187 assert.NilError(t, plugin.Create(ctx, client, "test:latest")) 188 defer client.PluginRemove(ctx, "test:latest", types.PluginRemoveOptions{Force: true}) 189 190 assert.NilError(t, client.PluginEnable(ctx, "test:latest", types.PluginEnableOptions{Timeout: 30})) 191 192 p := filepath.Join(dir, "myrt") 193 script := fmt.Sprintf(`#!/bin/sh 194 file="%s/success" 195 if [ "$1" = "someArg" ]; then 196 shift 197 file="${file}_someArg" 198 fi 199 200 touch $file 201 exec runc $@ 202 `, dir) 203 204 assert.NilError(t, ioutil.WriteFile(p, []byte(script), 0777)) 205 206 type config struct { 207 Runtimes map[string]types.Runtime `json:"runtimes"` 208 } 209 210 cfg, err := json.Marshal(config{ 211 Runtimes: map[string]types.Runtime{ 212 "myrt": {Path: p}, 213 "myrtArgs": {Path: p, Args: []string{"someArg"}}, 214 }, 215 }) 216 configPath := filepath.Join(dir, "config.json") 217 ioutil.WriteFile(configPath, cfg, 0644) 218 219 t.Run("No Args", func(t *testing.T) { 220 d.Restart(t, "--default-runtime=myrt", "--config-file="+configPath) 221 _, err = os.Stat(filepath.Join(dir, "success")) 222 assert.NilError(t, err) 223 }) 224 225 t.Run("With Args", func(t *testing.T) { 226 d.Restart(t, "--default-runtime=myrtArgs", "--config-file="+configPath) 227 _, err = os.Stat(filepath.Join(dir, "success_someArg")) 228 assert.NilError(t, err) 229 }) 230 } 231 232 func TestPluginBackCompatMediaTypes(t *testing.T) { 233 skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon") 234 skip.If(t, testEnv.OSType == "windows") 235 skip.If(t, testEnv.IsRootless, "Rootless has a different view of localhost (needed for test registry access)") 236 237 defer setupTest(t)() 238 239 reg := registry.NewV2(t) 240 defer reg.Close() 241 reg.WaitReady(t) 242 243 repo := path.Join(registry.DefaultURL, strings.ToLower(t.Name())+":latest") 244 245 client := testEnv.APIClient() 246 247 ctx := context.Background() 248 assert.NilError(t, plugin.Create(ctx, client, repo)) 249 250 rdr, err := client.PluginPush(ctx, repo, "") 251 assert.NilError(t, err) 252 defer rdr.Close() 253 254 buf := &strings.Builder{} 255 assert.NilError(t, jsonmessage.DisplayJSONMessagesStream(rdr, buf, 0, false, nil), buf) 256 257 // Use custom header here because older versions of the registry do not 258 // parse the accept header correctly and does not like the accept header 259 // that the default resolver code uses. "Older registries" here would be 260 // like the one currently included in the test suite. 261 headers := http.Header{} 262 headers.Add("Accept", images.MediaTypeDockerSchema2Manifest) 263 264 resolver := docker.NewResolver(docker.ResolverOptions{ 265 Headers: headers, 266 }) 267 assert.NilError(t, err) 268 269 n, desc, err := resolver.Resolve(ctx, repo) 270 assert.NilError(t, err, repo) 271 272 fetcher, err := resolver.Fetcher(ctx, n) 273 assert.NilError(t, err) 274 275 rdr, err = fetcher.Fetch(ctx, desc) 276 assert.NilError(t, err) 277 defer rdr.Close() 278 279 var m v1.Manifest 280 assert.NilError(t, json.NewDecoder(rdr).Decode(&m)) 281 assert.Check(t, cmp.Equal(m.MediaType, images.MediaTypeDockerSchema2Manifest)) 282 assert.Check(t, cmp.Len(m.Layers, 1)) 283 assert.Check(t, cmp.Equal(m.Layers[0].MediaType, images.MediaTypeDockerSchema2LayerGzip)) 284 }