mirror of https://github.com/docker/cli.git
129 lines
2.8 KiB
Go
129 lines
2.8 KiB
Go
package plugin
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/docker/cli/cli"
|
|
"github.com/docker/cli/cli/command"
|
|
"github.com/docker/distribution/reference"
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/pkg/archive"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
// validateTag checks if the given repoName can be resolved.
|
|
func validateTag(rawRepo string) error {
|
|
_, err := reference.ParseNormalizedNamed(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)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
stat, err := os.Lstat(absContextDir)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if !stat.IsDir() {
|
|
return "", errors.Errorf("context must be a directory")
|
|
}
|
|
|
|
return absContextDir, nil
|
|
}
|
|
|
|
type pluginCreateOptions struct {
|
|
repoName string
|
|
context string
|
|
compress bool
|
|
}
|
|
|
|
func newCreateCommand(dockerCli command.Cli) *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.Cli, 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
|
|
}
|