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  }