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