-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Simon Ferquel <[email protected]>
- Loading branch information
1 parent
f27f51f
commit a08fd72
Showing
11 changed files
with
617 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package context | ||
|
||
import ( | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/docker/cli/cli" | ||
"github.com/docker/cli/cli/command" | ||
) | ||
|
||
// NewContextCommand returns the context cli subcommand | ||
func NewContextCommand(dockerCli command.Cli) *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "context", | ||
Aliases: []string{"ctx"}, | ||
Short: "Manage contexts", | ||
Args: cli.NoArgs, | ||
RunE: command.ShowHelp(dockerCli.Err()), | ||
} | ||
cmd.AddCommand( | ||
newCreateCommand(dockerCli), | ||
newListCommand(dockerCli), | ||
newUseCommand(dockerCli), | ||
newExportCommand(dockerCli), | ||
newImportCommand(dockerCli), | ||
newRemoveCommand(dockerCli), | ||
) | ||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
package context | ||
|
||
import ( | ||
"io/ioutil" | ||
"os" | ||
|
||
"github.com/docker/cli/cli" | ||
"github.com/docker/cli/cli/command" | ||
"github.com/docker/cli/cli/context/docker" | ||
"github.com/docker/cli/cli/context/kubernetes" | ||
"github.com/docker/cli/cli/context/store" | ||
"github.com/pkg/errors" | ||
"github.com/spf13/cobra" | ||
"github.com/spf13/pflag" | ||
) | ||
|
||
type createOptions struct { | ||
name string | ||
description string // --description string: set the description | ||
defaultStackOrchestrator string | ||
docker dockerEndpointOptions | ||
kubernetes kubernetedEndpointOptions | ||
} | ||
|
||
func (o *createOptions) addFlags(flags *pflag.FlagSet) { | ||
flags.StringVar(&o.description, "description", "", "set the description of the context") | ||
flags.StringVar(&o.defaultStackOrchestrator, "default-stack-orchestrator", "", "set the default orchestrator for stack operations if different to the default one, to use with this context (swarm|kubernetes|all)") | ||
o.docker.addFlags(flags) | ||
o.kubernetes.addFlags(flags) | ||
} | ||
|
||
type dockerEndpointOptions struct { | ||
host string //--moby-host string: set moby endpoint host (same format as DOCKER_HOST) | ||
apiVersion string | ||
ca string //--moby-ca string: path to a CA file | ||
cert string //--moby-cert string: path to a client cert file | ||
key string //--moby-key string: path to a client key file | ||
skipTLSVerify bool | ||
} | ||
|
||
func (o *dockerEndpointOptions) addFlags(flags *pflag.FlagSet) { | ||
flags.StringVar(&o.host, "docker-host", "", "required: specify the docker endpoint on wich to connect") | ||
flags.StringVar(&o.apiVersion, "docker-api-version", "", "override negociated api version") | ||
flags.StringVar(&o.ca, "docker-tls-ca", "", "path to the ca file to validate docker endpoint") | ||
flags.StringVar(&o.cert, "docker-tls-cert", "", "path to the cert file to authenticate to the docker endpoint") | ||
flags.StringVar(&o.key, "docker-tls-key", "", "path to the key file to authenticate to the docker endpoint") | ||
flags.BoolVar(&o.skipTLSVerify, "docker-tls-skip-verify", false, "skip tls verify when connecting to the docker endpoint") | ||
} | ||
|
||
func (o *dockerEndpointOptions) toEndpoint(contextName string) (docker.Endpoint, error) { | ||
var ( | ||
ca, key, cert []byte | ||
err error | ||
tlsData *docker.TLSData | ||
) | ||
if o.ca != "" { | ||
if ca, err = ioutil.ReadFile(o.ca); err != nil { | ||
return docker.Endpoint{}, err | ||
} | ||
} | ||
if o.cert != "" { | ||
if cert, err = ioutil.ReadFile(o.cert); err != nil { | ||
return docker.Endpoint{}, err | ||
} | ||
} | ||
if o.key != "" { | ||
if key, err = ioutil.ReadFile(o.key); err != nil { | ||
return docker.Endpoint{}, err | ||
} | ||
} | ||
if ca != nil || key != nil || cert != nil { | ||
tlsData = &docker.TLSData{ | ||
CA: ca, | ||
Cert: cert, | ||
Key: key, | ||
} | ||
} | ||
return docker.Endpoint{ | ||
EndpointMeta: docker.EndpointMeta{ | ||
APIVersion: o.apiVersion, | ||
ContextName: contextName, | ||
Host: o.host, | ||
SkipTLSVerify: o.skipTLSVerify, | ||
}, | ||
TLSData: tlsData, | ||
}, nil | ||
} | ||
|
||
type kubernetedEndpointOptions struct { | ||
server string //--kubernetes-server string: kubernetes api server address | ||
ca string //--kubernetes-ca string: path to a CA file | ||
cert string //--kubernetes-cert string: path to a client cert file | ||
key string //--kubernetes-key string: path to a client key file | ||
skipTLSVerify bool | ||
defaultNamespace string | ||
kubeconfigFile string //--kubernetes-kubeconfig-file string: path to a kubernetes cli config file | ||
kubeconfigContext string //--kubernetes-kubeconfig-context string: name of the kubernetes cli config file context to use | ||
} | ||
|
||
func (o *kubernetedEndpointOptions) addFlags(flags *pflag.FlagSet) { | ||
flags.StringVar(&o.server, "kubernetes-host", "", "required: specify the docker endpoint on wich to connect") | ||
flags.StringVar(&o.ca, "kubernetes-tls-ca", "", "path to the ca file to validate kubernetes endpoint") | ||
flags.StringVar(&o.cert, "kubernetes-tls-cert", "", "path to the cert file to authenticate to the kubernetes endpoint") | ||
flags.StringVar(&o.key, "kubernetes-tls-key", "", "path to the key file to authenticate to the kubernetes endpoint") | ||
flags.BoolVar(&o.skipTLSVerify, "kubernetes-tls-skip-verify", false, "skip tls verify when connecting to the kubernetes endpoint") | ||
flags.StringVar(&o.defaultNamespace, "kubernetes-default-namespace", "default", "override default namespace when connecting to kubernetes endpoint") | ||
flags.StringVar(&o.kubeconfigFile, "kubernetes-kubeconfig", "", "path to an existing kubeconfig file") | ||
flags.StringVar(&o.kubeconfigContext, "kubernetes-kubeconfig-context", "", `context to use in the kubeconfig file referenced in "kubernetes-kubeconfig"`) | ||
} | ||
|
||
func (o *kubernetedEndpointOptions) toEndpoint(contextName string) (*kubernetes.Endpoint, error) { | ||
if o.kubeconfigFile != "" { | ||
ep, err := kubernetes.FromKubeConfig(contextName, o.kubeconfigFile, o.kubeconfigContext, o.defaultNamespace) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &ep, nil | ||
} | ||
if o.server != "" { | ||
var ( | ||
ca, key, cert []byte | ||
err error | ||
tlsData *kubernetes.TLSData | ||
) | ||
if o.ca != "" { | ||
if ca, err = ioutil.ReadFile(o.ca); err != nil { | ||
return nil, err | ||
} | ||
} | ||
if o.cert != "" { | ||
if cert, err = ioutil.ReadFile(o.cert); err != nil { | ||
return nil, err | ||
} | ||
} | ||
if o.key != "" { | ||
if key, err = ioutil.ReadFile(o.key); err != nil { | ||
return nil, err | ||
} | ||
} | ||
if ca != nil || key != nil || cert != nil { | ||
tlsData = &kubernetes.TLSData{ | ||
CA: ca, | ||
Cert: cert, | ||
Key: key, | ||
} | ||
} | ||
return &kubernetes.Endpoint{ | ||
EndpointMeta: kubernetes.EndpointMeta{ | ||
ContextName: contextName, | ||
DefaultNamespace: o.defaultNamespace, | ||
Server: o.server, | ||
SkipTLSVerify: o.skipTLSVerify, | ||
}, | ||
TLSData: tlsData, | ||
}, nil | ||
} | ||
return nil, nil | ||
} | ||
|
||
func loadFileIfNotEmpty(path string) ([]byte, error) { | ||
if path == "" { | ||
return nil, nil | ||
} | ||
return ioutil.ReadFile(path) | ||
} | ||
|
||
func (o *createOptions) process(s store.Store) error { | ||
if _, err := s.GetContextMetadata(o.name); !os.IsNotExist(err) { | ||
if err != nil { | ||
return errors.Wrap(err, "error while getting existing contexts") | ||
} | ||
return errors.Errorf("context %q already exists", o.name) | ||
} | ||
stackOrchestrator, err := command.NormalizeOrchestrator(o.defaultStackOrchestrator) | ||
if err != nil { | ||
return errors.Wrap(err, "unable to parse default-stack-orchestrator") | ||
} | ||
dockerEP, err := o.docker.toEndpoint(o.name) | ||
if err != nil { | ||
return errors.Wrap(err, "unable to create docker endpoint config") | ||
} | ||
if err := docker.Save(s, dockerEP); err != nil { | ||
return errors.Wrap(err, "unable to save docker endpoint config") | ||
} | ||
kubernetesEP, err := o.kubernetes.toEndpoint(o.name) | ||
if err != nil { | ||
return errors.Wrap(err, "unable to create kubernetes endpoint config") | ||
} | ||
if kubernetesEP != nil { | ||
if err := kubernetes.Save(s, *kubernetesEP); err != nil { | ||
return errors.Wrap(err, "unable to save kubernetes endpoint config") | ||
} | ||
} | ||
|
||
// at this point, the context should exist with endpoints configuration | ||
ctx, err := s.GetContextMetadata(o.name) | ||
if err != nil { | ||
return errors.Wrap(err, "error while getting context") | ||
} | ||
command.SetContextMetadata(&ctx, command.ContextMetadata{ | ||
Description: o.description, | ||
StackOrchestrator: stackOrchestrator, | ||
}) | ||
|
||
return s.CreateOrUpdateContext(o.name, ctx) | ||
} | ||
|
||
func newCreateCommand(dockerCli command.Cli) *cobra.Command { | ||
opts := &createOptions{} | ||
cmd := &cobra.Command{ | ||
Use: "create <name> [options]", | ||
Short: "create a context", | ||
Args: cli.ExactArgs(1), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
opts.name = args[0] | ||
return opts.process(dockerCli.ContextStore()) | ||
}, | ||
} | ||
|
||
opts.addFlags(cmd.Flags()) | ||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package context | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"os" | ||
|
||
"github.com/docker/cli/cli" | ||
"github.com/docker/cli/cli/command" | ||
"github.com/docker/cli/cli/context/kubernetes" | ||
"github.com/docker/cli/cli/context/store" | ||
"github.com/spf13/cobra" | ||
"k8s.io/client-go/tools/clientcmd" | ||
) | ||
|
||
type exportOptions struct { | ||
kubeconfig bool | ||
contextName string | ||
dest string | ||
} | ||
|
||
func newExportCommand(dockerCli command.Cli) *cobra.Command { | ||
opts := &exportOptions{} | ||
cmd := &cobra.Command{ | ||
Use: "export <context> [output file]", | ||
Short: "Export a context", | ||
Args: cli.RequiresRangeArgs(1, 2), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
opts.contextName = args[0] | ||
if len(args) == 2 { | ||
opts.dest = args[1] | ||
} else { | ||
opts.dest = opts.contextName | ||
if opts.kubeconfig { | ||
opts.dest += ".kubeconfig" | ||
} else { | ||
opts.dest += ".dockercontext" | ||
} | ||
} | ||
return runExport(dockerCli, opts) | ||
}, | ||
} | ||
|
||
flags := cmd.Flags() | ||
flags.BoolVarP(&opts.kubeconfig, "kubeconfig", "k", false, "export as a kubeconfig file") | ||
return cmd | ||
} | ||
func runExport(dockerCli command.Cli, opts *exportOptions) error { | ||
ctxMeta, err := dockerCli.ContextStore().GetContextMetadata(opts.contextName) | ||
if err != nil { | ||
return err | ||
} | ||
if !opts.kubeconfig { | ||
reader := store.Export(opts.contextName, dockerCli.ContextStore()) | ||
defer reader.Close() | ||
f, err := os.OpenFile(opts.dest, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) | ||
if err != nil { | ||
return err | ||
} | ||
defer f.Close() | ||
_, err = io.Copy(f, reader) | ||
return err | ||
} | ||
kubernetesEndpointMeta := kubernetes.Parse(opts.contextName, ctxMeta) | ||
if kubernetesEndpointMeta == nil { | ||
return fmt.Errorf("context %q has no kubernetes endpoint", opts.contextName) | ||
} | ||
kubernetesEndpoint, err := kubernetesEndpointMeta.WithTLSData(dockerCli.ContextStore()) | ||
if err != nil { | ||
return err | ||
} | ||
kubeConfig, err := kubernetesEndpoint.KubernetesConfig() | ||
if err != nil { | ||
return err | ||
} | ||
rawCfg, err := kubeConfig.RawConfig() | ||
if err != nil { | ||
return err | ||
} | ||
return clientcmd.WriteToFile(rawCfg, opts.dest) | ||
} |
Oops, something went wrong.