Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions cli/command/formatter/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ import (
)

const (
defaultStackTableFormat = "table {{.Name}}\t{{.Services}}\t{{.Orchestrator}}"
// KubernetesStackTableFormat is the default Kubernetes stack format
KubernetesStackTableFormat = "table {{.Name}}\t{{.Services}}\t{{.Orchestrator}}\t{{.Namespace}}"
// SwarmStackTableFormat is the default Swarm stack format
SwarmStackTableFormat = "table {{.Name}}\t{{.Services}}\t{{.Orchestrator}}"

stackServicesHeader = "SERVICES"
stackOrchestrastorHeader = "ORCHESTRATOR"
stackNamespaceHeader = "NAMESPACE"
)

// Stack contains deployed stack information.
Expand All @@ -17,17 +21,10 @@ type Stack struct {
Name string
// Services is the number of the services
Services int
// Orchestratort is the platform on which the stack is deployed
// Orchestrator is the platform where the stack is deployed
Orchestrator string
}

// NewStackFormat returns a format for use with a stack Context
func NewStackFormat(source string) Format {
switch source {
case TableFormatKey:
return defaultStackTableFormat
}
return Format(source)
// Namespace is the Kubernetes namespace assigned to the stack
Namespace string
}

// StackWrite writes formatted stacks using the Context
Expand All @@ -54,6 +51,7 @@ func newStackContext() *stackContext {
"Name": nameHeader,
"Services": stackServicesHeader,
"Orchestrator": stackOrchestrastorHeader,
"Namespace": stackNamespaceHeader,
}
return &stackCtx
}
Expand All @@ -73,3 +71,7 @@ func (s *stackContext) Services() string {
func (s *stackContext) Orchestrator() string {
return s.s.Orchestrator
}

func (s *stackContext) Namespace() string {
return s.s.Namespace
}
18 changes: 13 additions & 5 deletions cli/command/formatter/stack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,31 +26,39 @@ func TestStackContextWrite(t *testing.T) {
},
// Table format
{
Context{Format: NewStackFormat("table")},
Context{Format: Format(SwarmStackTableFormat)},
`NAME SERVICES ORCHESTRATOR
baz 2 orchestrator1
bar 1 orchestrator2
`,
},
// Kubernetes table format adds Namespace column
{
Context{Format: NewStackFormat("table {{.Name}}")},
Context{Format: Format(KubernetesStackTableFormat)},
`NAME SERVICES ORCHESTRATOR NAMESPACE
baz 2 orchestrator1 namespace1
bar 1 orchestrator2 namespace2
`,
},
{
Context{Format: Format("table {{.Name}}")},
`NAME
baz
bar
`,
},
// Custom Format
{
Context{Format: NewStackFormat("{{.Name}}")},
Context{Format: Format("{{.Name}}")},
`baz
bar
`,
},
}

