| package plugin |
| |
| import ( |
| "encoding/json" |
| "fmt" |
| "io" |
| "os" |
| "path/filepath" |
| |
| "github.com/Sirupsen/logrus" |
| "github.com/docker/docker/api/types" |
| "github.com/docker/docker/cli" |
| "github.com/docker/docker/cli/command" |
| "github.com/docker/docker/pkg/archive" |
| "github.com/docker/docker/reference" |
| "github.com/spf13/cobra" |
| "golang.org/x/net/context" |
| ) |
| |
| // validateTag checks if the given repoName can be resolved. |
| func validateTag(rawRepo string) error { |
| _, err := reference.ParseNamed(rawRepo) |
| |
| return err |
| } |
| |
| // validateConfig ensures that a valid config.json is available in the given path |
| func validateConfig(path string) error { |
| dt, err := os.Open(filepath.Join(path, "config.json")) |
| if err != nil { |
| return err |
| } |
| |
| m := types.PluginConfig{} |
| err = json.NewDecoder(dt).Decode(&m) |
| dt.Close() |
| |
| return err |
| } |
| |
| // validateContextDir validates the given dir and returns abs path on success. |
| func validateContextDir(contextDir string) (string, error) { |
| absContextDir, err := filepath.Abs(contextDir) |
| |
| stat, err := os.Lstat(absContextDir) |
| if err != nil { |
| return "", err |
| } |
| |
| if !stat.IsDir() { |
| return "", fmt.Errorf("context must be a directory") |
| } |
| |
| return absContextDir, nil |
| } |
| |
| type pluginCreateOptions struct { |
| repoName string |
| context string |
| compress bool |
| } |
| |
| func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command { |
| options := pluginCreateOptions{} |
| |
| cmd := &cobra.Command{ |
| Use: "create [OPTIONS] PLUGIN PLUGIN-DATA-DIR", |
| Short: "Create a plugin from a rootfs and configuration. Plugin data directory must contain config.json and rootfs directory.", |
| Args: cli.RequiresMinArgs(2), |
| RunE: func(cmd *cobra.Command, args []string) error { |
| options.repoName = args[0] |
| options.context = args[1] |
| return runCreate(dockerCli, options) |
| }, |
| } |
| |
| flags := cmd.Flags() |
| |
| flags.BoolVar(&options.compress, "compress", false, "Compress the context using gzip") |
| |
| return cmd |
| } |
| |
| func runCreate(dockerCli *command.DockerCli, options pluginCreateOptions) error { |
| var ( |
| createCtx io.ReadCloser |
| err error |
| ) |
| |
| if err := validateTag(options.repoName); err != nil { |
| return err |
| } |
| |
| absContextDir, err := validateContextDir(options.context) |
| if err != nil { |
| return err |
| } |
| |
| if err := validateConfig(options.context); err != nil { |
| return err |
| } |
| |
| compression := archive.Uncompressed |
| if options.compress { |
| logrus.Debugf("compression enabled") |
| compression = archive.Gzip |
| } |
| |
| createCtx, err = archive.TarWithOptions(absContextDir, &archive.TarOptions{ |
| Compression: compression, |
| }) |
| |
| if err != nil { |
| return err |
| } |
| |
| ctx := context.Background() |
| |
| createOptions := types.PluginCreateOptions{RepoName: options.repoName} |
| if err = dockerCli.Client().PluginCreate(ctx, createCtx, createOptions); err != nil { |
| return err |
| } |
| fmt.Fprintln(dockerCli.Out(), options.repoName) |
| return nil |
| } |