github.com/olljanat/moby@v1.13.1/cli/command/plugin/create.go (about)

     1  package plugin
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  
    10  	"github.com/Sirupsen/logrus"
    11  	"github.com/docker/docker/api/types"
    12  	"github.com/docker/docker/cli"
    13  	"github.com/docker/docker/cli/command"
    14  	"github.com/docker/docker/pkg/archive"
    15  	"github.com/docker/docker/reference"
    16  	"github.com/spf13/cobra"
    17  	"golang.org/x/net/context"
    18  )
    19  
    20  // validateTag checks if the given repoName can be resolved.
    21  func validateTag(rawRepo string) error {
    22  	_, err := reference.ParseNamed(rawRepo)
    23  
    24  	return err
    25  }
    26  
    27  // validateConfig ensures that a valid config.json is available in the given path
    28  func validateConfig(path string) error {
    29  	dt, err := os.Open(filepath.Join(path, "config.json"))
    30  	if err != nil {
    31  		return err
    32  	}
    33  
    34  	m := types.PluginConfig{}
    35  	err = json.NewDecoder(dt).Decode(&m)
    36  	dt.Close()
    37  
    38  	return err
    39  }
    40  
    41  // validateContextDir validates the given dir and returns abs path on success.
    42  func validateContextDir(contextDir string) (string, error) {
    43  	absContextDir, err := filepath.Abs(contextDir)
    44  
    45  	stat, err := os.Lstat(absContextDir)
    46  	if err != nil {
    47  		return "", err
    48  	}
    49  
    50  	if !stat.IsDir() {
    51  		return "", fmt.Errorf("context must be a directory")
    52  	}
    53  
    54  	return absContextDir, nil
    55  }
    56  
    57  type pluginCreateOptions struct {
    58  	repoName string
    59  	context  string
    60  	compress bool
    61  }
    62  
    63  func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
    64  	options := pluginCreateOptions{}
    65  
    66  	cmd := &cobra.Command{
    67  		Use:   "create [OPTIONS] PLUGIN PLUGIN-DATA-DIR",
    68  		Short: "Create a plugin from a rootfs and configuration. Plugin data directory must contain config.json and rootfs directory.",
    69  		Args:  cli.RequiresMinArgs(2),
    70  		RunE: func(cmd *cobra.Command, args []string) error {
    71  			options.repoName = args[0]
    72  			options.context = args[1]
    73  			return runCreate(dockerCli, options)
    74  		},
    75  	}
    76  
    77  	flags := cmd.Flags()
    78  
    79  	flags.BoolVar(&options.compress, "compress", false, "Compress the context using gzip")
    80  
    81  	return cmd
    82  }
    83  
    84  func runCreate(dockerCli *command.DockerCli, options pluginCreateOptions) error {
    85  	var (
    86  		createCtx io.ReadCloser
    87  		err       error
    88  	)
    89  
    90  	if err := validateTag(options.repoName); err != nil {
    91  		return err
    92  	}
    93  
    94  	absContextDir, err := validateContextDir(options.context)
    95  	if err != nil {
    96  		return err
    97  	}
    98  
    99  	if err := validateConfig(options.context); err != nil {
   100  		return err
   101  	}
   102  
   103  	compression := archive.Uncompressed
   104  	if options.compress {
   105  		logrus.Debugf("compression enabled")
   106  		compression = archive.Gzip
   107  	}
   108  
   109  	createCtx, err = archive.TarWithOptions(absContextDir, &archive.TarOptions{
   110  		Compression: compression,
   111  	})
   112  
   113  	if err != nil {
   114  		return err
   115  	}
   116  
   117  	ctx := context.Background()
   118  
   119  	createOptions := types.PluginCreateOptions{RepoName: options.repoName}
   120  	if err = dockerCli.Client().PluginCreate(ctx, createCtx, createOptions); err != nil {
   121  		return err
   122  	}
   123  	fmt.Fprintln(dockerCli.Out(), options.repoName)
   124  	return nil
   125  }