moby/integration/plugin/common/plugin_test.go
Roman Volosatovs dd01abf9bf
integration: copy loop variable into parallel test closures
Discovered a few instances, where loop variable is incorrectly used
within a test closure, which is marked as parallel.
Few of these were actually loops over singleton slices, therefore the issue
might not have surfaced there (yet), but it is good to fix there as
well, as this is an incorrect pattern used across different tests.

Signed-off-by: Roman Volosatovs <roman.volosatovs@docker.com>
2021-07-22 22:46:12 +02:00

289 lines
8 KiB
Go

package common // import "github.com/docker/docker/integration/plugin/common"
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"testing"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/remotes/docker"
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/testutil/daemon"
"github.com/docker/docker/testutil/fixtures/plugin"
"github.com/docker/docker/testutil/registry"
"github.com/docker/docker/testutil/request"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"gotest.tools/v3/assert"
"gotest.tools/v3/assert/cmp"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/skip"
)
func TestPluginInvalidJSON(t *testing.T) {
defer setupTest(t)()
endpoints := []string{"/plugins/foobar/set"}
for _, ep := range endpoints {
ep := ep
t.Run(ep, func(t *testing.T) {
t.Parallel()
res, body, err := request.Post(ep, request.RawString("{invalid json"), request.JSON)
assert.NilError(t, err)
assert.Equal(t, res.StatusCode, http.StatusBadRequest)
buf, err := request.ReadBody(body)
assert.NilError(t, err)
assert.Check(t, is.Contains(string(buf), "invalid character 'i' looking for beginning of object key string"))
res, body, err = request.Post(ep, request.JSON)
assert.NilError(t, err)
assert.Equal(t, res.StatusCode, http.StatusBadRequest)
buf, err = request.ReadBody(body)
assert.NilError(t, err)
assert.Check(t, is.Contains(string(buf), "got EOF while reading request body"))
})
}
}
func TestPluginInstall(t *testing.T) {
skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
skip.If(t, testEnv.OSType == "windows")
skip.If(t, testEnv.IsRootless, "rootless mode has different view of localhost")
ctx := context.Background()
client := testEnv.APIClient()
t.Run("no auth", func(t *testing.T) {
defer setupTest(t)()
reg := registry.NewV2(t)
defer reg.Close()
name := "test-" + strings.ToLower(t.Name())
repo := path.Join(registry.DefaultURL, name+":latest")
assert.NilError(t, plugin.CreateInRegistry(ctx, repo, nil))
rdr, err := client.PluginInstall(ctx, repo, types.PluginInstallOptions{Disabled: true, RemoteRef: repo})
assert.NilError(t, err)
defer rdr.Close()
_, err = io.Copy(ioutil.Discard, rdr)
assert.NilError(t, err)
_, _, err = client.PluginInspectWithRaw(ctx, repo)
assert.NilError(t, err)
})
t.Run("with htpasswd", func(t *testing.T) {
defer setupTest(t)()
reg := registry.NewV2(t, registry.Htpasswd)
defer reg.Close()
name := "test-" + strings.ToLower(t.Name())
repo := path.Join(registry.DefaultURL, name+":latest")
auth := &types.AuthConfig{ServerAddress: registry.DefaultURL, Username: "testuser", Password: "testpassword"}
assert.NilError(t, plugin.CreateInRegistry(ctx, repo, auth))
authEncoded, err := json.Marshal(auth)
assert.NilError(t, err)
rdr, err := client.PluginInstall(ctx, repo, types.PluginInstallOptions{
RegistryAuth: base64.URLEncoding.EncodeToString(authEncoded),
Disabled: true,
RemoteRef: repo,
})
assert.NilError(t, err)
defer rdr.Close()
_, err = io.Copy(ioutil.Discard, rdr)
assert.NilError(t, err)
_, _, err = client.PluginInspectWithRaw(ctx, repo)
assert.NilError(t, err)
})
t.Run("with insecure", func(t *testing.T) {
skip.If(t, !testEnv.IsLocalDaemon())
addrs, err := net.InterfaceAddrs()
assert.NilError(t, err)
var bindTo string
for _, addr := range addrs {
ip, ok := addr.(*net.IPNet)
if !ok {
continue
}
if ip.IP.IsLoopback() || ip.IP.To4() == nil {
continue
}
bindTo = ip.IP.String()
}
if bindTo == "" {
t.Skip("No suitable interface to bind registry to")
}
regURL := bindTo + ":5000"
d := daemon.New(t)
defer d.Stop(t)
d.Start(t, "--insecure-registry="+regURL)
defer d.Stop(t)
reg := registry.NewV2(t, registry.URL(regURL))
defer reg.Close()
name := "test-" + strings.ToLower(t.Name())
repo := path.Join(regURL, name+":latest")
assert.NilError(t, plugin.CreateInRegistry(ctx, repo, nil, plugin.WithInsecureRegistry(regURL)))
client := d.NewClientT(t)
rdr, err := client.PluginInstall(ctx, repo, types.PluginInstallOptions{Disabled: true, RemoteRef: repo})
assert.NilError(t, err)
defer rdr.Close()
_, err = io.Copy(ioutil.Discard, rdr)
assert.NilError(t, err)
_, _, err = client.PluginInspectWithRaw(ctx, repo)
assert.NilError(t, err)
})
// TODO: test insecure registry with https
}
func TestPluginsWithRuntimes(t *testing.T) {
skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
skip.If(t, testEnv.IsRootless, "Test not supported on rootless due to buggy daemon setup in rootless mode due to daemon restart")
skip.If(t, testEnv.OSType == "windows")
dir, err := ioutil.TempDir("", t.Name())
assert.NilError(t, err)
defer os.RemoveAll(dir)
d := daemon.New(t)
defer d.Cleanup(t)
d.Start(t)
defer d.Stop(t)
ctx := context.Background()
client := d.NewClientT(t)
assert.NilError(t, plugin.Create(ctx, client, "test:latest"))
defer client.PluginRemove(ctx, "test:latest", types.PluginRemoveOptions{Force: true})
assert.NilError(t, client.PluginEnable(ctx, "test:latest", types.PluginEnableOptions{Timeout: 30}))
p := filepath.Join(dir, "myrt")
script := fmt.Sprintf(`#!/bin/sh
file="%s/success"
if [ "$1" = "someArg" ]; then
shift
file="${file}_someArg"
fi
touch $file
exec runc $@
`, dir)
assert.NilError(t, ioutil.WriteFile(p, []byte(script), 0777))
type config struct {
Runtimes map[string]types.Runtime `json:"runtimes"`
}
cfg, err := json.Marshal(config{
Runtimes: map[string]types.Runtime{
"myrt": {Path: p},
"myrtArgs": {Path: p, Args: []string{"someArg"}},
},
})
configPath := filepath.Join(dir, "config.json")
ioutil.WriteFile(configPath, cfg, 0644)
t.Run("No Args", func(t *testing.T) {
d.Restart(t, "--default-runtime=myrt", "--config-file="+configPath)
_, err = os.Stat(filepath.Join(dir, "success"))
assert.NilError(t, err)
})
t.Run("With Args", func(t *testing.T) {
d.Restart(t, "--default-runtime=myrtArgs", "--config-file="+configPath)
_, err = os.Stat(filepath.Join(dir, "success_someArg"))
assert.NilError(t, err)
})
}
func TestPluginBackCompatMediaTypes(t *testing.T) {
skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
skip.If(t, testEnv.OSType == "windows")
skip.If(t, testEnv.IsRootless, "Rootless has a different view of localhost (needed for test registry access)")
defer setupTest(t)()
reg := registry.NewV2(t)
defer reg.Close()
reg.WaitReady(t)
repo := path.Join(registry.DefaultURL, strings.ToLower(t.Name())+":latest")
client := testEnv.APIClient()
ctx := context.Background()
assert.NilError(t, plugin.Create(ctx, client, repo))
rdr, err := client.PluginPush(ctx, repo, "")
assert.NilError(t, err)
defer rdr.Close()
buf := &strings.Builder{}
assert.NilError(t, jsonmessage.DisplayJSONMessagesStream(rdr, buf, 0, false, nil), buf)
// Use custom header here because older versions of the registry do not
// parse the accept header correctly and does not like the accept header
// that the default resolver code uses. "Older registries" here would be
// like the one currently included in the test suite.
headers := http.Header{}
headers.Add("Accept", images.MediaTypeDockerSchema2Manifest)
resolver := docker.NewResolver(docker.ResolverOptions{
Headers: headers,
})
assert.NilError(t, err)
n, desc, err := resolver.Resolve(ctx, repo)
assert.NilError(t, err, repo)
fetcher, err := resolver.Fetcher(ctx, n)
assert.NilError(t, err)
rdr, err = fetcher.Fetch(ctx, desc)
assert.NilError(t, err)
defer rdr.Close()
type manifest struct {
MediaType string
v1.Manifest
}
var m manifest
assert.NilError(t, json.NewDecoder(rdr).Decode(&m))
assert.Check(t, cmp.Equal(m.MediaType, images.MediaTypeDockerSchema2Manifest))
assert.Check(t, cmp.Len(m.Layers, 1))
assert.Check(t, cmp.Equal(m.Layers[0].MediaType, images.MediaTypeDockerSchema2LayerGzip))
}