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