1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00
moby--moby/cli/command/plugin/create.go
Tonis Tiigi 3d86b0c79b Implement content addressability for plugins
Move plugins to shared distribution stack with images.

Create immutable plugin config that matches schema2 requirements.

Ensure data being pushed is same as pulled/created.

Store distribution artifacts in a blobstore.

Run init layer setup for every plugin start.

Fix breakouts from unsafe file accesses.

Add support for `docker plugin install --alias`

Uses normalized references for default names to avoid collisions when using default hosts/tags.

Some refactoring of the plugin manager to support the change, like removing the singleton manager and adding manager config struct.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
Signed-off-by: Derek McGowan <derek@mcgstyle.net>
2016-12-23 13:29:58 -08:00

125 lines
2.8 KiB
Go

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
}