github.1git.de/docker/cli@v26.1.3+incompatible/e2e/testutils/plugins.go (about)

     1  package testutils
     2  
     3  import (
     4  	"context"
     5  	"crypto/rand"
     6  	"embed"
     7  	"encoding/base64"
     8  	"encoding/json"
     9  	"fmt"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"testing"
    14  
    15  	"github.com/docker/docker/api/types"
    16  	"github.com/pkg/errors"
    17  	"gotest.tools/v3/assert"
    18  	"gotest.tools/v3/fs"
    19  	"gotest.tools/v3/icmd"
    20  )
    21  
    22  //go:embed plugins/*
    23  var plugins embed.FS
    24  
    25  // SetupPlugin builds a plugin and creates a temporary
    26  // directory with the plugin's config.json and rootfs.
    27  func SetupPlugin(t *testing.T, ctx context.Context) *fs.Dir {
    28  	t.Helper()
    29  
    30  	p := &types.PluginConfig{
    31  		Linux: types.PluginConfigLinux{
    32  			Capabilities: []string{"CAP_SYS_ADMIN"},
    33  		},
    34  		Interface: types.PluginConfigInterface{
    35  			Socket: "basic.sock",
    36  			Types:  []types.PluginInterfaceType{{Capability: "docker.dummy/1.0"}},
    37  		},
    38  		Entrypoint: []string{"/basic"},
    39  	}
    40  	configJSON, err := json.Marshal(p)
    41  	assert.NilError(t, err)
    42  
    43  	binPath, err := buildPlugin(t, ctx)
    44  	assert.NilError(t, err)
    45  
    46  	dir := fs.NewDir(t, "plugin_test",
    47  		fs.WithFile("config.json", string(configJSON), fs.WithMode(0o644)),
    48  		fs.WithDir("rootfs", fs.WithMode(0o755)),
    49  	)
    50  
    51  	icmd.RunCommand("/bin/cp", binPath, dir.Join("rootfs", p.Entrypoint[0])).Assert(t, icmd.Success)
    52  	return dir
    53  }
    54  
    55  // buildPlugin uses Go to build a plugin from one of the source files in the plugins directory.
    56  // It returns the path to the built plugin binary.
    57  // To allow for multiple plugins to be built in parallel, the plugin is compiled with a unique
    58  // identifier in the binary. This is done by setting a linker flag with the -ldflags option.
    59  func buildPlugin(t *testing.T, ctx context.Context) (string, error) {
    60  	t.Helper()
    61  
    62  	randomName, err := randomString()
    63  	if err != nil {
    64  		return "", err
    65  	}
    66  
    67  	goBin, err := exec.LookPath("/usr/local/go/bin/go")
    68  	if err != nil {
    69  		return "", err
    70  	}
    71  	installPath := filepath.Join(os.Getenv("GOPATH"), "bin", randomName)
    72  
    73  	pluginContent, err := plugins.ReadFile("plugins/basic.go")
    74  	if err != nil {
    75  		return "", err
    76  	}
    77  	dir := fs.NewDir(t, "plugin_build")
    78  	if err := os.WriteFile(dir.Join("main.go"), pluginContent, 0o644); err != nil {
    79  		return "", err
    80  	}
    81  	defer dir.Remove()
    82  
    83  	cmd := exec.CommandContext(ctx, goBin, "build", "-ldflags",
    84  		fmt.Sprintf("-X 'main.UNIQUEME=%s'", randomName),
    85  		"-o", installPath, dir.Join("main.go"))
    86  
    87  	cmd.Env = append(os.Environ(), "CGO_ENABLED=0")
    88  
    89  	if out, err := cmd.CombinedOutput(); err != nil {
    90  		return "", errors.Wrapf(err, "error building basic plugin bin: %s", string(out))
    91  	}
    92  
    93  	return installPath, nil
    94  }
    95  
    96  func randomString() (string, error) {
    97  	b := make([]byte, 8)
    98  	if _, err := rand.Read(b); err != nil {
    99  		return "", err
   100  	}
   101  	return base64.StdEncoding.EncodeToString(b), nil
   102  }