From 3dac4df8032149abee48d9429bb30263c22a1637 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Wed, 8 Apr 2020 16:21:02 +0200 Subject: [PATCH 1/4] Implement context loading Signed-off-by: Nicolas De Loof --- cmd/main.go | 10 ++++++++ context/config.go | 38 +++++++++++++++++++++++++++ context/flags.go | 33 ++++++++++++++++++++++++ context/store.go | 65 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 context/config.go create mode 100644 context/flags.go create mode 100644 context/store.go diff --git a/cmd/main.go b/cmd/main.go index 986b32722..3e247a46e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -32,6 +32,7 @@ import ( "os" "path/filepath" + "github.com/docker/api/context" "github.com/sirupsen/logrus" "github.com/urfave/cli" ) @@ -57,11 +58,20 @@ func main() { Name: "debug", Usage: "enable debug output in the logs", }, + context.ConfigFlag, + context.ContextFlag, } app.Before = func(clix *cli.Context) error { if clix.GlobalBool("debug") { logrus.SetLevel(logrus.DebugLevel) } + + context, err := context.GetContext() + if err != nil { + return err + } + fmt.Println(context.Metadata.Type) + // TODO select backend based on context.Metadata.Type or delegate to legacy CLI if == "Moby" return nil } app.Commands = []cli.Command{ diff --git a/context/config.go b/context/config.go new file mode 100644 index 000000000..0a858e24c --- /dev/null +++ b/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"` +} diff --git a/context/flags.go b/context/flags.go new file mode 100644 index 000000000..b335cbc4a --- /dev/null +++ b/context/flags.go @@ -0,0 +1,33 @@ +package context + +import ( + "path/filepath" + + "github.com/docker/docker/pkg/homedir" + "github.com/urfave/cli" +) + +const ( + // ConfigFileName is the name of config file + ConfigFileName = "config.json" + configFileDir = ".docker" +) + +var ( + ConfigDir string + ConfigFlag = cli.StringFlag{ + Name: "config", + Usage: "Location of client config files", + EnvVar: "DOCKER_CONFIG", + Value: filepath.Join(homedir.Get(), configFileDir), + Destination: &ConfigDir, + } + + ContextName string + ContextFlag = cli.StringFlag{ + Name: "context, c", + Usage: `Name of the 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, + } +) diff --git a/context/store.go b/context/store.go new file mode 100644 index 000000000..8c298fd28 --- /dev/null +++ b/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() +} From a8403241e49170ec0235bc4f18464e99e51a4a9c Mon Sep 17 00:00:00 2001 From: Guillaume Lours Date: Wed, 15 Apr 2020 16:55:30 +0200 Subject: [PATCH 2/4] Add default shellout to engine if no context specified Improve CLI documentation Signed-off-by: Guillaume Lours --- cmd/main.go | 30 +++++++++++++++++++++++++----- context/flags.go | 4 ++-- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 3e247a46e..56508950e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -30,7 +30,9 @@ package main import ( "fmt" "os" + "os/exec" "path/filepath" + "sort" "github.com/docker/api/context" "github.com/sirupsen/logrus" @@ -61,25 +63,43 @@ func main() { context.ConfigFlag, context.ContextFlag, } + + app.Before = func(clix *cli.Context) error { if clix.GlobalBool("debug") { logrus.SetLevel(logrus.DebugLevel) } - - context, err := context.GetContext() + ctx, err := context.GetContext() if err != nil { - return err + logrus.Fatal(err) } - fmt.Println(context.Metadata.Type) - // TODO select backend based on context.Metadata.Type or delegate to legacy CLI if == "Moby" + if ctx.Metadata.Type == "Moby" { + shellOutToDefaultEngine() + os.Exit(0) + } + // 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 { + logrus.Fatal(err) + } +} diff --git a/context/flags.go b/context/flags.go index b335cbc4a..ce9d411fb 100644 --- a/context/flags.go +++ b/context/flags.go @@ -17,7 +17,7 @@ var ( ConfigDir string ConfigFlag = cli.StringFlag{ Name: "config", - Usage: "Location of client config files", + Usage: "Location of client config files `DIRECTORY`", EnvVar: "DOCKER_CONFIG", Value: filepath.Join(homedir.Get(), configFileDir), Destination: &ConfigDir, @@ -26,7 +26,7 @@ var ( ContextName string ContextFlag = cli.StringFlag{ Name: "context, c", - Usage: `Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var and default context set with "docker context use")`, + 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, } From f5aa9638af86bc38329a0f65d84c101907d839b1 Mon Sep 17 00:00:00 2001 From: Guillaume Lours Date: Mon, 20 Apr 2020 17:16:37 +0200 Subject: [PATCH 3/4] Add default shellout to engine if no context specified Signed-off-by: Guillaume Lours --- cmd/main.go | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 56508950e..bb0daec5e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -64,6 +64,24 @@ func main() { context.ContextFlag, } + /*cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) { + ctx, err := context.GetContext() + if err != nil { + logrus.Fatal(err) + } + fmt.Println(ctx.Metadata.Type) + if ctx.Metadata.Type == "Moby" { + err := shellOutToDefaultEngine() + if err != nil { + if exiterr, ok:= err.(*exec.ExitError); ok { + os.Exit(exiterr.ExitCode()) + } + os.Exit(1) + } + } else { + fmt.Fprintf(w, templ, data) + } + }*/ app.Before = func(clix *cli.Context) error { if clix.GlobalBool("debug") { @@ -74,7 +92,13 @@ func main() { logrus.Fatal(err) } if ctx.Metadata.Type == "Moby" { - shellOutToDefaultEngine() + err := shellOutToDefaultEngine() + if err != nil { + if exiterr, ok:= err.(*exec.ExitError); ok { + os.Exit(exiterr.ExitCode()) + } + os.Exit(1) + } os.Exit(0) } // TODO select backend based on context.Metadata.Type @@ -94,12 +118,14 @@ func main() { } } -func shellOutToDefaultEngine() { - cmd :=exec.Command("/Applications/Docker.app/Contents/Resources/bin/docker", os.Args[1:]...) +func shellOutToDefaultEngine() error { + 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 { - logrus.Fatal(err) + fmt.Println("Shellout") + if err:= cmd.Run(); err != nil { + return err } + return cmd.Wait() } From 4b2c8ae9cfdc304cedd4df9c96b04277d4251657 Mon Sep 17 00:00:00 2001 From: Djordje Lukic Date: Mon, 20 Apr 2020 18:06:38 +0200 Subject: [PATCH 4/4] Call original docker on moby context Signed-off-by: Guillaume Lours --- cmd/main.go | 46 +++++++++++++++++++++------------------------- context/flags.go | 15 ++++++++++----- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index bb0daec5e..026ebee34 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -29,6 +29,7 @@ package main import ( "fmt" + "io" "os" "os/exec" "path/filepath" @@ -64,24 +65,22 @@ func main() { context.ContextFlag, } - /*cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) { + // 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) } - fmt.Println(ctx.Metadata.Type) if ctx.Metadata.Type == "Moby" { - err := shellOutToDefaultEngine() - if err != nil { - if exiterr, ok:= err.(*exec.ExitError); ok { - os.Exit(exiterr.ExitCode()) - } - os.Exit(1) - } + shellOutToDefaultEngine() } else { - fmt.Fprintf(w, templ, data) + originalHelpPrinter(w, templ, data) } - }*/ + } app.Before = func(clix *cli.Context) error { if clix.GlobalBool("debug") { @@ -92,14 +91,7 @@ func main() { logrus.Fatal(err) } if ctx.Metadata.Type == "Moby" { - err := shellOutToDefaultEngine() - if err != nil { - if exiterr, ok:= err.(*exec.ExitError); ok { - os.Exit(exiterr.ExitCode()) - } - os.Exit(1) - } - os.Exit(0) + shellOutToDefaultEngine() } // TODO select backend based on context.Metadata.Type return nil @@ -118,14 +110,18 @@ func main() { } } -func shellOutToDefaultEngine() error { - cmd :=exec.Command(" /Applications/Docker.app/Contents/Resources/bin/docker", os.Args[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 - fmt.Println("Shellout") - if err:= cmd.Run(); err != nil { - return err + if err := cmd.Run(); err != nil { + if err != nil { + if exiterr, ok := err.(*exec.ExitError); ok { + os.Exit(exiterr.ExitCode()) + } + os.Exit(1) + } } - return cmd.Wait() + os.Exit(0) } diff --git a/context/flags.go b/context/flags.go index ce9d411fb..0456704f7 100644 --- a/context/flags.go +++ b/context/flags.go @@ -3,7 +3,7 @@ package context import ( "path/filepath" - "github.com/docker/docker/pkg/homedir" + "github.com/mitchellh/go-homedir" "github.com/urfave/cli" ) @@ -14,16 +14,16 @@ const ( ) var ( - ConfigDir string - ConfigFlag = cli.StringFlag{ + ConfigDir string + ContextName string + ConfigFlag = cli.StringFlag{ Name: "config", Usage: "Location of client config files `DIRECTORY`", EnvVar: "DOCKER_CONFIG", - Value: filepath.Join(homedir.Get(), configFileDir), + Value: filepath.Join(home(), configFileDir), Destination: &ConfigDir, } - ContextName string 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\")", @@ -31,3 +31,8 @@ var ( Destination: &ContextName, } ) + +func home() string { + home, _ := homedir.Dir() + return home +}