Fix PR comments

- More strict on orchestrator flag
- Make orchestrator flag more explicit as experimental
- Add experimentalCLI annotation on kubernetes flags
- Better kubeconfig error message
- Prefix service name with stackname in ps and services stack subcommands
- Fix yaml documentation
- Fix code coverage ignoring generated code

Signed-off-by: Silvin Lubecki <silvin.lubecki@docker.com>
This commit is contained in:
Silvin Lubecki 2018-01-02 23:56:07 +01:00
parent ad409767bf
commit f1b116179f
20 changed files with 83 additions and 52 deletions

View File

@ -140,7 +140,6 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error {
cli.clientInfo = ClientInfo{ cli.clientInfo = ClientInfo{
DefaultVersion: cli.client.ClientVersion(), DefaultVersion: cli.client.ClientVersion(),
HasExperimental: hasExperimental, HasExperimental: hasExperimental,
HasKubernetes: hasExperimental && orchestrator == OrchestratorKubernetes,
Orchestrator: orchestrator, Orchestrator: orchestrator,
} }
cli.initializeFromClient() cli.initializeFromClient()
@ -206,11 +205,15 @@ type ServerInfo struct {
// ClientInfo stores details about the supported features of the client // ClientInfo stores details about the supported features of the client
type ClientInfo struct { type ClientInfo struct {
HasExperimental bool HasExperimental bool
HasKubernetes bool
DefaultVersion string DefaultVersion string
Orchestrator Orchestrator Orchestrator Orchestrator
} }
// HasKubernetes checks if kubernetes orchestrator is enabled
func (c ClientInfo) HasKubernetes() bool {
return c.HasExperimental && c.Orchestrator == OrchestratorKubernetes
}
// NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err. // NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err.
func NewDockerCli(in io.ReadCloser, out, err io.Writer) *DockerCli { func NewDockerCli(in io.ReadCloser, out, err io.Writer) *DockerCli {
return &DockerCli{in: NewInStream(in), out: NewOutStream(out), err: err} return &DockerCli{in: NewInStream(in), out: NewOutStream(out), err: err}

View File

@ -171,7 +171,7 @@ func TestExperimentalCLI(t *testing.T) {
} }
func TestOrchestratorSwitch(t *testing.T) { func TestOrchestratorSwitch(t *testing.T) {
defaultVersion := "v1.55" defaultVersion := "v0.00"
var testcases = []struct { var testcases = []struct {
doc string doc string
@ -268,7 +268,7 @@ func TestOrchestratorSwitch(t *testing.T) {
} }
err := cli.Initialize(options) err := cli.Initialize(options)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, testcase.expectedKubernetes, cli.ClientInfo().HasKubernetes) assert.Equal(t, testcase.expectedKubernetes, cli.ClientInfo().HasKubernetes())
assert.Equal(t, testcase.expectedOrchestrator, string(cli.ClientInfo().Orchestrator)) assert.Equal(t, testcase.expectedOrchestrator, string(cli.ClientInfo().Orchestrator))
}) })
} }

View File

