瀏覽代碼

Merge pull request #9 from glours/default_to_cli_only

Fallback to default CLI
Guillaume Lours 5 年之前
父節點
當前提交
b24985af56
共有 4 個文件被更改,包括 193 次插入0 次删除
  1. 52 0
      cmd/main.go
  2. 38 0
      context/config.go
  3. 38 0
      context/flags.go
  4. 65 0
      context/store.go

+ 52 - 0
cmd/main.go

@@ -29,9 +29,13 @@ package main
 
 import (
 	"fmt"
+	"io"
 	"os"
+	"os/exec"
 	"path/filepath"
+	"sort"
 
+	"github.com/docker/api/context"
 	"github.com/sirupsen/logrus"
 	"github.com/urfave/cli"
 )
@@ -57,19 +61,67 @@ func main() {
 			Name:  "debug",
 			Usage: "enable debug output in the logs",
 		},
+		context.ConfigFlag,
+		context.ContextFlag,
 	}
+
+	// Make a copy of the default HelpPrinter function
+	originalHelpPrinter := cli.HelpPrinter
+	// Change the HelpPrinter function to shell out to the Moby CLI help
+	// when the current context is pointing to Docker engine
+	// else we use the copy of the original HelpPrinter
+	cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
+		ctx, err := context.GetContext()
+		if err != nil {
+			logrus.Fatal(err)
+		}
+		if ctx.Metadata.Type == "Moby" {
+			shellOutToDefaultEngine()
+		} else {
+			originalHelpPrinter(w, templ, data)
+		}
+	}
+
 	app.Before = func(clix *cli.Context) error {
 		if clix.GlobalBool("debug") {
 			logrus.SetLevel(logrus.DebugLevel)
 		}
+		ctx, err := context.GetContext()
+		if err != nil {
+			logrus.Fatal(err)
+		}
+		if ctx.Metadata.Type == "Moby" {
+			shellOutToDefaultEngine()
+		}
+		// TODO select backend based on context.Metadata.Type
 		return nil
 	}
 	app.Commands = []cli.Command{
 		contextCommand,
 		exampleCommand,
 	}
+
+	sort.Sort(cli.FlagsByName(app.Flags))
+	sort.Sort(cli.CommandsByName(app.Commands))
+
 	if err := app.Run(os.Args); err != nil {
 		fmt.Fprintln(os.Stderr, err)
 		os.Exit(1)
 	}
 }
+
+func shellOutToDefaultEngine() {
+	cmd := exec.Command("/Applications/Docker.app/Contents/Resources/bin/docker", os.Args[1:]...)
+	cmd.Stdin = os.Stdin
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	if err := cmd.Run(); err != nil {
+		if err != nil {
+			if exiterr, ok := err.(*exec.ExitError); ok {
+				os.Exit(exiterr.ExitCode())
+			}
+			os.Exit(1)
+		}
+	}
+	os.Exit(0)
+}

+ 38 - 0
context/config.go

@@ -0,0 +1,38 @@
+package context
+
+import (
+	"encoding/json"
+	"fmt"
+	"os"
+	"path/filepath"
+)
+
+func LoadConfigFile() (*ConfigFile, error) {
+	filename := filepath.Join(ConfigDir, ConfigFileName)
+	configFile := &ConfigFile{
+		Filename: filename,
+	}
+
+	if _, err := os.Stat(filename); err == nil {
+		file, err := os.Open(filename)
+		if err != nil {
+			return nil, fmt.Errorf("can't read %s: %w", filename, err)
+		}
+		defer file.Close()
+		err = json.NewDecoder(file).Decode(&configFile)
+		if err != nil {
+			err = fmt.Errorf("can't read %s: %w", filename, err)
+		}
+		return configFile, err
+	} else if !os.IsNotExist(err) {
+		// if file is there but we can't stat it for any reason other
+		// than it doesn't exist then stop
+		return nil, fmt.Errorf("can't read %s: %w", filename, err)
+	}
+	return configFile, nil
+}
+
+type ConfigFile struct {
+	Filename       string `json:"-"` // Note: for internal use only
+	CurrentContext string `json:"currentContext,omitempty"`
+}

+ 38 - 0
context/flags.go

@@ -0,0 +1,38 @@
+package context
+
+import (
+	"path/filepath"
+
+	"github.com/mitchellh/go-homedir"
+	"github.com/urfave/cli"
+)
+
+const (
+	// ConfigFileName is the name of config file
+	ConfigFileName = "config.json"
+	configFileDir  = ".docker"
+)
+
+var (
+	ConfigDir   string
+	ContextName string
+	ConfigFlag  = cli.StringFlag{
+		Name:        "config",
+		Usage:       "Location of client config files `DIRECTORY`",
+		EnvVar:      "DOCKER_CONFIG",
+		Value:       filepath.Join(home(), configFileDir),
+		Destination: &ConfigDir,
+	}
+
+	ContextFlag = cli.StringFlag{
+		Name:        "context, c",
+		Usage:       "Name of the context `CONTEXT` to use to connect to the daemon (overrides DOCKER_HOST env var and default context set with \"docker context use\")",
+		EnvVar:      "DOCKER_CONTEXT",
+		Destination: &ContextName,
+	}
+)
+
+func home() string {
+	home, _ := homedir.Dir()
+	return home
+}

+ 65 - 0
context/store.go

@@ -0,0 +1,65 @@
+package context
+
+import (
+	"encoding/json"
+	"io/ioutil"
+	"path/filepath"
+
+	"github.com/opencontainers/go-digest"
+)
+
+const (
+	contextsDir = "contexts"
+	metadataDir = "meta"
+	metaFile    = "meta.json"
+)
+
+// ContextStoreDir returns the directory the docker contexts are stored in
+func ContextStoreDir() string {
+	return filepath.Join(ConfigDir, contextsDir)
+}
+
+type Metadata struct {
+	Name      string                 `json:",omitempty"`
+	Metadata  TypeContext            `json:",omitempty"`
+	Endpoints map[string]interface{} `json:",omitempty"`
+}
+
+type TypeContext struct {
+	Type string
+}
+
+func GetContext() (*Metadata, error) {
+	config, err := LoadConfigFile()
+	if err != nil {
+		return nil, err
+	}
+	r := &Metadata{
+		Endpoints: make(map[string]interface{}),
+	}
+
+	if ContextName == "" {
+		ContextName = config.CurrentContext
+	}
+	if ContextName == "" || ContextName == "default" {
+		r.Metadata.Type = "Moby"
+		return r, nil
+	}
+
+	meta := filepath.Join(ConfigDir, contextsDir, metadataDir, contextdirOf(ContextName), metaFile)
+	bytes, err := ioutil.ReadFile(meta)
+	if err != nil {
+		return nil, err
+	}
+
+	if err := json.Unmarshal(bytes, r); err != nil {
+		return r, err
+	}
+
+	r.Name = ContextName
+	return r, nil
+}
+
+func contextdirOf(name string) string {
+	return digest.FromString(name).Encoded()
+}