github.com/kaisenlinux/docker@v0.0.0-20230510090727-ea55db55fac7/engine/integration-cli/docker_cli_plugins_test.go (about) 1 package main 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "net/http" 8 "os" 9 "path" 10 "path/filepath" 11 "strings" 12 "testing" 13 "time" 14 15 "github.com/docker/docker/api/types" 16 "github.com/docker/docker/integration-cli/cli" 17 "github.com/docker/docker/integration-cli/daemon" 18 "github.com/docker/docker/testutil/fixtures/plugin" 19 "gotest.tools/v3/assert" 20 ) 21 22 var ( 23 pluginProcessName = "sample-volume-plugin" 24 pName = "tiborvass/sample-volume-plugin" 25 npName = "tiborvass/test-docker-netplugin" 26 pTag = "latest" 27 pNameWithTag = pName + ":" + pTag 28 npNameWithTag = npName + ":" + pTag 29 ) 30 31 func (ps *DockerPluginSuite) TestPluginBasicOps(c *testing.T) { 32 plugin := ps.getPluginRepoWithTag() 33 _, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", plugin) 34 assert.NilError(c, err) 35 36 out, _, err := dockerCmdWithError("plugin", "ls") 37 assert.NilError(c, err) 38 assert.Assert(c, strings.Contains(out, plugin)) 39 assert.Assert(c, strings.Contains(out, "true")) 40 id, _, err := dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", plugin) 41 id = strings.TrimSpace(id) 42 assert.NilError(c, err) 43 44 out, _, err = dockerCmdWithError("plugin", "remove", plugin) 45 assert.ErrorContains(c, err, "") 46 assert.Assert(c, strings.Contains(out, "is enabled")) 47 _, _, err = dockerCmdWithError("plugin", "disable", plugin) 48 assert.NilError(c, err) 49 50 out, _, err = dockerCmdWithError("plugin", "remove", plugin) 51 assert.NilError(c, err) 52 assert.Assert(c, strings.Contains(out, plugin)) 53 _, err = os.Stat(filepath.Join(testEnv.DaemonInfo.DockerRootDir, "plugins", id)) 54 if !os.IsNotExist(err) { 55 c.Fatal(err) 56 } 57 } 58 59 func (ps *DockerPluginSuite) TestPluginForceRemove(c *testing.T) { 60 pNameWithTag := ps.getPluginRepoWithTag() 61 62 _, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag) 63 assert.NilError(c, err) 64 65 out, _, _ := dockerCmdWithError("plugin", "remove", pNameWithTag) 66 assert.Assert(c, strings.Contains(out, "is enabled")) 67 out, _, err = dockerCmdWithError("plugin", "remove", "--force", pNameWithTag) 68 assert.NilError(c, err) 69 assert.Assert(c, strings.Contains(out, pNameWithTag)) 70 } 71 72 func (s *DockerSuite) TestPluginActive(c *testing.T) { 73 testRequires(c, DaemonIsLinux, IsAmd64, Network) 74 75 _, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag) 76 assert.NilError(c, err) 77 78 _, _, err = dockerCmdWithError("volume", "create", "-d", pNameWithTag, "--name", "testvol1") 79 assert.NilError(c, err) 80 81 out, _, _ := dockerCmdWithError("plugin", "disable", pNameWithTag) 82 assert.Assert(c, strings.Contains(out, "in use")) 83 _, _, err = dockerCmdWithError("volume", "rm", "testvol1") 84 assert.NilError(c, err) 85 86 _, _, err = dockerCmdWithError("plugin", "disable", pNameWithTag) 87 assert.NilError(c, err) 88 89 out, _, err = dockerCmdWithError("plugin", "remove", pNameWithTag) 90 assert.NilError(c, err) 91 assert.Assert(c, strings.Contains(out, pNameWithTag)) 92 } 93 94 func (s *DockerSuite) TestPluginActiveNetwork(c *testing.T) { 95 testRequires(c, DaemonIsLinux, IsAmd64, Network) 96 _, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", npNameWithTag) 97 assert.NilError(c, err) 98 99 out, _, err := dockerCmdWithError("network", "create", "-d", npNameWithTag, "test") 100 assert.NilError(c, err) 101 102 nID := strings.TrimSpace(out) 103 104 out, _, _ = dockerCmdWithError("plugin", "remove", npNameWithTag) 105 assert.Assert(c, strings.Contains(out, "is in use")) 106 _, _, err = dockerCmdWithError("network", "rm", nID) 107 assert.NilError(c, err) 108 109 out, _, _ = dockerCmdWithError("plugin", "remove", npNameWithTag) 110 assert.Assert(c, strings.Contains(out, "is enabled")) 111 _, _, err = dockerCmdWithError("plugin", "disable", npNameWithTag) 112 assert.NilError(c, err) 113 114 out, _, err = dockerCmdWithError("plugin", "remove", npNameWithTag) 115 assert.NilError(c, err) 116 assert.Assert(c, strings.Contains(out, npNameWithTag)) 117 } 118 119 func (ps *DockerPluginSuite) TestPluginInstallDisable(c *testing.T) { 120 pName := ps.getPluginRepoWithTag() 121 122 out, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", "--disable", pName) 123 assert.NilError(c, err) 124 assert.Assert(c, strings.Contains(strings.TrimSpace(out), pName)) 125 out, _, err = dockerCmdWithError("plugin", "ls") 126 assert.NilError(c, err) 127 assert.Assert(c, strings.Contains(out, "false")) 128 out, _, err = dockerCmdWithError("plugin", "enable", pName) 129 assert.NilError(c, err) 130 assert.Assert(c, strings.Contains(strings.TrimSpace(out), pName)) 131 out, _, err = dockerCmdWithError("plugin", "disable", pName) 132 assert.NilError(c, err) 133 assert.Assert(c, strings.Contains(strings.TrimSpace(out), pName)) 134 out, _, err = dockerCmdWithError("plugin", "remove", pName) 135 assert.NilError(c, err) 136 assert.Assert(c, strings.Contains(strings.TrimSpace(out), pName)) 137 } 138 139 func (s *DockerSuite) TestPluginInstallDisableVolumeLs(c *testing.T) { 140 testRequires(c, DaemonIsLinux, IsAmd64, Network) 141 out, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", "--disable", pName) 142 assert.NilError(c, err) 143 assert.Assert(c, strings.Contains(strings.TrimSpace(out), pName)) 144 dockerCmd(c, "volume", "ls") 145 } 146 147 func (ps *DockerPluginSuite) TestPluginSet(c *testing.T) { 148 client := testEnv.APIClient() 149 150 name := "test" 151 ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) 152 defer cancel() 153 154 initialValue := "0" 155 mntSrc := "foo" 156 devPath := "/dev/bar" 157 158 // Create a new plugin with extra settings 159 err := plugin.Create(ctx, client, name, func(cfg *plugin.Config) { 160 cfg.Env = []types.PluginEnv{{Name: "DEBUG", Value: &initialValue, Settable: []string{"value"}}} 161 cfg.Mounts = []types.PluginMount{ 162 {Name: "pmount1", Settable: []string{"source"}, Type: "none", Source: &mntSrc}, 163 {Name: "pmount2", Settable: []string{"source"}, Type: "none"}, // Mount without source is invalid. 164 } 165 cfg.Linux.Devices = []types.PluginDevice{ 166 {Name: "pdev1", Path: &devPath, Settable: []string{"path"}}, 167 {Name: "pdev2", Settable: []string{"path"}}, // Device without Path is invalid. 168 } 169 }) 170 assert.Assert(c, err == nil, "failed to create test plugin") 171 172 env, _ := dockerCmd(c, "plugin", "inspect", "-f", "{{.Settings.Env}}", name) 173 assert.Equal(c, strings.TrimSpace(env), "[DEBUG=0]") 174 175 dockerCmd(c, "plugin", "set", name, "DEBUG=1") 176 177 env, _ = dockerCmd(c, "plugin", "inspect", "-f", "{{.Settings.Env}}", name) 178 assert.Equal(c, strings.TrimSpace(env), "[DEBUG=1]") 179 180 env, _ = dockerCmd(c, "plugin", "inspect", "-f", "{{with $mount := index .Settings.Mounts 0}}{{$mount.Source}}{{end}}", name) 181 assert.Assert(c, strings.Contains(strings.TrimSpace(env), mntSrc)) 182 dockerCmd(c, "plugin", "set", name, "pmount1.source=bar") 183 184 env, _ = dockerCmd(c, "plugin", "inspect", "-f", "{{with $mount := index .Settings.Mounts 0}}{{$mount.Source}}{{end}}", name) 185 assert.Assert(c, strings.Contains(strings.TrimSpace(env), "bar")) 186 out, _, err := dockerCmdWithError("plugin", "set", name, "pmount2.source=bar2") 187 assert.ErrorContains(c, err, "") 188 assert.Assert(c, strings.Contains(out, "Plugin config has no mount source")) 189 out, _, err = dockerCmdWithError("plugin", "set", name, "pdev2.path=/dev/bar2") 190 assert.ErrorContains(c, err, "") 191 assert.Assert(c, strings.Contains(out, "Plugin config has no device path")) 192 } 193 194 func (ps *DockerPluginSuite) TestPluginInstallArgs(c *testing.T) { 195 pName := path.Join(ps.registryHost(), "plugin", "testplugininstallwithargs") 196 ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) 197 defer cancel() 198 199 plugin.CreateInRegistry(ctx, pName, nil, func(cfg *plugin.Config) { 200 cfg.Env = []types.PluginEnv{{Name: "DEBUG", Settable: []string{"value"}}} 201 }) 202 203 out, _ := dockerCmd(c, "plugin", "install", "--grant-all-permissions", "--disable", pName, "DEBUG=1") 204 assert.Assert(c, strings.Contains(strings.TrimSpace(out), pName)) 205 env, _ := dockerCmd(c, "plugin", "inspect", "-f", "{{.Settings.Env}}", pName) 206 assert.Equal(c, strings.TrimSpace(env), "[DEBUG=1]") 207 } 208 209 func (ps *DockerPluginSuite) TestPluginInstallImage(c *testing.T) { 210 testRequires(c, IsAmd64) 211 212 repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) 213 // tag the image to upload it to the private registry 214 dockerCmd(c, "tag", "busybox", repoName) 215 // push the image to the registry 216 dockerCmd(c, "push", repoName) 217 218 out, _, err := dockerCmdWithError("plugin", "install", repoName) 219 assert.ErrorContains(c, err, "") 220 assert.Assert(c, strings.Contains(out, `Encountered remote "application/vnd.docker.container.image.v1+json"(image) when fetching`)) 221 } 222 223 func (ps *DockerPluginSuite) TestPluginEnableDisableNegative(c *testing.T) { 224 pName := ps.getPluginRepoWithTag() 225 226 out, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pName) 227 assert.NilError(c, err) 228 assert.Assert(c, strings.Contains(strings.TrimSpace(out), pName)) 229 out, _, err = dockerCmdWithError("plugin", "enable", pName) 230 assert.ErrorContains(c, err, "") 231 assert.Assert(c, strings.Contains(strings.TrimSpace(out), "already enabled")) 232 _, _, err = dockerCmdWithError("plugin", "disable", pName) 233 assert.NilError(c, err) 234 235 out, _, err = dockerCmdWithError("plugin", "disable", pName) 236 assert.ErrorContains(c, err, "") 237 assert.Assert(c, strings.Contains(strings.TrimSpace(out), "already disabled")) 238 _, _, err = dockerCmdWithError("plugin", "remove", pName) 239 assert.NilError(c, err) 240 } 241 242 func (ps *DockerPluginSuite) TestPluginCreate(c *testing.T) { 243 name := "foo/bar-driver" 244 temp, err := os.MkdirTemp("", "foo") 245 assert.NilError(c, err) 246 defer os.RemoveAll(temp) 247 248 data := `{"description": "foo plugin"}` 249 err = os.WriteFile(filepath.Join(temp, "config.json"), []byte(data), 0644) 250 assert.NilError(c, err) 251 252 err = os.MkdirAll(filepath.Join(temp, "rootfs"), 0700) 253 assert.NilError(c, err) 254 255 out, _, err := dockerCmdWithError("plugin", "create", name, temp) 256 assert.NilError(c, err) 257 assert.Assert(c, strings.Contains(out, name)) 258 out, _, err = dockerCmdWithError("plugin", "ls") 259 assert.NilError(c, err) 260 assert.Assert(c, strings.Contains(out, name)) 261 out, _, err = dockerCmdWithError("plugin", "create", name, temp) 262 assert.ErrorContains(c, err, "") 263 assert.Assert(c, strings.Contains(out, "already exist")) 264 out, _, err = dockerCmdWithError("plugin", "ls") 265 assert.NilError(c, err) 266 assert.Assert(c, strings.Contains(out, name)) 267 // The output will consists of one HEADER line and one line of foo/bar-driver 268 assert.Equal(c, len(strings.Split(strings.TrimSpace(out), "\n")), 2) 269 } 270 271 func (ps *DockerPluginSuite) TestPluginInspect(c *testing.T) { 272 pNameWithTag := ps.getPluginRepoWithTag() 273 274 _, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag) 275 assert.NilError(c, err) 276 277 out, _, err := dockerCmdWithError("plugin", "ls") 278 assert.NilError(c, err) 279 assert.Assert(c, strings.Contains(out, pNameWithTag)) 280 assert.Assert(c, strings.Contains(out, "true")) 281 // Find the ID first 282 out, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", pNameWithTag) 283 assert.NilError(c, err) 284 id := strings.TrimSpace(out) 285 assert.Assert(c, id != "") 286 287 // Long form 288 out, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", id) 289 assert.NilError(c, err) 290 assert.Equal(c, strings.TrimSpace(out), id) 291 292 // Short form 293 out, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", id[:5]) 294 assert.NilError(c, err) 295 assert.Equal(c, strings.TrimSpace(out), id) 296 297 // Name with tag form 298 out, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", pNameWithTag) 299 assert.NilError(c, err) 300 assert.Equal(c, strings.TrimSpace(out), id) 301 302 // Name without tag form 303 out, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", ps.getPluginRepo()) 304 assert.NilError(c, err) 305 assert.Equal(c, strings.TrimSpace(out), id) 306 307 _, _, err = dockerCmdWithError("plugin", "disable", pNameWithTag) 308 assert.NilError(c, err) 309 310 out, _, err = dockerCmdWithError("plugin", "remove", pNameWithTag) 311 assert.NilError(c, err) 312 assert.Assert(c, strings.Contains(out, pNameWithTag)) 313 // After remove nothing should be found 314 _, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", id[:5]) 315 assert.ErrorContains(c, err, "") 316 } 317 318 // Test case for https://github.com/docker/docker/pull/29186#discussion_r91277345 319 func (s *DockerSuite) TestPluginInspectOnWindows(c *testing.T) { 320 // This test should work on Windows only 321 testRequires(c, DaemonIsWindows) 322 323 out, _, err := dockerCmdWithError("plugin", "inspect", "foobar") 324 assert.ErrorContains(c, err, "") 325 assert.Assert(c, strings.Contains(out, "plugins are not supported on this platform")) 326 assert.ErrorContains(c, err, "plugins are not supported on this platform") 327 } 328 329 func (ps *DockerPluginSuite) TestPluginIDPrefix(c *testing.T) { 330 name := "test" 331 client := testEnv.APIClient() 332 333 ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) 334 initialValue := "0" 335 err := plugin.Create(ctx, client, name, func(cfg *plugin.Config) { 336 cfg.Env = []types.PluginEnv{{Name: "DEBUG", Value: &initialValue, Settable: []string{"value"}}} 337 }) 338 cancel() 339 340 assert.Assert(c, err == nil, "failed to create test plugin") 341 342 // Find ID first 343 id, _, err := dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", name) 344 id = strings.TrimSpace(id) 345 assert.NilError(c, err) 346 347 // List current state 348 out, _, err := dockerCmdWithError("plugin", "ls") 349 assert.NilError(c, err) 350 assert.Assert(c, strings.Contains(out, name)) 351 assert.Assert(c, strings.Contains(out, "false")) 352 env, _ := dockerCmd(c, "plugin", "inspect", "-f", "{{.Settings.Env}}", id[:5]) 353 assert.Equal(c, strings.TrimSpace(env), "[DEBUG=0]") 354 355 dockerCmd(c, "plugin", "set", id[:5], "DEBUG=1") 356 357 env, _ = dockerCmd(c, "plugin", "inspect", "-f", "{{.Settings.Env}}", id[:5]) 358 assert.Equal(c, strings.TrimSpace(env), "[DEBUG=1]") 359 360 // Enable 361 _, _, err = dockerCmdWithError("plugin", "enable", id[:5]) 362 assert.NilError(c, err) 363 out, _, err = dockerCmdWithError("plugin", "ls") 364 assert.NilError(c, err) 365 assert.Assert(c, strings.Contains(out, name)) 366 assert.Assert(c, strings.Contains(out, "true")) 367 // Disable 368 _, _, err = dockerCmdWithError("plugin", "disable", id[:5]) 369 assert.NilError(c, err) 370 out, _, err = dockerCmdWithError("plugin", "ls") 371 assert.NilError(c, err) 372 assert.Assert(c, strings.Contains(out, name)) 373 assert.Assert(c, strings.Contains(out, "false")) 374 // Remove 375 _, _, err = dockerCmdWithError("plugin", "remove", id[:5]) 376 assert.NilError(c, err) 377 // List returns none 378 out, _, err = dockerCmdWithError("plugin", "ls") 379 assert.NilError(c, err) 380 assert.Assert(c, !strings.Contains(out, name)) 381 } 382 383 func (ps *DockerPluginSuite) TestPluginListDefaultFormat(c *testing.T) { 384 config, err := os.MkdirTemp("", "config-file-") 385 assert.NilError(c, err) 386 defer os.RemoveAll(config) 387 388 err = os.WriteFile(filepath.Join(config, "config.json"), []byte(`{"pluginsFormat": "raw"}`), 0644) 389 assert.NilError(c, err) 390 391 name := "test:latest" 392 client := testEnv.APIClient() 393 394 ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) 395 defer cancel() 396 err = plugin.Create(ctx, client, name, func(cfg *plugin.Config) { 397 cfg.Description = "test plugin" 398 }) 399 assert.Assert(c, err == nil, "failed to create test plugin") 400 401 out, _ := dockerCmd(c, "plugin", "inspect", "--format", "{{.ID}}", name) 402 id := strings.TrimSpace(out) 403 404 // We expect the format to be in `raw + --no-trunc` 405 expectedOutput := fmt.Sprintf(`plugin_id: %s 406 name: %s 407 description: test plugin 408 enabled: false`, id, name) 409 410 out, _ = dockerCmd(c, "--config", config, "plugin", "ls", "--no-trunc") 411 assert.Assert(c, strings.Contains(strings.TrimSpace(out), expectedOutput)) 412 } 413 414 func (s *DockerSuite) TestPluginUpgrade(c *testing.T) { 415 testRequires(c, DaemonIsLinux, Network, testEnv.IsLocalDaemon, IsAmd64, NotUserNamespace) 416 plugin := "cpuguy83/docker-volume-driver-plugin-local:latest" 417 pluginV2 := "cpuguy83/docker-volume-driver-plugin-local:v2" 418 419 dockerCmd(c, "plugin", "install", "--grant-all-permissions", plugin) 420 dockerCmd(c, "volume", "create", "--driver", plugin, "bananas") 421 dockerCmd(c, "run", "--rm", "-v", "bananas:/apple", "busybox", "sh", "-c", "touch /apple/core") 422 423 out, _, err := dockerCmdWithError("plugin", "upgrade", "--grant-all-permissions", plugin, pluginV2) 424 assert.ErrorContains(c, err, "", out) 425 assert.Assert(c, strings.Contains(out, "disabled before upgrading")) 426 out, _ = dockerCmd(c, "plugin", "inspect", "--format={{.ID}}", plugin) 427 id := strings.TrimSpace(out) 428 429 // make sure "v2" does not exists 430 _, err = os.Stat(filepath.Join(testEnv.DaemonInfo.DockerRootDir, "plugins", id, "rootfs", "v2")) 431 assert.Assert(c, os.IsNotExist(err), out) 432 433 dockerCmd(c, "plugin", "disable", "-f", plugin) 434 dockerCmd(c, "plugin", "upgrade", "--grant-all-permissions", "--skip-remote-check", plugin, pluginV2) 435 436 // make sure "v2" file exists 437 _, err = os.Stat(filepath.Join(testEnv.DaemonInfo.DockerRootDir, "plugins", id, "rootfs", "v2")) 438 assert.NilError(c, err) 439 440 dockerCmd(c, "plugin", "enable", plugin) 441 dockerCmd(c, "volume", "inspect", "bananas") 442 dockerCmd(c, "run", "--rm", "-v", "bananas:/apple", "busybox", "sh", "-c", "ls -lh /apple/core") 443 } 444 445 func (s *DockerSuite) TestPluginMetricsCollector(c *testing.T) { 446 testRequires(c, DaemonIsLinux, Network, testEnv.IsLocalDaemon, IsAmd64) 447 d := daemon.New(c, dockerBinary, dockerdBinary) 448 d.Start(c) 449 defer d.Stop(c) 450 451 name := "cpuguy83/docker-metrics-plugin-test:latest" 452 r := cli.Docker(cli.Args("plugin", "install", "--grant-all-permissions", name), cli.Daemon(d)) 453 assert.Assert(c, r.Error == nil, r.Combined()) 454 455 // plugin lisens on localhost:19393 and proxies the metrics 456 resp, err := http.Get("http://localhost:19393/metrics") 457 assert.NilError(c, err) 458 defer resp.Body.Close() 459 460 b, err := io.ReadAll(resp.Body) 461 assert.NilError(c, err) 462 // check that a known metric is there... don't expect this metric to change over time.. probably safe 463 assert.Assert(c, strings.Contains(string(b), "container_actions")) 464 }