stacks := []*Stack{
{Name: "baz", Services: 2, Orchestrator: "orchestrator1"},
{Name: "bar", Services: 1, Orchestrator: "orchestrator2"},
{Name: "baz", Services: 2, Orchestrator: "orchestrator1", Namespace: "namespace1"},
{Name: "bar", Services: 1, Orchestrator: "orchestrator2", Namespace: "namespace2"},
}
for _, testcase := range cases {
out := bytes.NewBufferString("")
Expand Down
2 changes: 1 addition & 1 deletion cli/command/stack/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func NewStackCommand(dockerCli command.Cli) *cobra.Command {
newServicesCommand(dockerCli),
)
flags := cmd.PersistentFlags()
flags.String("namespace", "default", "Kubernetes namespace to use")
flags.String("namespace", "", "Kubernetes namespace to use")
flags.SetAnnotation("namespace", "kubernetes", nil)
flags.SetAnnotation("namespace", "experimentalCLI", nil)
flags.String("kubeconfig", "", "Kubernetes config file")
Expand Down
46 changes: 12 additions & 34 deletions cli/command/stack/kubernetes/cli.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
package kubernetes

import (
"os"
"path/filepath"

"github.com/docker/cli/cli/command"
"github.com/docker/cli/kubernetes"
"github.com/docker/docker/pkg/homedir"
"github.com/pkg/errors"
flag "github.com/spf13/pflag"
kubeclient "k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
Expand Down Expand Up @@ -43,21 +38,20 @@ func NewOptions(flags *flag.FlagSet) Options {
func WrapCli(dockerCli command.Cli, opts Options) (*KubeCli, error) {
var err error
cli := &KubeCli{
Cli: dockerCli,
kubeNamespace: "default",
}
if opts.Namespace != "" {
cli.kubeNamespace = opts.Namespace
Cli: dockerCli,
}
kubeConfig := opts.Config
if kubeConfig == "" {
if config := os.Getenv("KUBECONFIG"); config != "" {
kubeConfig = config
} else {
kubeConfig = filepath.Join(homedir.Get(), ".kube/config")
clientConfig := kubernetes.NewKubernetesConfig(opts.Config)

cli.kubeNamespace = opts.Namespace
if opts.Namespace == "" {
configNamespace, _, err := clientConfig.Namespace()
if err != nil {
return nil, err
}
cli.kubeNamespace = configNamespace
}
config, err := kubernetes.NewKubernetesConfig(kubeConfig)

config, err := clientConfig.ClientConfig()
if err != nil {
return nil, err
}
Expand All @@ -73,21 +67,5 @@ func WrapCli(dockerCli command.Cli, opts Options) (*KubeCli, error) {
}

func (c *KubeCli) composeClient() (*Factory, error) {
return NewFactory(c.kubeNamespace, c.kubeConfig)
}

func (c *KubeCli) stacks() (stackClient, error) {
version, err := kubernetes.GetStackAPIVersion(c.clientSet)
if err != nil {
return nil, err
}

switch version {
case kubernetes.StackAPIV1Beta1:
return newStackV1Beta1(c.kubeConfig, c.kubeNamespace)
case kubernetes.StackAPIV1Beta2:
return newStackV1Beta2(c.kubeConfig, c.kubeNamespace)
default:
return nil, errors.Errorf("no supported Stack API version")
}
return NewFactory(c.kubeNamespace, c.kubeConfig, c.clientSet)
}
31 changes: 29 additions & 2 deletions cli/command/stack/kubernetes/client.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package kubernetes

import (
"github.com/docker/cli/kubernetes"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kubeclient "k8s.io/client-go/kubernetes"
appsv1beta2 "k8s.io/client-go/kubernetes/typed/apps/v1beta2"
typesappsv1beta2 "k8s.io/client-go/kubernetes/typed/apps/v1beta2"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
Expand All @@ -13,10 +17,11 @@ type Factory struct {
config *restclient.Config
coreClientSet *corev1.CoreV1Client
appsClientSet *appsv1beta2.AppsV1beta2Client
clientSet *kubeclient.Clientset
}

// NewFactory creates a kubernetes client factory
func NewFactory(namespace string, config *restclient.Config) (*Factory, error) {
func NewFactory(namespace string, config *restclient.Config, clientSet *kubeclient.Clientset) (*Factory, error) {
coreClientSet, err := corev1.NewForConfig(config)
if err != nil {
return nil, err
Expand All @@ -32,6 +37,7 @@ func NewFactory(namespace string, config *restclient.Config) (*Factory, error) {
config: config,
coreClientSet: coreClientSet,
appsClientSet: appsClientSet,
clientSet: clientSet,
}, nil
}

Expand Down Expand Up @@ -65,7 +71,28 @@ func (s *Factory) ReplicationControllers() corev1.ReplicationControllerInterface
return s.coreClientSet.ReplicationControllers(s.namespace)
}

// ReplicaSets return a client for kubernetes replace sets
// ReplicaSets returns a client for kubernetes replace sets
func (s *Factory) ReplicaSets() typesappsv1beta2.ReplicaSetInterface {
return s.appsClientSet.ReplicaSets(s.namespace)
}

// Stacks returns a client for Docker's Stack on Kubernetes
func (s *Factory) Stacks(allNamespaces bool) (StackClient, error) {
version, err := kubernetes.GetStackAPIVersion(s.clientSet)
if err != nil {
return nil, err
}
namespace := s.namespace
if allNamespaces {
namespace = metav1.NamespaceAll
}

switch version {
case kubernetes.StackAPIV1Beta1:
return newStackV1Beta1(s.config, namespace)
case kubernetes.StackAPIV1Beta2:
return newStackV1Beta2(s.config, namespace)
default:
return nil, errors.Errorf("no supported Stack API version")
}
}
6 changes: 4 additions & 2 deletions cli/command/stack/kubernetes/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func stackFromV1beta1(in *v1beta1.Stack) (stack, error) {
}
return stack{
name: in.ObjectMeta.Name,
namespace: in.ObjectMeta.Namespace,
composeFile: in.Spec.ComposeFile,
spec: fromComposeConfig(ioutil.Discard, cfg),
}, nil
Expand All @@ -55,8 +56,9 @@ func stackToV1beta1(s stack) *v1beta1.Stack {

func stackFromV1beta2(in *v1beta2.Stack) stack {
return stack{
name: in.ObjectMeta.Name,
spec: in.Spec,
name: in.ObjectMeta.Name,
namespace: in.ObjectMeta.Namespace,
spec: in.Spec,
}
}

Expand Down
4 changes: 2 additions & 2 deletions cli/command/stack/kubernetes/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ func RunDeploy(dockerCli *KubeCli, opts options.Deploy) error {
}

// Initialize clients
stacks, err := dockerCli.stacks()
composeClient, err := dockerCli.composeClient()
if err != nil {
return err
}
composeClient, err := dockerCli.composeClient()
stacks, err := composeClient.Stacks(false)
if err != nil {
return err
}
Expand Down
18 changes: 11 additions & 7 deletions cli/command/stack/kubernetes/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ import (

// RunList is the kubernetes implementation of docker stack ls
func RunList(dockerCli *KubeCli, opts options.List) error {
stacks, err := getStacks(dockerCli)
stacks, err := getStacks(dockerCli, opts.AllNamespaces)
if err != nil {
return err
}
format := opts.Format
if len(format) == 0 {
format = formatter.TableFormatKey
if format == "" || format == formatter.TableFormatKey {
format = formatter.KubernetesStackTableFormat
}
stackCtx := formatter.Context{
Output: dockerCli.Out(),
Format: formatter.NewStackFormat(format),
Format: formatter.Format(format),
}
sort.Sort(byName(stacks))
return formatter.StackWrite(stackCtx, stacks)
Expand All @@ -33,12 +33,15 @@ func (n byName) Len() int { return len(n) }
func (n byName) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
func (n byName) Less(i, j int) bool { return sortorder.NaturalLess(n[i].Name, n[j].Name) }

func getStacks(kubeCli *KubeCli) ([]*formatter.Stack, error) {
stackSvc, err := kubeCli.stacks()
func getStacks(kubeCli *KubeCli, allNamespaces bool) ([]*formatter.Stack, error) {
composeClient, err := kubeCli.composeClient()
if err != nil {
return nil, err
}
stackSvc, err := composeClient.Stacks(allNamespaces)
if err != nil {
return nil, err
}

stacks, err := stackSvc.List(metav1.ListOptions{})
if err != nil {
return nil, err
Expand All @@ -49,6 +52,7 @@ func getStacks(kubeCli *KubeCli) ([]*formatter.Stack, error) {
Name: stack.name,
Services: len(stack.getServices()),
Orchestrator: "Kubernetes",
Namespace: stack.namespace,
})
}
return formattedStacks, nil
Expand Down
2 changes: 1 addition & 1 deletion cli/command/stack/kubernetes/ps.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func RunPS(dockerCli *KubeCli, options options.PS) error {
if err != nil {
return err
}
stacks, err := dockerCli.stacks()
stacks, err := client.Stacks(false)
if err != nil {
return err
}
Expand Down
6 changes: 5 additions & 1 deletion cli/command/stack/kubernetes/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import (

// RunRemove is the kubernetes implementation of docker stack remove
func RunRemove(dockerCli *KubeCli, opts options.Remove) error {
stacks, err := dockerCli.stacks()
composeClient, err := dockerCli.composeClient()
if err != nil {
return err
}
stacks, err := composeClient.Stacks(false)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cli/command/stack/kubernetes/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func RunServices(dockerCli *KubeCli, opts options.Services) error {
if err != nil {
return nil
}
stacks, err := dockerCli.stacks()
stacks, err := client.Stacks(false)
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions cli/command/stack/kubernetes/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
// stack is the main type used by stack commands so they remain independent from kubernetes compose component version.
type stack struct {
name string
namespace string
composeFile string
spec *v1beta2.StackSpec
}
Expand Down
8 changes: 4 additions & 4 deletions cli/command/stack/kubernetes/stackclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import (
"k8s.io/client-go/rest"
)

// stackClient talks to a kubernetes compose component.
type stackClient interface {
// StackClient talks to a kubernetes compose component.
type StackClient interface {
CreateOrUpdate(s stack) error
Delete(name string) error
Get(name string) (stack, error)
Expand All @@ -29,7 +29,7 @@ type stackV1Beta1 struct {
stacks composev1beta1.StackInterface
}

func newStackV1Beta1(config *rest.Config, namespace string) (stackClient, error) {
func newStackV1Beta1(config *rest.Config, namespace string) (StackClient, error) {
client, err := composev1beta1.NewForConfig(config)
if err != nil {
return nil, err
Expand Down Expand Up @@ -122,7 +122,7 @@ type stackV1Beta2 struct {
stacks composev1beta2.StackInterface
}

func newStackV1Beta2(config *rest.Config, namespace string) (stackClient, error) {
func newStackV1Beta2(config *rest.Config, namespace string) (StackClient, error) {
client, err := composev1beta2.NewForConfig(config)
if err != nil {
return nil, err
Expand Down
2 changes: 2 additions & 0 deletions cli/command/stack/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,7 @@ func newListCommand(dockerCli command.Cli) *cobra.Command {

flags := cmd.Flags()
flags.StringVar(&opts.Format, "format", "", "Pretty-print stacks using a Go template")
flags.BoolVarP(&opts.AllNamespaces, "all-namespaces", "", false, "List stacks among all Kubernetes namespaces")
Copy link
Member

@thaJeztah thaJeztah May 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder if we considered

  • using --namespace=all (e.g.) to show all namespaces (which would be consistent with --orchestrator=all), also allowing listing stack in a single namespace --namespace=<something>
  • showing all namespaces by default, but use a --filter namespace=foo to limit the output

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe what drives the current design is to mimic what kubectl does in order to not confuse Kubernetes users.

flags.SetAnnotation("all-namespaces", "kubernetes", nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this have to be gated by API-version as well? (possibly not because it was experimental so far)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it was experimental and also targeting only Kubernetes and UCP. The behavior toward the Docker daemon is supposed to not have been changed.

return cmd
}
Loading