@ -3,7 +3,6 @@ package command
import ( import (
"fmt" "fmt"
"os" "os"
"strings"
) )
// Orchestrator type acts as an enum describing supported orchestrators. // Orchestrator type acts as an enum describing supported orchestrators.
@ -17,11 +16,11 @@ const (
orchestratorUnset = Orchestrator("unset") orchestratorUnset = Orchestrator("unset")
defaultOrchestrator = OrchestratorSwarm defaultOrchestrator = OrchestratorSwarm
dockerOrchestrator = "DOCKER_ORCHESTRATOR" envVarDockerOrchestrator = "DOCKER_ORCHESTRATOR"
) )
func normalize(flag string) Orchestrator { func normalize(flag string) Orchestrator {
switch strings.ToLower(flag) { switch flag {
case "kubernetes", "k8s": case "kubernetes", "k8s":
return OrchestratorKubernetes return OrchestratorKubernetes
case "swarm", "swarmkit": case "swarm", "swarmkit":
@ -43,7 +42,7 @@ func GetOrchestrator(isExperimental bool, flagValue, value string) Orchestrator
return o return o
} }
// Check environment variable // Check environment variable
env := os.Getenv(dockerOrchestrator) env := os.Getenv(envVarDockerOrchestrator)
if o := normalize(env); o != orchestratorUnset { if o := normalize(env); o != orchestratorUnset {
return o return o
} }

View File

@ -25,8 +25,10 @@ func NewStackCommand(dockerCli command.Cli) *cobra.Command {
flags := cmd.PersistentFlags() flags := cmd.PersistentFlags()
flags.String("namespace", "default", "Kubernetes namespace to use") flags.String("namespace", "default", "Kubernetes namespace to use")
flags.SetAnnotation("namespace", "kubernetes", nil) flags.SetAnnotation("namespace", "kubernetes", nil)
flags.SetAnnotation("namespace", "experimentalCLI", nil)
flags.String("kubeconfig", "", "Kubernetes config file") flags.String("kubeconfig", "", "Kubernetes config file")
flags.SetAnnotation("kubeconfig", "kubernetes", nil) flags.SetAnnotation("kubeconfig", "kubernetes", nil)
flags.SetAnnotation("kubeconfig", "experimentalCLI", nil)
return cmd return cmd
} }

View File

@ -19,7 +19,7 @@ func newDeployCommand(dockerCli command.Cli) *cobra.Command {
Args: cli.ExactArgs(1), Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
opts.Namespace = args[0] opts.Namespace = args[0]
if dockerCli.ClientInfo().HasKubernetes { if dockerCli.ClientInfo().HasKubernetes() {
kli, err := kubernetes.WrapCli(dockerCli, cmd) kli, err := kubernetes.WrapCli(dockerCli, cmd)
if err != nil { if err != nil {
return err return err

View File

@ -1,6 +1,7 @@
package kubernetes package kubernetes
import ( import (
"fmt"
"os" "os"
"path/filepath" "path/filepath"
@ -49,7 +50,7 @@ func WrapCli(dockerCli command.Cli, cmd *cobra.Command) (*KubeCli, error) {
config, err := clientcmd.BuildConfigFromFlags("", kubeConfig) config, err := clientcmd.BuildConfigFromFlags("", kubeConfig)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("Failed to load kubernetes configuration file '%s'", kubeConfig)
} }
cli.kubeConfig = config cli.kubeConfig = config

View File

@ -126,12 +126,16 @@ func replicasToServices(replicas *appsv1beta2.ReplicaSetList, services *apiv1.Se
if !ok { if !ok {
return nil, nil, fmt.Errorf("could not find service '%s'", r.Labels[labels.ForServiceName]) return nil, nil, fmt.Errorf("could not find service '%s'", r.Labels[labels.ForServiceName])
} }
stack, ok := service.Labels[labels.ForStackName]
if ok {
stack += "_"
}
uid := string(service.UID) uid := string(service.UID)
s := swarm.Service{ s := swarm.Service{
ID: uid, ID: uid,
Spec: swarm.ServiceSpec{ Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{ Annotations: swarm.Annotations{
Name: service.Name, Name: stack + service.Name,
}, },
TaskTemplate: swarm.TaskSpec{ TaskTemplate: swarm.TaskSpec{
ContainerSpec: &swarm.ContainerSpec{ ContainerSpec: &swarm.ContainerSpec{

View File

@ -20,7 +20,7 @@ func RunDeploy(dockerCli *KubeCli, opts options.Deploy) error {
return errors.Errorf("Please specify a Compose file (with --compose-file).") return errors.Errorf("Please specify a Compose file (with --compose-file).")
} }
// Initialize clients // Initialize clients
stackInterface, err := dockerCli.stacks() stacks, err := dockerCli.stacks()
if err != nil { if err != nil {
return err return err
} }
@ -28,12 +28,12 @@ func RunDeploy(dockerCli *KubeCli, opts options.Deploy) error {
if err != nil { if err != nil {
return err return err
} }
configMapInterface := composeClient.ConfigMaps() configMaps := composeClient.ConfigMaps()
secretInterface := composeClient.Secrets() secrets := composeClient.Secrets()
serviceInterface := composeClient.Services() services := composeClient.Services()
podInterface := composeClient.Pods() pods := composeClient.Pods()
watcher := DeployWatcher{ watcher := DeployWatcher{
Pods: podInterface, Pods: pods,
} }
// Parse the compose file // Parse the compose file
@ -43,28 +43,28 @@ func RunDeploy(dockerCli *KubeCli, opts options.Deploy) error {
} }
// FIXME(vdemeester) handle warnings server-side // FIXME(vdemeester) handle warnings server-side
if err = IsColliding(serviceInterface, stack, cfg); err != nil { if err = IsColliding(services, stack, cfg); err != nil {
return err return err
} }
if err = createFileBasedConfigMaps(stack.Name, cfg.Configs, configMapInterface); err != nil { if err = createFileBasedConfigMaps(stack.Name, cfg.Configs, configMaps); err != nil {
return err return err
} }
if err = createFileBasedSecrets(stack.Name, cfg.Secrets, secretInterface); err != nil { if err = createFileBasedSecrets(stack.Name, cfg.Secrets, secrets); err != nil {
return err return err
} }
if in, err := stackInterface.Get(stack.Name, metav1.GetOptions{}); err == nil { if in, err := stacks.Get(stack.Name, metav1.GetOptions{}); err == nil {
in.Spec = stack.Spec in.Spec = stack.Spec
if _, err = stackInterface.Update(in); err != nil { if _, err = stacks.Update(in); err != nil {
return err return err
} }
fmt.Printf("Stack %s was updated\n", stack.Name) fmt.Printf("Stack %s was updated\n", stack.Name)
} else { } else {
if _, err = stackInterface.Create(stack); err != nil { if _, err = stacks.Create(stack); err != nil {
return err return err
} }
@ -76,7 +76,7 @@ func RunDeploy(dockerCli *KubeCli, opts options.Deploy) error {
<-watcher.Watch(stack, serviceNames(cfg)) <-watcher.Watch(stack, serviceNames(cfg))
fmt.Fprintf(cmdOut, "Stack %s is stable and running\n\n", stack.Name) fmt.Fprintf(cmdOut, "Stack %s is stable and running\n\n", stack.Name)
// fmt.Fprintf(cmdOut, "Read the logs with:\n $ %s stack logs %s\n", filepath.Base(os.Args[0]), stack.Name) // TODO: fmt.Fprintf(cmdOut, "Read the logs with:\n $ %s stack logs %s\n", filepath.Base(os.Args[0]), stack.Name)
return nil return nil
} }

View File

@ -55,12 +55,12 @@ func RunPS(dockerCli *KubeCli, options options.PS) error {
for i, pod := range pods { for i, pod := range pods {
tasks[i] = podToTask(pod) tasks[i] = podToTask(pod)
} }
return print(dockerCli, tasks, pods, nodeResolver, !options.NoTrunc, options.Quiet, format) return print(dockerCli, namespace, tasks, pods, nodeResolver, !options.NoTrunc, options.Quiet, format)
} }
type idResolver func(name string) (string, error) type idResolver func(name string) (string, error)
func print(dockerCli command.Cli, tasks []swarm.Task, pods []apiv1.Pod, nodeResolver idResolver, trunc, quiet bool, format string) error { func print(dockerCli command.Cli, namespace string, tasks []swarm.Task, pods []apiv1.Pod, nodeResolver idResolver, trunc, quiet bool, format string) error {
sort.Stable(tasksBySlot(tasks)) sort.Stable(tasksBySlot(tasks))
names := map[string]string{} names := map[string]string{}
@ -78,7 +78,7 @@ func print(dockerCli command.Cli, tasks []swarm.Task, pods []apiv1.Pod, nodeReso
return err return err
} }
names[task.ID] = pods[i].Name names[task.ID] = fmt.Sprintf("%s_%s", namespace, pods[i].Name)
nodes[task.ID] = nodeValue nodes[task.ID] = nodeValue
} }

View File

@ -18,7 +18,7 @@ func newListCommand(dockerCli command.Cli) *cobra.Command {
Short: "List stacks", Short: "List stacks",
Args: cli.NoArgs, Args: cli.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if dockerCli.ClientInfo().HasKubernetes { if dockerCli.ClientInfo().HasKubernetes() {
kli, err := kubernetes.WrapCli(dockerCli, cmd) kli, err := kubernetes.WrapCli(dockerCli, cmd)
if err != nil { if err != nil {
return err return err

View File

@ -19,7 +19,7 @@ func newPsCommand(dockerCli command.Cli) *cobra.Command {
Args: cli.ExactArgs(1), Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
opts.Namespace = args[0] opts.Namespace = args[0]
if dockerCli.ClientInfo().HasKubernetes { if dockerCli.ClientInfo().HasKubernetes() {
kli, err := kubernetes.WrapCli(dockerCli, cmd) kli, err := kubernetes.WrapCli(dockerCli, cmd)
if err != nil { if err != nil {
return err return err

View File

@ -19,7 +19,7 @@ func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
Args: cli.RequiresMinArgs(1), Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
opts.Namespaces = args opts.Namespaces = args
if dockerCli.ClientInfo().HasKubernetes { if dockerCli.ClientInfo().HasKubernetes() {
kli, err := kubernetes.WrapCli(dockerCli, cmd) kli, err := kubernetes.WrapCli(dockerCli, cmd)
if err != nil { if err != nil {
return err return err

View File

@ -19,7 +19,7 @@ func newServicesCommand(dockerCli command.Cli) *cobra.Command {
Args: cli.ExactArgs(1), Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
opts.Namespace = args[0] opts.Namespace = args[0]
if dockerCli.ClientInfo().HasKubernetes { if dockerCli.ClientInfo().HasKubernetes() {
kli, err := kubernetes.WrapCli(dockerCli, cmd) kli, err := kubernetes.WrapCli(dockerCli, cmd)
if err != nil { if err != nil {
return err return err

View File

@ -54,7 +54,8 @@ func (commonOpts *CommonOptions) InstallFlags(flags *pflag.FlagSet) {
flags.StringVarP(&commonOpts.LogLevel, "log-level", "l", "info", `Set the logging level ("debug"|"info"|"warn"|"error"|"fatal")`) flags.StringVarP(&commonOpts.LogLevel, "log-level", "l", "info", `Set the logging level ("debug"|"info"|"warn"|"error"|"fatal")`)
flags.BoolVar(&commonOpts.TLS, "tls", false, "Use TLS; implied by --tlsverify") flags.BoolVar(&commonOpts.TLS, "tls", false, "Use TLS; implied by --tlsverify")
flags.BoolVar(&commonOpts.TLSVerify, FlagTLSVerify, dockerTLSVerify, "Use TLS and verify the remote") flags.BoolVar(&commonOpts.TLSVerify, FlagTLSVerify, dockerTLSVerify, "Use TLS and verify the remote")
flags.StringVar(&commonOpts.Orchestrator, "orchestrator", "", "Which orchestrator to use with the docker cli (swarm|kubernetes)") flags.StringVar(&commonOpts.Orchestrator, "orchestrator", "", "Which orchestrator to use with the docker cli (swarm|kubernetes) (default swarm) (experimental)")
flags.SetAnnotation("orchestrator", "experimentalCLI", nil)
// TODO use flag flags.String("identity"}, "i", "", "Path to libtrust key file") // TODO use flag flags.String("identity"}, "i", "", "Path to libtrust key file")

View File

@ -220,7 +220,7 @@ func hideUnsupportedFeatures(cmd *cobra.Command, details versionDetails) {
osType := details.ServerInfo().OSType osType := details.ServerInfo().OSType
hasExperimental := details.ServerInfo().HasExperimental hasExperimental := details.ServerInfo().HasExperimental
hasExperimentalCLI := details.ClientInfo().HasExperimental hasExperimentalCLI := details.ClientInfo().HasExperimental
hasKubernetes := details.ClientInfo().HasKubernetes hasKubernetes := details.ClientInfo().HasKubernetes()
cmd.Flags().VisitAll(func(f *pflag.Flag) { cmd.Flags().VisitAll(func(f *pflag.Flag) {
hideFeatureFlag(f, hasExperimental, "experimental") hideFeatureFlag(f, hasExperimental, "experimental")
@ -261,7 +261,7 @@ func areFlagsSupported(cmd *cobra.Command, details versionDetails) error {
clientVersion := details.Client().ClientVersion() clientVersion := details.Client().ClientVersion()
osType := details.ServerInfo().OSType osType := details.ServerInfo().OSType
hasExperimental := details.ServerInfo().HasExperimental hasExperimental := details.ServerInfo().HasExperimental
hasKubernetes := details.ClientInfo().HasKubernetes hasKubernetes := details.ClientInfo().HasKubernetes()
hasExperimentalCLI := details.ClientInfo().HasExperimental hasExperimentalCLI := details.ClientInfo().HasExperimental
errs := []string{} errs := []string{}
@ -301,7 +301,7 @@ func areSubcommandsSupported(cmd *cobra.Command, details versionDetails) error {
clientVersion := details.Client().ClientVersion() clientVersion := details.Client().ClientVersion()
hasExperimental := details.ServerInfo().HasExperimental hasExperimental := details.ServerInfo().HasExperimental
hasExperimentalCLI := details.ClientInfo().HasExperimental hasExperimentalCLI := details.ClientInfo().HasExperimental
hasKubernetes := details.ClientInfo().HasKubernetes hasKubernetes := details.ClientInfo().HasKubernetes()
// Check recursively so that, e.g., `docker stack ls` returns the same output as `docker stack` // Check recursively so that, e.g., `docker stack ls` returns the same output as `docker stack`
for curr := cmd; curr != nil; curr = curr.Parent() { for curr := cmd; curr != nil; curr = curr.Parent() {

View File

@ -17,3 +17,6 @@ ignore:
- "**/internal/test" - "**/internal/test"
- "vendor/*" - "vendor/*"
- "cli/compose/schema/bindata.go" - "cli/compose/schema/bindata.go"
- "cli/command/stack/kubernetes/api/openapi"
- "cli/command/stack/kubernetes/api/client"
- ".*generated.*"

View File

@ -23,6 +23,9 @@ type cmdOption struct {
Deprecated bool Deprecated bool
MinAPIVersion string `yaml:"min_api_version,omitempty"` MinAPIVersion string `yaml:"min_api_version,omitempty"`
Experimental bool Experimental bool
ExperimentalCLI bool
Kubernetes bool
Swarm bool
} }
type cmdDoc struct { type cmdDoc struct {
@ -43,6 +46,9 @@ type cmdDoc struct {
Deprecated bool Deprecated bool
MinAPIVersion string `yaml:"min_api_version,omitempty"` MinAPIVersion string `yaml:"min_api_version,omitempty"`
Experimental bool Experimental bool
ExperimentalCLI bool
Kubernetes bool
Swarm bool
} }
// GenYamlTree creates yaml structured ref files // GenYamlTree creates yaml structured ref files
@ -110,6 +116,15 @@ func GenYamlCustom(cmd *cobra.Command, w io.Writer) error {
if _, ok := curr.Annotations["experimental"]; ok && !cliDoc.Experimental { if _, ok := curr.Annotations["experimental"]; ok && !cliDoc.Experimental {
cliDoc.Experimental = true cliDoc.Experimental = true
} }
if _, ok := curr.Annotations["experimentalCLI"]; ok && !cliDoc.ExperimentalCLI {
cliDoc.ExperimentalCLI = true
}
if _, ok := curr.Annotations["kubernetes"]; ok && !cliDoc.Kubernetes {
cliDoc.Kubernetes = true
}
if _, ok := curr.Annotations["swarm"]; ok && !cliDoc.Swarm {
cliDoc.Kubernetes = true
}
} }
flags := cmd.NonInheritedFlags() flags := cmd.NonInheritedFlags()
@ -186,6 +201,15 @@ func genFlagResult(flags *pflag.FlagSet) []cmdOption {
if v, ok := flag.Annotations["version"]; ok { if v, ok := flag.Annotations["version"]; ok {
opt.MinAPIVersion = v[0] opt.MinAPIVersion = v[0]
} }
if _, ok := flag.Annotations["experimentalCLI"]; ok {
opt.ExperimentalCLI = true
}
if _, ok := flag.Annotations["kubernetes"]; ok {
opt.Kubernetes = true
}
if _, ok := flag.Annotations["swarm"]; ok {
opt.Kubernetes = true
}
result = append(result, opt) result = append(result, opt)
}) })

View File

@ -8,8 +8,6 @@ services:
image: 'docker:${TEST_ENGINE_VERSION:-edge-dind}' image: 'docker:${TEST_ENGINE_VERSION:-edge-dind}'
privileged: true privileged: true
command: ['--insecure-registry=registry:5000'] command: ['--insecure-registry=registry:5000']
depends_on:
- registry
notary-server: notary-server:
image: 'notary:server-0.4.2' image: 'notary:server-0.4.2'

View File

@ -2,7 +2,6 @@ package stack
import ( import (
"fmt" "fmt"
"os"
"strings" "strings"
"testing" "testing"
@ -21,9 +20,7 @@ func TestRemove(t *testing.T) {
deployFullStack(t, stackname) deployFullStack(t, stackname)
defer cleanupFullStack(t, stackname) defer cleanupFullStack(t, stackname)
result := icmd.RunCmd(shell(t, "docker version")) result := icmd.RunCmd(shell(t, "docker stack rm %s", stackname))
fmt.Println(result.Stdout(), os.Getenv("DOCKER_HOST"), os.Getenv("TEST_DOCKER_HOST"))
result = icmd.RunCmd(shell(t, "docker stack rm %s", stackname))
result.Assert(t, icmd.Expected{Err: icmd.None}) result.Assert(t, icmd.Expected{Err: icmd.None})
golden.Assert(t, result.Stdout(), "stack-remove-success.golden") golden.Assert(t, result.Stdout(), "stack-remove-success.golden")

View File

@ -1,5 +1,4 @@
# Kubernetes client libraries # Kubernetes client libraries
This package (and sub-packages) holds the client libraries for the kubernetes integration in This package (and sub-packages) holds the client libraries for the kubernetes integration in
the docker platform. Most of the code is currently generated but will evolved quickly in the docker platform. Most of the code is currently generated.
the next months.