github.com/rita33cool1/iot-system-gateway@v0.0.0-20200911033302-e65bde238cc5/docker-engine/internal/test/fixtures/plugin/plugin.go (about)

     1  package plugin // import "github.com/docker/docker/internal/test/fixtures/plugin"
     2  
     3  import (
     4  	"encoding/json"
     5  	"io"
     6  	"io/ioutil"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"time"
    11  
    12  	"github.com/docker/docker/api/types"
    13  	"github.com/docker/docker/pkg/archive"
    14  	"github.com/docker/docker/plugin"
    15  	"github.com/docker/docker/registry"
    16  	"github.com/pkg/errors"
    17  	"golang.org/x/net/context"
    18  )
    19  
    20  // CreateOpt is is passed used to change the default plugin config before
    21  // creating it
    22  type CreateOpt func(*Config)
    23  
    24  // Config wraps types.PluginConfig to provide some extra state for options
    25  // extra customizations on the plugin details, such as using a custom binary to
    26  // create the plugin with.
    27  type Config struct {
    28  	*types.PluginConfig
    29  	binPath string
    30  }
    31  
    32  // WithBinary is a CreateOpt to set an custom binary to create the plugin with.
    33  // This binary must be statically compiled.
    34  func WithBinary(bin string) CreateOpt {
    35  	return func(cfg *Config) {
    36  		cfg.binPath = bin
    37  	}
    38  }
    39  
    40  // CreateClient is the interface used for `BuildPlugin` to interact with the
    41  // daemon.
    42  type CreateClient interface {
    43  	PluginCreate(context.Context, io.Reader, types.PluginCreateOptions) error
    44  }
    45  
    46  // Create creates a new plugin with the specified name
    47  func Create(ctx context.Context, c CreateClient, name string, opts ...CreateOpt) error {
    48  	tmpDir, err := ioutil.TempDir("", "create-test-plugin")
    49  	if err != nil {
    50  		return err
    51  	}
    52  	defer os.RemoveAll(tmpDir)
    53  
    54  	tar, err := makePluginBundle(tmpDir, opts...)
    55  	if err != nil {
    56  		return err
    57  	}
    58  	defer tar.Close()
    59  
    60  	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
    61  	defer cancel()
    62  
    63  	return c.PluginCreate(ctx, tar, types.PluginCreateOptions{RepoName: name})
    64  }
    65  
    66  // CreateInRegistry makes a plugin (locally) and pushes it to a registry.
    67  // This does not use a dockerd instance to create or push the plugin.
    68  // If you just want to create a plugin in some daemon, use `Create`.
    69  //
    70  // This can be useful when testing plugins on swarm where you don't really want
    71  // the plugin to exist on any of the daemons (immediately) and there needs to be
    72  // some way to distribute the plugin.
    73  func CreateInRegistry(ctx context.Context, repo string, auth *types.AuthConfig, opts ...CreateOpt) error {
    74  	tmpDir, err := ioutil.TempDir("", "create-test-plugin-local")
    75  	if err != nil {
    76  		return err
    77  	}
    78  	defer os.RemoveAll(tmpDir)
    79  
    80  	inPath := filepath.Join(tmpDir, "plugin")
    81  	if err := os.MkdirAll(inPath, 0755); err != nil {
    82  		return errors.Wrap(err, "error creating plugin root")
    83  	}
    84  
    85  	tar, err := makePluginBundle(inPath, opts...)
    86  	if err != nil {
    87  		return err
    88  	}
    89  	defer tar.Close()
    90  
    91  	dummyExec := func(m *plugin.Manager) (plugin.Executor, error) {
    92  		return nil, nil
    93  	}
    94  
    95  	regService, err := registry.NewService(registry.ServiceOptions{V2Only: true})
    96  	if err != nil {
    97  		return err
    98  	}
    99  
   100  	managerConfig := plugin.ManagerConfig{
   101  		Store:           plugin.NewStore(),
   102  		RegistryService: regService,
   103  		Root:            filepath.Join(tmpDir, "root"),
   104  		ExecRoot:        "/run/docker", // manager init fails if not set
   105  		CreateExecutor:  dummyExec,
   106  		LogPluginEvent:  func(id, name, action string) {}, // panics when not set
   107  	}
   108  	manager, err := plugin.NewManager(managerConfig)
   109  	if err != nil {
   110  		return errors.Wrap(err, "error creating plugin manager")
   111  	}
   112  
   113  	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
   114  	defer cancel()
   115  	if err := manager.CreateFromContext(ctx, tar, &types.PluginCreateOptions{RepoName: repo}); err != nil {
   116  		return err
   117  	}
   118  
   119  	if auth == nil {
   120  		auth = &types.AuthConfig{}
   121  	}
   122  	err = manager.Push(ctx, repo, nil, auth, ioutil.Discard)
   123  	return errors.Wrap(err, "error pushing plugin")
   124  }
   125  
   126  func makePluginBundle(inPath string, opts ...CreateOpt) (io.ReadCloser, error) {
   127  	p := &types.PluginConfig{
   128  		Interface: types.PluginConfigInterface{
   129  			Socket: "basic.sock",
   130  			Types:  []types.PluginInterfaceType{{Capability: "docker.dummy/1.0"}},
   131  		},
   132  		Entrypoint: []string{"/basic"},
   133  	}
   134  	cfg := &Config{
   135  		PluginConfig: p,
   136  	}
   137  	for _, o := range opts {
   138  		o(cfg)
   139  	}
   140  	if cfg.binPath == "" {
   141  		binPath, err := ensureBasicPluginBin()
   142  		if err != nil {
   143  			return nil, err
   144  		}
   145  		cfg.binPath = binPath
   146  	}
   147  
   148  	configJSON, err := json.Marshal(p)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	if err := ioutil.WriteFile(filepath.Join(inPath, "config.json"), configJSON, 0644); err != nil {
   153  		return nil, err
   154  	}
   155  	if err := os.MkdirAll(filepath.Join(inPath, "rootfs", filepath.Dir(p.Entrypoint[0])), 0755); err != nil {
   156  		return nil, errors.Wrap(err, "error creating plugin rootfs dir")
   157  	}
   158  
   159  	// Ensure the mount target paths exist
   160  	for _, m := range p.Mounts {
   161  		var stat os.FileInfo
   162  		if m.Source != nil {
   163  			stat, err = os.Stat(*m.Source)
   164  			if err != nil && !os.IsNotExist(err) {
   165  				return nil, err
   166  			}
   167  		}
   168  
   169  		if stat == nil || stat.IsDir() {
   170  			var mode os.FileMode = 0755
   171  			if stat != nil {
   172  				mode = stat.Mode()
   173  			}
   174  			if err := os.MkdirAll(filepath.Join(inPath, "rootfs", m.Destination), mode); err != nil {
   175  				return nil, errors.Wrap(err, "error preparing plugin mount destination path")
   176  			}
   177  		} else {
   178  			if err := os.MkdirAll(filepath.Join(inPath, "rootfs", filepath.Dir(m.Destination)), 0755); err != nil {
   179  				return nil, errors.Wrap(err, "error preparing plugin mount destination dir")
   180  			}
   181  			f, err := os.Create(filepath.Join(inPath, "rootfs", m.Destination))
   182  			if err != nil && !os.IsExist(err) {
   183  				return nil, errors.Wrap(err, "error preparing plugin mount destination file")
   184  			}
   185  			if f != nil {
   186  				f.Close()
   187  			}
   188  		}
   189  	}
   190  	if err := archive.NewDefaultArchiver().CopyFileWithTar(cfg.binPath, filepath.Join(inPath, "rootfs", p.Entrypoint[0])); err != nil {
   191  		return nil, errors.Wrap(err, "error copying plugin binary to rootfs path")
   192  	}
   193  	tar, err := archive.Tar(inPath, archive.Uncompressed)
   194  	return tar, errors.Wrap(err, "error making plugin archive")
   195  }
   196  
   197  func ensureBasicPluginBin() (string, error) {
   198  	name := "docker-basic-plugin"
   199  	p, err := exec.LookPath(name)
   200  	if err == nil {
   201  		return p, nil
   202  	}
   203  
   204  	goBin, err := exec.LookPath("go")
   205  	if err != nil {
   206  		return "", err
   207  	}
   208  	installPath := filepath.Join(os.Getenv("GOPATH"), "bin", name)
   209  	sourcePath := filepath.Join("github.com", "docker", "docker", "internal", "test", "fixtures", "plugin", "basic")
   210  	cmd := exec.Command(goBin, "build", "-o", installPath, sourcePath)
   211  	cmd.Env = append(cmd.Env, "GOPATH="+os.Getenv("GOPATH"), "CGO_ENABLED=0")
   212  	if out, err := cmd.CombinedOutput(); err != nil {
   213  		return "", errors.Wrapf(err, "error building basic plugin bin: %s", string(out))
   214  	}
   215  	return installPath, nil
   216  }