github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/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/distribution/reference"
    12  	"github.com/docker/docker/api/types"
    13  	"github.com/docker/docker/cli"
    14  	"github.com/docker/docker/cli/command"
    15  	"github.com/docker/docker/pkg/archive"
    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.ParseNormalizedNamed(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  	if err != nil {
    45  		return "", err
    46  	}
    47  	stat, err := os.Lstat(absContextDir)
    48  	if err != nil {
    49  		return "", err
    50  	}
    51  
    52  	if !stat.IsDir() {
    53  		return "", fmt.Errorf("context must be a directory")
    54  	}
    55  
    56  	return absContextDir, nil
    57  }
    58  
    59  type pluginCreateOptions struct {
    60  	repoName string
    61  	context  string
    62  	compress bool
    63  }
    64  
    65  func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
    66  	options := pluginCreateOptions{}
    67  
    68  	cmd := &cobra.Command{
    69  		Use:   "create [OPTIONS] PLUGIN PLUGIN-DATA-DIR",
    70  		Short: "Create a plugin from a rootfs and configuration. Plugin data directory must contain config.json and rootfs directory.",
    71  		Args:  cli.RequiresMinArgs(2),
    72  		RunE: func(cmd *cobra.Command, args []string) error {
    73  			options.repoName = args[0]
    74  			options.context = args[1]
    75  			return runCreate(dockerCli, options)
    76  		},
    77  	}
    78  
    79  	flags := cmd.Flags()
    80  
    81  	flags.BoolVar(&options.compress, "compress", false, "Compress the context using gzip")
    82  
    83  	return cmd
    84  }
    85  
    86  func runCreate(dockerCli *command.DockerCli, options pluginCreateOptions) error {
    87  	var (
    88  		createCtx io.ReadCloser
    89  		err       error
    90  	)
    91  
    92  	if err := validateTag(options.repoName); err != nil {
    93  		return err
    94  	}
    95  
    96  	absContextDir, err := validateContextDir(options.context)
    97  	if err != nil {
    98  		return err
    99  	}
   100  
   101  	if err := validateConfig(options.context); err != nil {
   102  		return err
   103  	}
   104  
   105  	compression := archive.Uncompressed
   106  	if options.compress {
   107  		logrus.Debugf("compression enabled")
   108  		compression = archive.Gzip
   109  	}
   110  
   111  	createCtx, err = archive.TarWithOptions(absContextDir, &archive.TarOptions{
   112  		Compression: compression,
   113  	})
   114  
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	ctx := context.Background()
   120  
   121  	createOptions := types.PluginCreateOptions{RepoName: options.repoName}
   122  	if err = dockerCli.Client().PluginCreate(ctx, createCtx, createOptions); err != nil {
   123  		return err
   124  	}
   125  	fmt.Fprintln(dockerCli.Out(), options.repoName)
   126  	return nil
   127  }