Notification time stamped 2022-10-22 19:58:12 UTC
From e1e997eca7dabc5a27c67ce1933cffe428091abb Mon Sep 17 00:00:00 2001 From: Robert-André Mauchin zebob.m@gmail.com Date: Oct 22 2022 19:57:48 +0000 Subject: Update to to 22.06~beta.0
Close: rhbz#1990103, rhbz#2113287
---
diff --git a/.gitignore b/.gitignore index da7f9f2..659773b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ /cli-20.10.5.tar.gz /cli-20.10.6.tar.gz /cli-20.10.7.tar.gz +/cli-22.06.0-beta.0.tar.gz diff --git a/0001-Drop-obsolete-compose-on-kubernetes.patch b/0001-Drop-obsolete-compose-on-kubernetes.patch deleted file mode 100644 index d408ba0..0000000 --- a/0001-Drop-obsolete-compose-on-kubernetes.patch +++ /dev/null @@ -1,5233 +0,0 @@ -From 6077685887953717815823e22ef0553b64a1509c Mon Sep 17 00:00:00 2001 -From: Nicolas De Loof nicolas.deloof@gmail.com -Date: Wed, 16 Jun 2021 09:08:42 +0200 -Subject: [PATCH] Drop support for (archived) Compose-on-Kubernetes - -Signed-off-by: Nicolas De Loof nicolas.deloof@gmail.com ---- - cli/command/stack/cmd.go | 12 +- - cli/command/stack/common.go | 19 - - cli/command/stack/deploy.go | 6 +- - cli/command/stack/formatter/formatter.go | 11 - - cli/command/stack/formatter/formatter_test.go | 12 +- - cli/command/stack/kubernetes/cli.go | 144 - - cli/command/stack/kubernetes/client.go | 105 - - cli/command/stack/kubernetes/conversion.go | 269 - - .../stack/kubernetes/conversion_test.go | 185 - - cli/command/stack/kubernetes/convert.go | 578 - - cli/command/stack/kubernetes/convert_test.go | 349 - - cli/command/stack/kubernetes/deploy.go | 171 - - cli/command/stack/kubernetes/deploy_test.go | 299 - - cli/command/stack/kubernetes/list.go | 136 - - cli/command/stack/kubernetes/ps.go | 112 - - cli/command/stack/kubernetes/remove.go | 27 - - cli/command/stack/kubernetes/services.go | 140 - - cli/command/stack/kubernetes/services_test.go | 138 - - cli/command/stack/kubernetes/stack.go | 161 - - cli/command/stack/kubernetes/stackclient.go | 274 - - .../stack/kubernetes/stackclient_test.go | 60 - - .../testdata/compose-with-expose.yml | 9 - - .../testdata/compose-with-pull-policy.yml | 6 - - .../testdata/compose-with-pull-secret.yml | 6 - - cli/command/stack/kubernetes/testdata/config | 1 - - cli/command/stack/kubernetes/testdata/secret | 1 - - .../stack/kubernetes/testdata/warnings.golden | 31 - - cli/command/stack/kubernetes/warnings.go | 145 - - cli/command/stack/kubernetes/warnings_test.go | 78 - - cli/command/stack/kubernetes/watcher.go | 262 - - cli/command/stack/kubernetes/watcher_test.go | 220 - - cli/command/stack/list.go | 26 +- - cli/command/stack/options/opts.go | 1 - - cli/command/stack/ps.go | 6 +- - cli/command/stack/remove.go | 7 +- - cli/command/stack/services.go | 15 +- - cli/context/kubernetes/constants.go | 6 - - cli/context/kubernetes/endpoint_test.go | 224 - - cli/context/kubernetes/load.go | 146 - - cli/context/kubernetes/load_test.go | 25 - - cli/context/kubernetes/save.go | 69 - - .../kubernetes/testdata/eks-kubeconfig | 23 - - .../kubernetes/testdata/gke-kubeconfig | 23 - - .../kubernetes/testdata/k3s-kubeconfig | 20 - - .../kubernetes/testdata/test-kubeconfig | 20 - - cli/context/store/doc.go | 2 +- - e2e/stack/deploy_test.go | 9 - - e2e/stack/help_test.go | 3 - - e2e/stack/remove_test.go | 6 - - .../stack-deploy-help-kubernetes.golden | 14 - - .../testdata/stack-deploy-help-swarm.golden | 2 +- - .../stack-deploy-with-names-kubernetes.golden | 7 - - .../stack-remove-kubernetes-success.golden | 1 - - \ - 3633 files changed, 12 insertions(+), 1137760 deletions(-) - delete mode 100644 cli/command/stack/kubernetes/cli.go - delete mode 100644 cli/command/stack/kubernetes/client.go - delete mode 100644 cli/command/stack/kubernetes/conversion.go - delete mode 100644 cli/command/stack/kubernetes/conversion_test.go - delete mode 100644 cli/command/stack/kubernetes/convert.go - delete mode 100644 cli/command/stack/kubernetes/convert_test.go - delete mode 100644 cli/command/stack/kubernetes/deploy.go - delete mode 100644 cli/command/stack/kubernetes/deploy_test.go - delete mode 100644 cli/command/stack/kubernetes/list.go - delete mode 100644 cli/command/stack/kubernetes/ps.go - delete mode 100644 cli/command/stack/kubernetes/remove.go - delete mode 100644 cli/command/stack/kubernetes/services.go - delete mode 100644 cli/command/stack/kubernetes/services_test.go - delete mode 100644 cli/command/stack/kubernetes/stack.go - delete mode 100644 cli/command/stack/kubernetes/stackclient.go - delete mode 100644 cli/command/stack/kubernetes/stackclient_test.go - delete mode 100644 cli/command/stack/kubernetes/testdata/compose-with-expose.yml - delete mode 100644 cli/command/stack/kubernetes/testdata/compose-with-pull-policy.yml - delete mode 100644 cli/command/stack/kubernetes/testdata/compose-with-pull-secret.yml - delete mode 100644 cli/command/stack/kubernetes/testdata/config - delete mode 100644 cli/command/stack/kubernetes/testdata/secret - delete mode 100644 cli/command/stack/kubernetes/testdata/warnings.golden - delete mode 100644 cli/command/stack/kubernetes/warnings.go - delete mode 100644 cli/command/stack/kubernetes/warnings_test.go - delete mode 100644 cli/command/stack/kubernetes/watcher.go - delete mode 100644 cli/command/stack/kubernetes/watcher_test.go - delete mode 100644 cli/context/kubernetes/constants.go - delete mode 100644 cli/context/kubernetes/endpoint_test.go - delete mode 100644 cli/context/kubernetes/load.go - delete mode 100644 cli/context/kubernetes/load_test.go - delete mode 100644 cli/context/kubernetes/save.go - delete mode 100644 cli/context/kubernetes/testdata/eks-kubeconfig - delete mode 100644 cli/context/kubernetes/testdata/gke-kubeconfig - delete mode 100644 cli/context/kubernetes/testdata/k3s-kubeconfig - delete mode 100644 cli/context/kubernetes/testdata/test-kubeconfig - delete mode 100644 e2e/stack/testdata/stack-deploy-help-kubernetes.golden - delete mode 100644 e2e/stack/testdata/stack-deploy-with-names-kubernetes.golden - delete mode 100644 e2e/stack/testdata/stack-remove-kubernetes-success.golden - -diff --git a/cli/command/stack/cmd.go b/cli/command/stack/cmd.go -index f4ded771693..60c0cf21b80 100644 ---- a/cli/command/stack/cmd.go -+++ b/cli/command/stack/cmd.go -@@ -11,8 +11,6 @@ import ( - "github.com/spf13/pflag" - ) - --var errUnsupportedAllOrchestrator = fmt.Errorf(`no orchestrator specified: use either "kubernetes" or "swarm"`) -- - type commonOptions struct { - orchestrator command.Orchestrator - } -@@ -67,9 +65,7 @@ func NewStackCommand(dockerCli command.Cli) *cobra.Command { - newServicesCommand(dockerCli, &opts), - ) - flags := cmd.PersistentFlags() -- flags.String("kubeconfig", "", "Kubernetes config file") -- flags.SetAnnotation("kubeconfig", "kubernetes", nil) -- flags.String("orchestrator", "", "Orchestrator to use (swarm|kubernetes|all)") -+ flags.String("orchestrator", "", "Orchestrator to use (swarm|all)") - return cmd - } - -@@ -83,9 +79,6 @@ func getOrchestrator(dockerCli command.Cli, cmd *cobra.Command) (command.Orchest - - func hideOrchestrationFlags(cmd *cobra.Command, orchestrator command.Orchestrator) { - cmd.Flags().VisitAll(func(f *pflag.Flag) { -- if _, ok := f.Annotations["kubernetes"]; ok && !orchestrator.HasKubernetes() { -- f.Hidden = true -- } - if _, ok := f.Annotations["swarm"]; ok && !orchestrator.HasSwarm() { - f.Hidden = true - } -@@ -101,9 +94,6 @@ func checkSupportedFlag(cmd *cobra.Command, orchestrator command.Orchestrator) e - if !f.Changed { - return - } -- if _, ok := f.Annotations["kubernetes"]; ok && !orchestrator.HasKubernetes() { -- errs = append(errs, fmt.Sprintf(`"--%s" is only supported on a Docker cli with kubernetes features enabled`, f.Name)) -- } - if _, ok := f.Annotations["swarm"]; ok && !orchestrator.HasSwarm() { - errs = append(errs, fmt.Sprintf(`"--%s" is only supported on a Docker cli with swarm features enabled`, f.Name)) - } -diff --git a/cli/command/stack/common.go b/cli/command/stack/common.go -index 2a89cfad2ef..6de410da84c 100644 ---- a/cli/command/stack/common.go -+++ b/cli/command/stack/common.go -@@ -4,10 +4,6 @@ import ( - "fmt" - "strings" - "unicode" -- -- "github.com/docker/cli/cli/command" -- "github.com/docker/cli/cli/command/stack/kubernetes" -- "github.com/spf13/pflag" - ) - - // validateStackName checks if the provided string is a valid stack name (namespace). -@@ -33,18 +29,3 @@ func validateStackNames(namespaces []string) error { - func quotesOrWhitespace(r rune) bool { - return unicode.IsSpace(r) || r == '"' || r == ''' - } -- --func runOrchestratedCommand(dockerCli command.Cli, flags *pflag.FlagSet, commonOrchestrator command.Orchestrator, swarmCmd func() error, kubernetesCmd func(*kubernetes.KubeCli) error) error { -- switch { -- case commonOrchestrator.HasAll(): -- return errUnsupportedAllOrchestrator -- case commonOrchestrator.HasKubernetes(): -- kli, err := kubernetes.WrapCli(dockerCli, kubernetes.NewOptions(flags, commonOrchestrator)) -- if err != nil { -- return err -- } -- return kubernetesCmd(kli) -- default: -- return swarmCmd() -- } --} -diff --git a/cli/command/stack/deploy.go b/cli/command/stack/deploy.go -index 8dd31f41937..a25ce0cc68f 100644 ---- a/cli/command/stack/deploy.go -+++ b/cli/command/stack/deploy.go -@@ -3,7 +3,6 @@ package stack - import ( - "github.com/docker/cli/cli" - "github.com/docker/cli/cli/command" -- "github.com/docker/cli/cli/command/stack/kubernetes" - "github.com/docker/cli/cli/command/stack/loader" - "github.com/docker/cli/cli/command/stack/options" - "github.com/docker/cli/cli/command/stack/swarm" -@@ -45,13 +44,10 @@ func newDeployCommand(dockerCli command.Cli, common *commonOptions) *cobra.Comma - `Query the registry to resolve image digest and supported platforms ("`+swarm.ResolveImageAlways+`"|"`+swarm.ResolveImageChanged+`"|"`+swarm.ResolveImageNever+`")`) - flags.SetAnnotation("resolve-image", "version", []string{"1.30"}) - flags.SetAnnotation("resolve-image", "swarm", nil) -- kubernetes.AddNamespaceFlag(flags) - return cmd - } - - // RunDeploy performs a stack deploy against the specified orchestrator - func RunDeploy(dockerCli command.Cli, flags *pflag.FlagSet, config *composetypes.Config, commonOrchestrator command.Orchestrator, opts options.Deploy) error { -- return runOrchestratedCommand(dockerCli, flags, commonOrchestrator, -- func() error { return swarm.RunDeploy(dockerCli, opts, config) }, -- func(kli *kubernetes.KubeCli) error { return kubernetes.RunDeploy(kli, opts, config) }) -+ return swarm.RunDeploy(dockerCli, opts, config) - } -diff --git a/cli/command/stack/formatter/formatter.go b/cli/command/stack/formatter/formatter.go -index c52d9309c8e..6bc6bf03f30 100644 ---- a/cli/command/stack/formatter/formatter.go -+++ b/cli/command/stack/formatter/formatter.go -@@ -7,15 +7,11 @@ import ( - ) - - const ( -- // KubernetesStackTableFormat is the default Kubernetes stack format -- KubernetesStackTableFormat formatter.Format = "table {{.Name}}\t{{.Services}}\t{{.Orchestrator}}\t{{.Namespace}}" -- - // SwarmStackTableFormat is the default Swarm stack format - SwarmStackTableFormat formatter.Format = "table {{.Name}}\t{{.Services}}\t{{.Orchestrator}}" - - stackServicesHeader = "SERVICES" - stackOrchestrastorHeader = "ORCHESTRATOR" -- stackNamespaceHeader = "NAMESPACE" - - // TableFormatKey is an alias for formatter.TableFormatKey - TableFormatKey = formatter.TableFormatKey -@@ -35,8 +31,6 @@ type Stack struct { - Services int - // Orchestrator is the platform where the stack is deployed - Orchestrator string -- // Namespace is the Kubernetes namespace assigned to the stack -- Namespace string - } - - // StackWrite writes formatted stacks using the Context -@@ -63,7 +57,6 @@ func newStackContext() *stackContext { - "Name": formatter.NameHeader, - "Services": stackServicesHeader, - "Orchestrator": stackOrchestrastorHeader, -- "Namespace": stackNamespaceHeader, - } - return &stackCtx - } -@@ -83,7 +76,3 @@ func (s *stackContext) Services() string { - func (s *stackContext) Orchestrator() string { - return s.s.Orchestrator - } -- --func (s *stackContext) Namespace() string { -- return s.s.Namespace --} -diff --git a/cli/command/stack/formatter/formatter_test.go b/cli/command/stack/formatter/formatter_test.go -index dd25bca7d2b..3dc439634f8 100644 ---- a/cli/command/stack/formatter/formatter_test.go -+++ b/cli/command/stack/formatter/formatter_test.go -@@ -30,14 +30,6 @@ func TestStackContextWrite(t *testing.T) { - `NAME SERVICES ORCHESTRATOR - baz 2 orchestrator1 - bar 1 orchestrator2 --`, -- }, -- // Kubernetes table format adds Namespace column -- { -- formatter.Context{Format: KubernetesStackTableFormat}, -- `NAME SERVICES ORCHESTRATOR NAMESPACE --baz 2 orchestrator1 namespace1 --bar 1 orchestrator2 namespace2 - `, - }, - { -@@ -57,8 +49,8 @@ bar - } - - stacks := []*Stack{ -- {Name: "baz", Services: 2, Orchestrator: "orchestrator1", Namespace: "namespace1"}, -- {Name: "bar", Services: 1, Orchestrator: "orchestrator2", Namespace: "namespace2"}, -+ {Name: "baz", Services: 2, Orchestrator: "orchestrator1"}, -+ {Name: "bar", Services: 1, Orchestrator: "orchestrator2"}, - } - for _, tc := range cases { - tc := tc -diff --git a/cli/command/stack/kubernetes/cli.go b/cli/command/stack/kubernetes/cli.go -deleted file mode 100644 -index a531846809c..00000000000 ---- a/cli/command/stack/kubernetes/cli.go -+++ /dev/null -@@ -1,144 +0,0 @@ --package kubernetes -- --import ( -- "fmt" -- "net" -- "net/url" -- "os" -- -- "github.com/docker/cli/cli/command" -- kubecontext "github.com/docker/cli/cli/context/kubernetes" -- kubernetes "github.com/docker/compose-on-kubernetes/api" -- cliv1beta1 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1" -- "github.com/pkg/errors" -- flag "github.com/spf13/pflag" -- kubeclient "k8s.io/client-go/kubernetes" -- restclient "k8s.io/client-go/rest" -- "k8s.io/client-go/tools/clientcmd" --) -- --// KubeCli holds kubernetes specifics (client, namespace) with the command.Cli --type KubeCli struct { -- command.Cli -- kubeConfig *restclient.Config -- kubeNamespace string -- clientSet *kubeclient.Clientset --} -- --// Options contains resolved parameters to initialize kubernetes clients --type Options struct { -- Namespace string -- Config string -- Orchestrator command.Orchestrator --} -- --// NewOptions returns an Options initialized with command line flags --func NewOptions(flags *flag.FlagSet, orchestrator command.Orchestrator) Options { -- opts := Options{ -- Orchestrator: orchestrator, -- } -- if namespace, err := flags.GetString("namespace"); err == nil { -- opts.Namespace = namespace -- } -- if kubeConfig, err := flags.GetString("kubeconfig"); err == nil { -- opts.Config = kubeConfig -- } -- return opts --} -- --// AddNamespaceFlag adds the namespace flag to the given flag set --func AddNamespaceFlag(flags *flag.FlagSet) { -- flags.String("namespace", "", "Kubernetes namespace to use") -- flags.SetAnnotation("namespace", "kubernetes", nil) --} -- --// WrapCli wraps command.Cli with kubernetes specifics --func WrapCli(dockerCli command.Cli, opts Options) (*KubeCli, error) { -- cli := &KubeCli{ -- Cli: dockerCli, -- } -- var ( -- clientConfig clientcmd.ClientConfig -- err error -- ) -- if dockerCli.CurrentContext() == "" { -- clientConfig = kubernetes.NewKubernetesConfig(opts.Config) -- } else { -- clientConfig, err = kubecontext.ConfigFromContext(dockerCli.CurrentContext(), dockerCli.ContextStore()) -- } -- if err != nil { -- return nil, err -- } -- -- cli.kubeNamespace = opts.Namespace -- if opts.Namespace == "" { -- configNamespace, _, err := clientConfig.Namespace() -- switch { -- case os.IsNotExist(err), os.IsPermission(err): -- return nil, errors.Wrap(err, "unable to load configuration file") -- case err != nil: -- return nil, err -- } -- cli.kubeNamespace = configNamespace -- } -- -- config, err := clientConfig.ClientConfig() -- if err != nil { -- return nil, err -- } -- cli.kubeConfig = config -- -- clientSet, err := kubeclient.NewForConfig(config) -- if err != nil { -- return nil, err -- } -- cli.clientSet = clientSet -- -- if opts.Orchestrator.HasAll() { -- if err := cli.checkHostsMatch(); err != nil { -- return nil, err -- } -- } -- return cli, nil --} -- --func (c *KubeCli) composeClient() (*Factory, error) { -- return NewFactory(c.kubeNamespace, c.kubeConfig, c.clientSet) --} -- --func (c *KubeCli) checkHostsMatch() error { -- daemonEndpoint, err := url.Parse(c.Client().DaemonHost()) -- if err != nil { -- return err -- } -- kubeEndpoint, err := url.Parse(c.kubeConfig.Host) -- if err != nil { -- return err -- } -- if daemonEndpoint.Hostname() == kubeEndpoint.Hostname() { -- return nil -- } -- // The daemon can be local in Docker for Desktop, e.g. "npipe", "unix", ... -- if daemonEndpoint.Scheme != "tcp" { -- ips, err := net.LookupIP(kubeEndpoint.Hostname()) -- if err != nil { -- return err -- } -- for _, ip := range ips { -- if ip.IsLoopback() { -- return nil -- } -- } -- } -- fmt.Fprintf(c.Err(), "WARNING: Swarm and Kubernetes hosts do not match (docker host=%s, kubernetes host=%s).\n"+ -- " Update $DOCKER_HOST (or pass -H), or use 'kubectl config use-context' to match.\n", daemonEndpoint.Hostname(), kubeEndpoint.Hostname()) -- return nil --} -- --func (c *KubeCli) stacksv1beta1() (cliv1beta1.StackInterface, error) { -- raw, err := newStackV1Beta1(c.kubeConfig, c.kubeNamespace) -- if err != nil { -- return nil, err -- } -- return raw.stacks, nil --} -diff --git a/cli/command/stack/kubernetes/client.go b/cli/command/stack/kubernetes/client.go -deleted file mode 100644 -index 9a7dc7e3eeb..00000000000 ---- a/cli/command/stack/kubernetes/client.go -+++ /dev/null -@@ -1,105 +0,0 @@ --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" -- restclient "k8s.io/client-go/rest" --) -- --// Factory is the kubernetes client factory --type Factory struct { -- namespace string -- config *restclient.Config -- coreClientSet corev1.CoreV1Interface -- appsClientSet appsv1beta2.AppsV1beta2Interface -- clientSet *kubeclient.Clientset --} -- --// NewFactory creates a kubernetes client factory --func NewFactory(namespace string, config *restclient.Config, clientSet *kubeclient.Clientset) (*Factory, error) { -- coreClientSet, err := corev1.NewForConfig(config) -- if err != nil { -- return nil, err -- } -- -- appsClientSet, err := appsv1beta2.NewForConfig(config) -- if err != nil { -- return nil, err -- } -- -- return &Factory{ -- namespace: namespace, -- config: config, -- coreClientSet: coreClientSet, -- appsClientSet: appsClientSet, -- clientSet: clientSet, -- }, nil --} -- --// ConfigMaps returns a client for kubernetes's config maps --func (s *Factory) ConfigMaps() corev1.ConfigMapInterface { -- return s.coreClientSet.ConfigMaps(s.namespace) --} -- --// Secrets returns a client for kubernetes's secrets --func (s *Factory) Secrets() corev1.SecretInterface { -- return s.coreClientSet.Secrets(s.namespace) --} -- --// Services returns a client for kubernetes's secrets --func (s *Factory) Services() corev1.ServiceInterface { -- return s.coreClientSet.Services(s.namespace) --} -- --// Pods returns a client for kubernetes's pods --func (s *Factory) Pods() corev1.PodInterface { -- return s.coreClientSet.Pods(s.namespace) --} -- --// Nodes returns a client for kubernetes's nodes --func (s *Factory) Nodes() corev1.NodeInterface { -- return s.coreClientSet.Nodes() --} -- --// ReplicationControllers returns a client for kubernetes replication controllers --func (s *Factory) ReplicationControllers() corev1.ReplicationControllerInterface { -- return s.coreClientSet.ReplicationControllers(s.namespace) --} -- --// ReplicaSets returns a client for kubernetes replace sets --func (s *Factory) ReplicaSets() typesappsv1beta2.ReplicaSetInterface { -- return s.appsClientSet.ReplicaSets(s.namespace) --} -- --// DaemonSets returns a client for kubernetes daemon sets --func (s *Factory) DaemonSets() typesappsv1beta2.DaemonSetInterface { -- return s.appsClientSet.DaemonSets(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.Discovery()) -- 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) -- case kubernetes.StackAPIV1Alpha3: -- return newStackV1Alpha3(s.config, namespace) -- default: -- return nil, errors.Errorf("unsupported stack API version: %q", version) -- } --} -diff --git a/cli/command/stack/kubernetes/conversion.go b/cli/command/stack/kubernetes/conversion.go -deleted file mode 100644 -index 14797b081d3..00000000000 ---- a/cli/command/stack/kubernetes/conversion.go -+++ /dev/null -@@ -1,269 +0,0 @@ --package kubernetes -- --import ( -- "fmt" -- "sort" -- "strings" -- "time" -- -- "github.com/docker/compose-on-kubernetes/api/labels" -- "github.com/docker/docker/api/types/filters" -- "github.com/docker/docker/api/types/swarm" -- appsv1beta2 "k8s.io/api/apps/v1beta2" -- apiv1 "k8s.io/api/core/v1" -- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -- corev1 "k8s.io/client-go/kubernetes/typed/core/v1" --) -- --// Pod conversion --func podToTask(pod apiv1.Pod) swarm.Task { -- var startTime time.Time -- if pod.Status.StartTime != nil { -- startTime = (*pod.Status.StartTime).Time -- } -- task := swarm.Task{ -- ID: string(pod.UID), -- NodeID: pod.Spec.NodeName, -- Spec: swarm.TaskSpec{ -- ContainerSpec: &swarm.ContainerSpec{ -- Image: getContainerImage(pod.Spec.Containers), -- }, -- }, -- DesiredState: podPhaseToState(pod.Status.Phase), -- Status: swarm.TaskStatus{ -- State: podPhaseToState(pod.Status.Phase), -- Timestamp: startTime, -- PortStatus: swarm.PortStatus{ -- Ports: getPorts(pod.Spec.Containers), -- }, -- }, -- } -- -- return task --} -- --func podPhaseToState(phase apiv1.PodPhase) swarm.TaskState { -- switch phase { -- case apiv1.PodPending: -- return swarm.TaskStatePending -- case apiv1.PodRunning: -- return swarm.TaskStateRunning -- case apiv1.PodSucceeded: -- return swarm.TaskStateComplete -- case apiv1.PodFailed: -- return swarm.TaskStateFailed -- default: -- return swarm.TaskState("unknown") -- } --} -- --func toSwarmProtocol(protocol apiv1.Protocol) swarm.PortConfigProtocol { -- switch protocol { -- case apiv1.ProtocolTCP: -- return swarm.PortConfigProtocolTCP -- case apiv1.ProtocolUDP: -- return swarm.PortConfigProtocolUDP -- } -- return swarm.PortConfigProtocol("unknown") --} -- --func fetchPods(stackName string, pods corev1.PodInterface, f filters.Args) ([]apiv1.Pod, error) { -- services := f.Get("service") -- // for existing script compatibility, support either <servicename> or <stackname>_<servicename> format -- stackNamePrefix := stackName + "_" -- for _, s := range services { -- if strings.HasPrefix(s, stackNamePrefix) { -- services = append(services, strings.TrimPrefix(s, stackNamePrefix)) -- } -- } -- listOpts := metav1.ListOptions{LabelSelector: labels.SelectorForStack(stackName, services...)} -- var result []apiv1.Pod -- podsList, err := pods.List(listOpts) -- if err != nil { -- return nil, err -- } -- nodes := f.Get("node") -- for _, pod := range podsList.Items { -- if filterPod(pod, nodes) && -- // name filter is done client side for matching partials -- f.FuzzyMatch("name", stackNamePrefix+pod.Name) { -- -- result = append(result, pod) -- } -- } -- return result, nil --} -- --func filterPod(pod apiv1.Pod, nodes []string) bool { -- if len(nodes) == 0 { -- return true -- } -- for _, name := range nodes { -- if pod.Spec.NodeName == name { -- return true -- } -- } -- return false --} -- --func getContainerImage(containers []apiv1.Container) string { -- if len(containers) == 0 { -- return "" -- } -- return containers[0].Image --} -- --func getPorts(containers []apiv1.Container) []swarm.PortConfig { -- if len(containers) == 0 || len(containers[0].Ports) == 0 { -- return nil -- } -- ports := make([]swarm.PortConfig, len(containers[0].Ports)) -- for i, port := range containers[0].Ports { -- ports[i] = swarm.PortConfig{ -- PublishedPort: uint32(port.HostPort), -- TargetPort: uint32(port.ContainerPort), -- Protocol: toSwarmProtocol(port.Protocol), -- } -- } -- return ports --} -- --type tasksBySlot []swarm.Task -- --func (t tasksBySlot) Len() int { -- return len(t) --} -- --func (t tasksBySlot) Swap(i, j int) { -- t[i], t[j] = t[j], t[i] --} -- --func (t tasksBySlot) Less(i, j int) bool { -- // Sort by slot. -- if t[i].Slot != t[j].Slot { -- return t[i].Slot < t[j].Slot -- } -- -- // If same slot, sort by most recent. -- return t[j].Meta.CreatedAt.Before(t[i].CreatedAt) --} -- --const ( -- publishedServiceSuffix = "-published" -- publishedOnRandomPortSuffix = "-random-ports" --) -- --func convertToServices(replicas *appsv1beta2.ReplicaSetList, daemons *appsv1beta2.DaemonSetList, services *apiv1.ServiceList) ([]swarm.Service, error) { -- result := make([]swarm.Service, len(replicas.Items)) -- -- for i, r := range replicas.Items { -- s, err := replicatedService(r, services) -- if err != nil { -- return nil, err -- } -- result[i] = *s -- } -- for _, d := range daemons.Items { -- s, err := globalService(d, services) -- if err != nil { -- return nil, err -- } -- result = append(result, *s) -- } -- sort.Slice(result, func(i, j int) bool { -- return result[i].ID < result[j].ID -- }) -- return result, nil --} -- --func uint64ptr(i int32) *uint64 { -- var o uint64 -- if i > 0 { -- o = uint64(i) -- } -- return &o --} -- --func replicatedService(r appsv1beta2.ReplicaSet, services *apiv1.ServiceList) (*swarm.Service, error) { -- s, err := convertToService(r.Labels[labels.ForServiceName], services, r.Spec.Template.Spec.Containers) -- if err != nil { -- return nil, err -- } -- s.Spec.Mode = swarm.ServiceMode{ -- Replicated: &swarm.ReplicatedService{Replicas: uint64ptr(r.Status.Replicas)}, -- } -- s.ServiceStatus = &swarm.ServiceStatus{ -- RunningTasks: uint64(r.Status.AvailableReplicas), -- DesiredTasks: uint64(r.Status.Replicas), -- } -- return s, nil --} -- --func globalService(d appsv1beta2.DaemonSet, services *apiv1.ServiceList) (*swarm.Service, error) { -- s, err := convertToService(d.Labels[labels.ForServiceName], services, d.Spec.Template.Spec.Containers) -- if err != nil { -- return nil, err -- } -- s.Spec.Mode = swarm.ServiceMode{ -- Global: &swarm.GlobalService{}, -- } -- s.ServiceStatus = &swarm.ServiceStatus{ -- RunningTasks: uint64(d.Status.NumberReady), -- DesiredTasks: uint64(d.Status.DesiredNumberScheduled), -- } -- return s, nil --} -- --func convertToService(serviceName string, services *apiv1.ServiceList, containers []apiv1.Container) (*swarm.Service, error) { -- serviceHeadless, err := findService(services, serviceName) -- if err != nil { -- return nil, err -- } -- stack, ok := serviceHeadless.Labels[labels.ForStackName] -- if ok { -- stack += "_" -- } -- uid := string(serviceHeadless.UID) -- s := &swarm.Service{ -- ID: uid, -- Spec: swarm.ServiceSpec{ -- Annotations: swarm.Annotations{ -- Name: stack + serviceHeadless.Name, -- }, -- TaskTemplate: swarm.TaskSpec{ -- ContainerSpec: &swarm.ContainerSpec{ -- Image: getContainerImage(containers), -- }, -- }, -- }, -- } -- if serviceNodePort, err := findService(services, serviceName+publishedOnRandomPortSuffix); err == nil && serviceNodePort.Spec.Type == apiv1.ServiceTypeNodePort { -- s.Endpoint = serviceEndpoint(serviceNodePort, swarm.PortConfigPublishModeHost) -- } -- if serviceLoadBalancer, err := findService(services, serviceName+publishedServiceSuffix); err == nil && serviceLoadBalancer.Spec.Type == apiv1.ServiceTypeLoadBalancer { -- s.Endpoint = serviceEndpoint(serviceLoadBalancer, swarm.PortConfigPublishModeIngress) -- } -- return s, nil --} -- --func findService(services *apiv1.ServiceList, name string) (apiv1.Service, error) { -- for _, s := range services.Items { -- if s.Name == name { -- return s, nil -- } -- } -- return apiv1.Service{}, fmt.Errorf("could not find service '%s'", name) --} -- --func serviceEndpoint(service apiv1.Service, publishMode swarm.PortConfigPublishMode) swarm.Endpoint { -- configs := make([]swarm.PortConfig, len(service.Spec.Ports)) -- for i, p := range service.Spec.Ports { -- configs[i] = swarm.PortConfig{ -- PublishMode: publishMode, -- PublishedPort: uint32(p.Port), -- TargetPort: uint32(p.TargetPort.IntValue()), -- Protocol: toSwarmProtocol(p.Protocol), -- } -- } -- return swarm.Endpoint{Ports: configs} --} -diff --git a/cli/command/stack/kubernetes/conversion_test.go b/cli/command/stack/kubernetes/conversion_test.go -deleted file mode 100644 -index cad49d39746..00000000000 ---- a/cli/command/stack/kubernetes/conversion_test.go -+++ /dev/null -@@ -1,185 +0,0 @@ --package kubernetes -- --import ( -- "testing" -- -- . "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function -- "github.com/docker/compose-on-kubernetes/api/labels" -- "github.com/docker/docker/api/types/swarm" -- "gotest.tools/v3/assert" -- appsv1beta2 "k8s.io/api/apps/v1beta2" -- apiv1 "k8s.io/api/core/v1" -- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -- apimachineryTypes "k8s.io/apimachinery/pkg/types" -- apimachineryUtil "k8s.io/apimachinery/pkg/util/intstr" --) -- --func TestReplicasConversionNeedsAService(t *testing.T) { -- replicas := appsv1beta2.ReplicaSetList{ -- Items: []appsv1beta2.ReplicaSet{makeReplicaSet("unknown", 0, 0)}, -- } -- services := apiv1.ServiceList{} -- _, err := convertToServices(&replicas, &appsv1beta2.DaemonSetList{}, &services) -- assert.ErrorContains(t, err, "could not find service") --} -- --func TestKubernetesServiceToSwarmServiceConversion(t *testing.T) { -- testCases := []struct { -- doc string -- replicas *appsv1beta2.ReplicaSetList -- services *apiv1.ServiceList -- expectedServices []swarm.Service -- }{ -- { -- doc: "Match replicas with headless stack services", -- replicas: &appsv1beta2.ReplicaSetList{ -- Items: []appsv1beta2.ReplicaSet{ -- makeReplicaSet("service1", 2, 5), -- makeReplicaSet("service2", 3, 3), -- }, -- }, -- services: &apiv1.ServiceList{ -- Items: []apiv1.Service{ -- makeKubeService("service1", "stack", "uid1", apiv1.ServiceTypeClusterIP, nil), -- makeKubeService("service2", "stack", "uid2", apiv1.ServiceTypeClusterIP, nil), -- makeKubeService("service3", "other-stack", "uid2", apiv1.ServiceTypeClusterIP, nil), -- }, -- }, -- expectedServices: []swarm.Service{ -- makeSwarmService(t, "stack_service1", "uid1", ReplicatedService(5), ServiceStatus(5, 2)), -- makeSwarmService(t, "stack_service2", "uid2", ReplicatedService(3), ServiceStatus(3, 3)), -- }, -- }, -- { -- doc: "Headless service and LoadBalancer Service are tied to the same Swarm service", -- replicas: &appsv1beta2.ReplicaSetList{ -- Items: []appsv1beta2.ReplicaSet{ -- makeReplicaSet("service", 1, 1), -- }, -- }, -- services: &apiv1.ServiceList{ -- Items: []apiv1.Service{ -- makeKubeService("service", "stack", "uid1", apiv1.ServiceTypeClusterIP, nil), -- makeKubeService("service-published", "stack", "uid2", apiv1.ServiceTypeLoadBalancer, []apiv1.ServicePort{ -- { -- Port: 80, -- TargetPort: apimachineryUtil.FromInt(80), -- Protocol: apiv1.ProtocolTCP, -- }, -- }), -- }, -- }, -- expectedServices: []swarm.Service{ -- makeSwarmService(t, "stack_service", "uid1", -- ReplicatedService(1), -- ServiceStatus(1, 1), -- withPort(swarm.PortConfig{ -- PublishMode: swarm.PortConfigPublishModeIngress, -- PublishedPort: 80, -- TargetPort: 80, -- Protocol: swarm.PortConfigProtocolTCP, -- }), -- ), -- }, -- }, -- { -- doc: "Headless service and NodePort Service are tied to the same Swarm service", -- replicas: &appsv1beta2.ReplicaSetList{ -- Items: []appsv1beta2.ReplicaSet{ -- makeReplicaSet("service", 1, 1), -- }, -- }, -- services: &apiv1.ServiceList{ -- Items: []apiv1.Service{ -- makeKubeService("service", "stack", "uid1", apiv1.ServiceTypeClusterIP, nil), -- makeKubeService("service-random-ports", "stack", "uid2", apiv1.ServiceTypeNodePort, []apiv1.ServicePort{ -- { -- Port: 35666, -- TargetPort: apimachineryUtil.FromInt(80), -- Protocol: apiv1.ProtocolTCP, -- }, -- }), -- }, -- }, -- expectedServices: []swarm.Service{ -- makeSwarmService(t, "stack_service", "uid1", -- ReplicatedService(1), -- ServiceStatus(1, 1), -- withPort(swarm.PortConfig{ -- PublishMode: swarm.PortConfigPublishModeHost, -- PublishedPort: 35666, -- TargetPort: 80, -- Protocol: swarm.PortConfigProtocolTCP, -- }), -- ), -- }, -- }, -- } -- -- for _, tc := range testCases { -- tc := tc -- t.Run(tc.doc, func(t *testing.T) { -- swarmServices, err := convertToServices(tc.replicas, &appsv1beta2.DaemonSetList{}, tc.services) -- assert.NilError(t, err) -- assert.DeepEqual(t, tc.expectedServices, swarmServices) -- }) -- } --} -- --func makeReplicaSet(service string, available, replicas int32) appsv1beta2.ReplicaSet { -- return appsv1beta2.ReplicaSet{ -- ObjectMeta: metav1.ObjectMeta{ -- Labels: map[string]string{ -- labels.ForServiceName: service, -- }, -- }, -- Spec: appsv1beta2.ReplicaSetSpec{ -- Template: apiv1.PodTemplateSpec{ -- Spec: apiv1.PodSpec{ -- Containers: []apiv1.Container{ -- { -- Image: "image", -- }, -- }, -- }, -- }, -- }, -- Status: appsv1beta2.ReplicaSetStatus{ -- AvailableReplicas: available, -- Replicas: replicas, -- }, -- } --} -- --func makeKubeService(service, stack, uid string, serviceType apiv1.ServiceType, ports []apiv1.ServicePort) apiv1.Service { -- return apiv1.Service{ -- ObjectMeta: metav1.ObjectMeta{ -- Labels: map[string]string{ -- labels.ForStackName: stack, -- }, -- Name: service, -- UID: apimachineryTypes.UID(uid), -- }, -- Spec: apiv1.ServiceSpec{ -- Type: serviceType, -- Ports: ports, -- }, -- } --} -- --// TODO convertToServices currently doesn't set swarm.EndpointSpec.Ports --func withPort(port swarm.PortConfig) func(*swarm.Service) { -- return func(service *swarm.Service) { -- if service.Endpoint.Ports == nil { -- service.Endpoint.Ports = make([]swarm.PortConfig, 0) -- } -- service.Endpoint.Ports = append(service.Endpoint.Ports, port) -- } --} -- --func makeSwarmService(t *testing.T, name, id string, opts ...func(*swarm.Service)) swarm.Service { -- t.Helper() -- options := []func(*swarm.Service){ServiceID(id), ServiceName(name), ServiceImage("image")} -- options = append(options, opts...) -- return *Service(options...) --} -diff --git a/cli/command/stack/kubernetes/convert.go b/cli/command/stack/kubernetes/convert.go -deleted file mode 100644 -index 60c2c884472..00000000000 ---- a/cli/command/stack/kubernetes/convert.go -+++ /dev/null -@@ -1,578 +0,0 @@ --package kubernetes -- --import ( -- "io" -- "io/ioutil" -- "regexp" -- "strconv" -- "strings" -- -- "github.com/docker/cli/cli/compose/loader" -- "github.com/docker/cli/cli/compose/schema" -- composeTypes "github.com/docker/cli/cli/compose/types" -- composetypes "github.com/docker/cli/cli/compose/types" -- latest "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3" -- "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" -- "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" -- "github.com/docker/go-connections/nat" -- "github.com/mitchellh/mapstructure" -- "github.com/pkg/errors" -- yaml "gopkg.in/yaml.v2" -- v1 "k8s.io/api/core/v1" -- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" --) -- --const ( -- // kubernatesExtraField is an extra field on ServiceConfigs containing kubernetes-specific extensions to compose format -- kubernatesExtraField = "x-kubernetes" --) -- --// NewStackConverter returns a converter from types.Config (compose) to the specified --// stack version or error out if the version is not supported or existent. --func NewStackConverter(version string) (StackConverter, error) { -- switch version { -- case "v1beta1": -- return stackV1Beta1Converter{}, nil -- case "v1beta2": -- return stackV1Beta2Converter{}, nil -- case "v1alpha3": -- return stackV1Alpha3Converter{}, nil -- default: -- return nil, errors.Errorf("stack version %s unsupported", version) -- } --} -- --// StackConverter converts a compose types.Config to a Stack --type StackConverter interface { -- FromCompose(stderr io.Writer, name string, cfg *composetypes.Config) (Stack, error) --} -- --type stackV1Beta1Converter struct{} -- --func (s stackV1Beta1Converter) FromCompose(stderr io.Writer, name string, cfg *composetypes.Config) (Stack, error) { -- cfg.Version = v1beta1.MaxComposeVersion -- st, err := fromCompose(stderr, name, cfg, v1beta1Capabilities) -- if err != nil { -- return Stack{}, err -- } -- res, err := yaml.Marshal(cfg) -- if err != nil { -- return Stack{}, err -- } -- // reload the result to check that it produced a valid 3.5 compose file -- resparsedConfig, err := loader.ParseYAML(res) -- if err != nil { -- return Stack{}, err -- } -- if err = schema.Validate(resparsedConfig, v1beta1.MaxComposeVersion); err != nil { -- return Stack{}, errors.Wrapf(err, "the compose yaml file is invalid with v%s", v1beta1.MaxComposeVersion) -- } -- -- st.ComposeFile = string(res) -- return st, nil --} -- --type stackV1Beta2Converter struct{} -- --func (s stackV1Beta2Converter) FromCompose(stderr io.Writer, name string, cfg *composetypes.Config) (Stack, error) { -- return fromCompose(stderr, name, cfg, v1beta2Capabilities) --} -- --type stackV1Alpha3Converter struct{} -- --func (s stackV1Alpha3Converter) FromCompose(stderr io.Writer, name string, cfg *composetypes.Config) (Stack, error) { -- return fromCompose(stderr, name, cfg, v1alpha3Capabilities) --} -- --func fromCompose(stderr io.Writer, name string, cfg *composetypes.Config, capabilities composeCapabilities) (Stack, error) { -- spec, err := fromComposeConfig(stderr, cfg, capabilities) -- if err != nil { -- return Stack{}, err -- } -- return Stack{ -- Name: name, -- Spec: spec, -- }, nil --} -- --func loadStackData(composefile string) (*composetypes.Config, error) { -- parsed, err := loader.ParseYAML([]byte(composefile)) -- if err != nil { -- return nil, err -- } -- return loader.Load(composetypes.ConfigDetails{ -- ConfigFiles: []composetypes.ConfigFile{ -- { -- Config: parsed, -- }, -- }, -- }) --} -- --// Conversions from internal stack to different stack compose component versions. --func stackFromV1beta1(in *v1beta1.Stack) (Stack, error) { -- cfg, err := loadStackData(in.Spec.ComposeFile) -- if err != nil { -- return Stack{}, err -- } -- spec, err := fromComposeConfig(ioutil.Discard, cfg, v1beta1Capabilities) -- if err != nil { -- return Stack{}, err -- } -- return Stack{ -- Name: in.ObjectMeta.Name, -- Namespace: in.ObjectMeta.Namespace, -- ComposeFile: in.Spec.ComposeFile, -- Spec: spec, -- }, nil --} -- --func stackToV1beta1(s Stack) *v1beta1.Stack { -- return &v1beta1.Stack{ -- ObjectMeta: metav1.ObjectMeta{ -- Name: s.Name, -- }, -- Spec: v1beta1.StackSpec{ -- ComposeFile: s.ComposeFile, -- }, -- } --} -- --func stackFromV1beta2(in *v1beta2.Stack) (Stack, error) { -- var spec *latest.StackSpec -- if in.Spec != nil { -- spec = &latest.StackSpec{} -- if err := latest.Convert_v1beta2_StackSpec_To_v1alpha3_StackSpec(in.Spec, spec, nil); err != nil { -- return Stack{}, err -- } -- } -- return Stack{ -- Name: in.ObjectMeta.Name, -- Namespace: in.ObjectMeta.Namespace, -- Spec: spec, -- }, nil --} -- --func stackToV1beta2(s Stack) (*v1beta2.Stack, error) { -- var spec *v1beta2.StackSpec -- if s.Spec != nil { -- spec = &v1beta2.StackSpec{} -- if err := latest.Convert_v1alpha3_StackSpec_To_v1beta2_StackSpec(s.Spec, spec, nil); err != nil { -- return nil, err -- } -- } -- return &v1beta2.Stack{ -- ObjectMeta: metav1.ObjectMeta{ -- Name: s.Name, -- }, -- Spec: spec, -- }, nil --} -- --func stackFromV1alpha3(in *latest.Stack) Stack { -- return Stack{ -- Name: in.ObjectMeta.Name, -- Namespace: in.ObjectMeta.Namespace, -- Spec: in.Spec, -- } --} -- --func stackToV1alpha3(s Stack) *latest.Stack { -- return &latest.Stack{ -- ObjectMeta: metav1.ObjectMeta{ -- Name: s.Name, -- }, -- Spec: s.Spec, -- } --} -- --func fromComposeConfig(stderr io.Writer, c *composeTypes.Config, capabilities composeCapabilities) (*latest.StackSpec, error) { -- if c == nil { -- return nil, nil -- } -- warnUnsupportedFeatures(stderr, c) -- serviceConfigs := make([]latest.ServiceConfig, len(c.Services)) -- for i, s := range c.Services { -- svc, err := fromComposeServiceConfig(s, capabilities) -- if err != nil { -- return nil, err -- } -- serviceConfigs[i] = svc -- } -- return &latest.StackSpec{ -- Services: serviceConfigs, -- Secrets: fromComposeSecrets(c.Secrets), -- Configs: fromComposeConfigs(c.Configs), -- }, nil --} -- --func fromComposeSecrets(s map[string]composeTypes.SecretConfig) map[string]latest.SecretConfig { -- if s == nil { -- return nil -- } -- m := map[string]latest.SecretConfig{} -- for key, value := range s { -- m[key] = latest.SecretConfig{ -- Name: value.Name, -- File: value.File, -- External: latest.External{ -- Name: value.External.Name, -- External: value.External.External, -- }, -- Labels: value.Labels, -- } -- } -- return m --} -- --func fromComposeConfigs(s map[string]composeTypes.ConfigObjConfig) map[string]latest.ConfigObjConfig { -- if s == nil { -- return nil -- } -- m := map[string]latest.ConfigObjConfig{} -- for key, value := range s { -- m[key] = latest.ConfigObjConfig{ -- Name: value.Name, -- File: value.File, -- External: latest.External{ -- Name: value.External.Name, -- External: value.External.External, -- }, -- Labels: value.Labels, -- } -- } -- return m --} -- --func fromComposeServiceConfig(s composeTypes.ServiceConfig, capabilities composeCapabilities) (latest.ServiceConfig, error) { -- var ( -- userID *int64 -- err error -- ) -- if s.User != "" { -- numerical, err := strconv.Atoi(s.User) -- if err == nil { -- unixUserID := int64(numerical) -- userID = &unixUserID -- } -- } -- kubeExtra, err := resolveServiceExtra(s) -- if err != nil { -- return latest.ServiceConfig{}, err -- } -- if kubeExtra.PullSecret != "" && !capabilities.hasPullSecrets { -- return latest.ServiceConfig{}, errors.Errorf(`stack API version %s does not support pull secrets (field "x-kubernetes.pull_secret"), please use version v1alpha3 or higher`, capabilities.apiVersion) -- } -- if kubeExtra.PullPolicy != "" && !capabilities.hasPullPolicies { -- return latest.ServiceConfig{}, errors.Errorf(`stack API version %s does not support pull policies (field "x-kubernetes.pull_policy"), please use version v1alpha3 or higher`, capabilities.apiVersion) -- } -- -- internalPorts, err := setupIntraStackNetworking(s, kubeExtra, capabilities) -- if err != nil { -- return latest.ServiceConfig{}, err -- } -- -- return latest.ServiceConfig{ -- Name: s.Name, -- CapAdd: s.CapAdd, -- CapDrop: s.CapDrop, -- Command: s.Command, -- Configs: fromComposeServiceConfigs(s.Configs), -- Deploy: latest.DeployConfig{ -- Mode: s.Deploy.Mode, -- Replicas: s.Deploy.Replicas, -- Labels: s.Deploy.Labels, -- UpdateConfig: fromComposeUpdateConfig(s.Deploy.UpdateConfig), -- Resources: fromComposeResources(s.Deploy.Resources), -- RestartPolicy: fromComposeRestartPolicy(s.Deploy.RestartPolicy), -- Placement: fromComposePlacement(s.Deploy.Placement), -- }, -- Entrypoint: s.Entrypoint, -- Environment: s.Environment, -- ExtraHosts: s.ExtraHosts, -- Hostname: s.Hostname, -- HealthCheck: fromComposeHealthcheck(s.HealthCheck), -- Image: s.Image, -- Ipc: s.Ipc, -- Labels: s.Labels, -- Pid: s.Pid, -- Ports: fromComposePorts(s.Ports), -- Privileged: s.Privileged, -- ReadOnly: s.ReadOnly, -- Secrets: fromComposeServiceSecrets(s.Secrets), -- StdinOpen: s.StdinOpen, -- StopGracePeriod: composetypes.ConvertDurationPtr(s.StopGracePeriod), -- Tmpfs: s.Tmpfs, -- Tty: s.Tty, -- User: userID, -- Volumes: fromComposeServiceVolumeConfig(s.Volumes), -- WorkingDir: s.WorkingDir, -- PullSecret: kubeExtra.PullSecret, -- PullPolicy: kubeExtra.PullPolicy, -- InternalServiceType: kubeExtra.InternalServiceType, -- InternalPorts: internalPorts, -- }, nil --} -- --func setupIntraStackNetworking(s composeTypes.ServiceConfig, kubeExtra kubernetesExtra, capabilities composeCapabilities) ([]latest.InternalPort, error) { -- if kubeExtra.InternalServiceType != latest.InternalServiceTypeAuto && !capabilities.hasIntraStackLoadBalancing { -- return nil, -- errors.Errorf(`stack API version %s does not support intra-stack load balancing (field "x-kubernetes.internal_service_type"), please use version v1alpha3 or higher`, -- capabilities.apiVersion) -- } -- if !capabilities.hasIntraStackLoadBalancing { -- return nil, nil -- } -- if err := validateInternalServiceType(kubeExtra.InternalServiceType); err != nil { -- return nil, err -- } -- internalPorts, err := toInternalPorts(s.Expose) -- if err != nil { -- return nil, err -- } -- return internalPorts, nil --} -- --func validateInternalServiceType(internalServiceType latest.InternalServiceType) error { -- switch internalServiceType { -- case latest.InternalServiceTypeAuto, latest.InternalServiceTypeClusterIP, latest.InternalServiceTypeHeadless: -- default: -- return errors.Errorf(`invalid value %q for field "x-kubernetes.internal_service_type", valid values are %q or %q`, internalServiceType, -- latest.InternalServiceTypeClusterIP, -- latest.InternalServiceTypeHeadless) -- } -- return nil --} -- --func toInternalPorts(expose []string) ([]latest.InternalPort, error) { -- var internalPorts []latest.InternalPort -- for _, sourcePort := range expose { -- proto, port := nat.SplitProtoPort(sourcePort) -- start, end, err := nat.ParsePortRange(port) -- if err != nil { -- return nil, errors.Errorf("invalid format for expose: %q, error: %s", sourcePort, err) -- } -- for i := start; i <= end; i++ { -- k8sProto := v1.Protocol(strings.ToUpper(proto)) -- switch k8sProto { -- case v1.ProtocolSCTP, v1.ProtocolTCP, v1.ProtocolUDP: -- default: -- return nil, errors.Errorf("invalid protocol for expose: %q, supported values are %q, %q and %q", sourcePort, v1.ProtocolSCTP, v1.ProtocolTCP, v1.ProtocolUDP) -- } -- internalPorts = append(internalPorts, latest.InternalPort{ -- Port: int32(i), -- Protocol: k8sProto, -- }) -- } -- } -- return internalPorts, nil --} -- --func resolveServiceExtra(s composeTypes.ServiceConfig) (kubernetesExtra, error) { -- if iface, ok := s.Extras[kubernatesExtraField]; ok { -- var result kubernetesExtra -- if err := mapstructure.Decode(iface, &result); err != nil { -- return kubernetesExtra{}, err -- } -- return result, nil -- } -- return kubernetesExtra{}, nil --} -- --func fromComposePorts(ports []composeTypes.ServicePortConfig) []latest.ServicePortConfig { -- if ports == nil { -- return nil -- } -- p := make([]latest.ServicePortConfig, len(ports)) -- for i, port := range ports { -- p[i] = latest.ServicePortConfig{ -- Mode: port.Mode, -- Target: port.Target, -- Published: port.Published, -- Protocol: port.Protocol, -- } -- } -- return p --} -- --func fromComposeServiceSecrets(secrets []composeTypes.ServiceSecretConfig) []latest.ServiceSecretConfig { -- if secrets == nil { -- return nil -- } -- c := make([]latest.ServiceSecretConfig, len(secrets)) -- for i, secret := range secrets { -- c[i] = latest.ServiceSecretConfig{ -- Source: secret.Source, -- Target: secret.Target, -- UID: secret.UID, -- Mode: secret.Mode, -- } -- } -- return c --} -- --func fromComposeServiceConfigs(configs []composeTypes.ServiceConfigObjConfig) []latest.ServiceConfigObjConfig { -- if configs == nil { -- return nil -- } -- c := make([]latest.ServiceConfigObjConfig, len(configs)) -- for i, config := range configs { -- c[i] = latest.ServiceConfigObjConfig{ -- Source: config.Source, -- Target: config.Target, -- UID: config.UID, -- Mode: config.Mode, -- } -- } -- return c --} -- --func fromComposeHealthcheck(h *composeTypes.HealthCheckConfig) *latest.HealthCheckConfig { -- if h == nil { -- return nil -- } -- return &latest.HealthCheckConfig{ -- Test: h.Test, -- Timeout: composetypes.ConvertDurationPtr(h.Timeout), -- Interval: composetypes.ConvertDurationPtr(h.Interval), -- Retries: h.Retries, -- } --} -- --func fromComposePlacement(p composeTypes.Placement) latest.Placement { -- return latest.Placement{ -- Constraints: fromComposeConstraints(p.Constraints), -- } --} -- --var constraintEquals = regexp.MustCompile(`([\w.]*)\W*(==|!=)\W*([\w.]*)`) -- --const ( -- swarmOs = "node.platform.os" -- swarmArch = "node.platform.arch" -- swarmHostname = "node.hostname" -- swarmLabelPrefix = "node.labels." --) -- --func fromComposeConstraints(s []string) *latest.Constraints { -- if len(s) == 0 { -- return nil -- } -- constraints := &latest.Constraints{} -- for _, constraint := range s { -- matches := constraintEquals.FindStringSubmatch(constraint) -- if len(matches) == 4 { -- key := matches[1] -- operator := matches[2] -- value := matches[3] -- constraint := &latest.Constraint{ -- Operator: operator, -- Value: value, -- } -- switch { -- case key == swarmOs: -- constraints.OperatingSystem = constraint -- case key == swarmArch: -- constraints.Architecture = constraint -- case key == swarmHostname: -- constraints.Hostname = constraint -- case strings.HasPrefix(key, swarmLabelPrefix): -- if constraints.MatchLabels == nil { -- constraints.MatchLabels = map[string]latest.Constraint{} -- } -- constraints.MatchLabels[strings.TrimPrefix(key, swarmLabelPrefix)] = *constraint -- } -- } -- } -- return constraints --} -- --func fromComposeResources(r composeTypes.Resources) latest.Resources { -- return latest.Resources{ -- Limits: fromComposeResourcesResourceLimit(r.Limits), -- Reservations: fromComposeResourcesResource(r.Reservations), -- } --} -- --// TODO create ResourceLimit type and support for limiting Pids on k8s --func fromComposeResourcesResourceLimit(r *composeTypes.ResourceLimit) *latest.Resource { -- if r == nil { -- return nil -- } -- return &latest.Resource{ -- MemoryBytes: int64(r.MemoryBytes), -- NanoCPUs: r.NanoCPUs, -- } --} -- --func fromComposeResourcesResource(r *composeTypes.Resource) *latest.Resource { -- if r == nil { -- return nil -- } -- return &latest.Resource{ -- MemoryBytes: int64(r.MemoryBytes), -- NanoCPUs: r.NanoCPUs, -- } --} -- --func fromComposeUpdateConfig(u *composeTypes.UpdateConfig) *latest.UpdateConfig { -- if u == nil { -- return nil -- } -- return &latest.UpdateConfig{ -- Parallelism: u.Parallelism, -- } --} -- --func fromComposeRestartPolicy(r *composeTypes.RestartPolicy) *latest.RestartPolicy { -- if r == nil { -- return nil -- } -- return &latest.RestartPolicy{ -- Condition: r.Condition, -- } --} -- --func fromComposeServiceVolumeConfig(vs []composeTypes.ServiceVolumeConfig) []latest.ServiceVolumeConfig { -- if vs == nil { -- return nil -- } -- volumes := []latest.ServiceVolumeConfig{} -- for _, v := range vs { -- volumes = append(volumes, latest.ServiceVolumeConfig{ -- Type: v.Type, -- Source: v.Source, -- Target: v.Target, -- ReadOnly: v.ReadOnly, -- }) -- } -- return volumes --} -- --var ( -- v1beta1Capabilities = composeCapabilities{ -- apiVersion: "v1beta1", -- } -- v1beta2Capabilities = composeCapabilities{ -- apiVersion: "v1beta2", -- } -- v1alpha3Capabilities = composeCapabilities{ -- apiVersion: "v1alpha3", -- hasPullSecrets: true, -- hasPullPolicies: true, -- hasIntraStackLoadBalancing: true, -- } --) -- --type composeCapabilities struct { -- apiVersion string -- hasPullSecrets bool -- hasPullPolicies bool -- hasIntraStackLoadBalancing bool --} -- --type kubernetesExtra struct { -- PullSecret string `mapstructure:"pull_secret"` -- PullPolicy string `mapstructure:"pull_policy"` -- InternalServiceType latest.InternalServiceType `mapstructure:"internal_service_type"` --} -diff --git a/cli/command/stack/kubernetes/convert_test.go b/cli/command/stack/kubernetes/convert_test.go -deleted file mode 100644 -index aaab4eb8124..00000000000 ---- a/cli/command/stack/kubernetes/convert_test.go -+++ /dev/null -@@ -1,349 +0,0 @@ --package kubernetes -- --import ( -- "fmt" -- "io/ioutil" -- "path/filepath" -- "testing" -- -- "github.com/docker/cli/cli/compose/loader" -- composetypes "github.com/docker/cli/cli/compose/types" -- "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3" -- "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" -- "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" -- "gotest.tools/v3/assert" -- is "gotest.tools/v3/assert/cmp" -- v1 "k8s.io/api/core/v1" -- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" --) -- --func TestNewStackConverter(t *testing.T) { -- _, err := NewStackConverter("v1alpha1") -- assert.Check(t, is.ErrorContains(err, "stack version v1alpha1 unsupported")) -- -- _, err = NewStackConverter("v1beta1") -- assert.NilError(t, err) -- _, err = NewStackConverter("v1beta2") -- assert.NilError(t, err) -- _, err = NewStackConverter("v1alpha3") -- assert.NilError(t, err) --} -- --func TestConvertFromToV1beta1(t *testing.T) { -- composefile := `version: "3.3" --services: -- test: -- image: nginx --secrets: -- test: -- file: testdata/secret --configs: -- test: -- file: testdata/config --` -- stackv1beta1 := &v1beta1.Stack{ -- ObjectMeta: metav1.ObjectMeta{ -- Name: "test", -- }, -- Spec: v1beta1.StackSpec{ -- ComposeFile: composefile, -- }, -- } -- -- result, err := stackFromV1beta1(stackv1beta1) -- assert.NilError(t, err) -- expected := Stack{ -- Name: "test", -- ComposeFile: composefile, -- Spec: &v1alpha3.StackSpec{ -- Services: []v1alpha3.ServiceConfig{ -- { -- Name: "test", -- Image: "nginx", -- Environment: make(map[string]*string), -- }, -- }, -- Secrets: map[string]v1alpha3.SecretConfig{ -- "test": {File: filepath.FromSlash("testdata/secret")}, -- }, -- Configs: map[string]v1alpha3.ConfigObjConfig{ -- "test": {File: filepath.FromSlash("testdata/config")}, -- }, -- }, -- } -- assert.DeepEqual(t, expected, result) -- assert.DeepEqual(t, stackv1beta1, stackToV1beta1(result)) --} -- --func TestConvertFromToV1beta2(t *testing.T) { -- stackv1beta2 := &v1beta2.Stack{ -- ObjectMeta: metav1.ObjectMeta{ -- Name: "test", -- }, -- Spec: &v1beta2.StackSpec{ -- Services: []v1beta2.ServiceConfig{ -- { -- Name: "test", -- Image: "nginx", -- Environment: make(map[string]*string), -- }, -- }, -- Secrets: map[string]v1beta2.SecretConfig{ -- "test": {File: filepath.FromSlash("testdata/secret")}, -- }, -- Configs: map[string]v1beta2.ConfigObjConfig{ -- "test": {File: filepath.FromSlash("testdata/config")}, -- }, -- }, -- } -- expected := Stack{ -- Name: "test", -- Spec: &v1alpha3.StackSpec{ -- Services: []v1alpha3.ServiceConfig{ -- { -- Name: "test", -- Image: "nginx", -- Environment: make(map[string]*string), -- }, -- }, -- Secrets: map[string]v1alpha3.SecretConfig{ -- "test": {File: filepath.FromSlash("testdata/secret")}, -- }, -- Configs: map[string]v1alpha3.ConfigObjConfig{ -- "test": {File: filepath.FromSlash("testdata/config")}, -- }, -- }, -- } -- result, err := stackFromV1beta2(stackv1beta2) -- assert.NilError(t, err) -- assert.DeepEqual(t, expected, result) -- gotBack, err := stackToV1beta2(result) -- assert.NilError(t, err) -- assert.DeepEqual(t, stackv1beta2, gotBack) --} -- --func TestConvertFromToV1alpha3(t *testing.T) { -- stackv1alpha3 := &v1alpha3.Stack{ -- ObjectMeta: metav1.ObjectMeta{ -- Name: "test", -- }, -- Spec: &v1alpha3.StackSpec{ -- Services: []v1alpha3.ServiceConfig{ -- { -- Name: "test", -- Image: "nginx", -- Environment: make(map[string]*string), -- }, -- }, -- Secrets: map[string]v1alpha3.SecretConfig{ -- "test": {File: filepath.FromSlash("testdata/secret")}, -- }, -- Configs: map[string]v1alpha3.ConfigObjConfig{ -- "test": {File: filepath.FromSlash("testdata/config")}, -- }, -- }, -- } -- expected := Stack{ -- Name: "test", -- Spec: &v1alpha3.StackSpec{ -- Services: []v1alpha3.ServiceConfig{ -- { -- Name: "test", -- Image: "nginx", -- Environment: make(map[string]*string), -- }, -- }, -- Secrets: map[string]v1alpha3.SecretConfig{ -- "test": {File: filepath.FromSlash("testdata/secret")}, -- }, -- Configs: map[string]v1alpha3.ConfigObjConfig{ -- "test": {File: filepath.FromSlash("testdata/config")}, -- }, -- }, -- } -- result := stackFromV1alpha3(stackv1alpha3) -- assert.DeepEqual(t, expected, result) -- gotBack := stackToV1alpha3(result) -- assert.DeepEqual(t, stackv1alpha3, gotBack) --} -- --func loadTestStackWith(t *testing.T, with string) *composetypes.Config { -- t.Helper() -- filePath := fmt.Sprintf("testdata/compose-with-%s.yml", with) -- data, err := ioutil.ReadFile(filePath) -- assert.NilError(t, err) -- yamlData, err := loader.ParseYAML(data) -- assert.NilError(t, err) -- cfg, err := loader.Load(composetypes.ConfigDetails{ -- ConfigFiles: []composetypes.ConfigFile{ -- {Config: yamlData, Filename: filePath}, -- }, -- }) -- assert.NilError(t, err) -- return cfg --} -- --func TestHandlePullSecret(t *testing.T) { -- testData := loadTestStackWith(t, "pull-secret") -- cases := []struct { -- version string -- err string -- }{ -- {version: "v1beta1", err: `stack API version v1beta1 does not support pull secrets (field "x-kubernetes.pull_secret"), please use version v1alpha3 or higher`}, -- {version: "v1beta2", err: `stack API version v1beta2 does not support pull secrets (field "x-kubernetes.pull_secret"), please use version v1alpha3 or higher`}, -- {version: "v1alpha3"}, -- } -- -- for _, c := range cases { -- c := c -- t.Run(c.version, func(t *testing.T) { -- conv, err := NewStackConverter(c.version) -- assert.NilError(t, err) -- s, err := conv.FromCompose(ioutil.Discard, "test", testData) -- if c.err != "" { -- assert.Error(t, err, c.err) -- -- } else { -- assert.NilError(t, err) -- assert.Equal(t, s.Spec.Services[0].PullSecret, "some-secret") -- } -- }) -- } --} -- --func TestHandlePullPolicy(t *testing.T) { -- testData := loadTestStackWith(t, "pull-policy") -- cases := []struct { -- version string -- err string -- }{ -- {version: "v1beta1", err: `stack API version v1beta1 does not support pull policies (field "x-kubernetes.pull_policy"), please use version v1alpha3 or higher`}, -- {version: "v1beta2", err: `stack API version v1beta2 does not support pull policies (field "x-kubernetes.pull_policy"), please use version v1alpha3 or higher`}, -- {version: "v1alpha3"}, -- } -- -- for _, c := range cases { -- c := c -- t.Run(c.version, func(t *testing.T) { -- conv, err := NewStackConverter(c.version) -- assert.NilError(t, err) -- s, err := conv.FromCompose(ioutil.Discard, "test", testData) -- if c.err != "" { -- assert.Error(t, err, c.err) -- -- } else { -- assert.NilError(t, err) -- assert.Equal(t, s.Spec.Services[0].PullPolicy, "Never") -- } -- }) -- } --} -- --func TestHandleInternalServiceType(t *testing.T) { -- cases := []struct { -- name string -- value string -- caps composeCapabilities -- err string -- expected v1alpha3.InternalServiceType -- }{ -- { -- name: "v1beta1", -- value: "ClusterIP", -- caps: v1beta1Capabilities, -- err: `stack API version v1beta1 does not support intra-stack load balancing (field "x-kubernetes.internal_service_type"), please use version v1alpha3 or higher`, -- }, -- { -- name: "v1beta2", -- value: "ClusterIP", -- caps: v1beta2Capabilities, -- err: `stack API version v1beta2 does not support intra-stack load balancing (field "x-kubernetes.internal_service_type"), please use version v1alpha3 or higher`, -- }, -- { -- name: "v1alpha3", -- value: "ClusterIP", -- caps: v1alpha3Capabilities, -- expected: v1alpha3.InternalServiceTypeClusterIP, -- }, -- { -- name: "v1alpha3-invalid", -- value: "invalid", -- caps: v1alpha3Capabilities, -- err: `invalid value "invalid" for field "x-kubernetes.internal_service_type", valid values are "ClusterIP" or "Headless"`, -- }, -- } -- for _, c := range cases { -- c := c -- t.Run(c.name, func(t *testing.T) { -- res, err := fromComposeServiceConfig(composetypes.ServiceConfig{ -- Name: "test", -- Image: "test", -- Extras: map[string]interface{}{ -- "x-kubernetes": map[string]interface{}{ -- "internal_service_type": c.value, -- }, -- }, -- }, c.caps) -- if c.err == "" { -- assert.NilError(t, err) -- assert.Equal(t, res.InternalServiceType, c.expected) -- } else { -- assert.ErrorContains(t, err, c.err) -- } -- }) -- } --} -- --func TestIgnoreExpose(t *testing.T) { -- testData := loadTestStackWith(t, "expose") -- for _, version := range []string{"v1beta1", "v1beta2"} { -- conv, err := NewStackConverter(version) -- assert.NilError(t, err) -- s, err := conv.FromCompose(ioutil.Discard, "test", testData) -- assert.NilError(t, err) -- assert.Equal(t, len(s.Spec.Services[0].InternalPorts), 0) -- } --} -- --func TestParseExpose(t *testing.T) { -- testData := loadTestStackWith(t, "expose") -- conv, err := NewStackConverter("v1alpha3") -- assert.NilError(t, err) -- s, err := conv.FromCompose(ioutil.Discard, "test", testData) -- assert.NilError(t, err) -- expected := []v1alpha3.InternalPort{ -- { -- Port: 1, -- Protocol: v1.ProtocolTCP, -- }, -- { -- Port: 2, -- Protocol: v1.ProtocolTCP, -- }, -- { -- Port: 3, -- Protocol: v1.ProtocolTCP, -- }, -- { -- Port: 4, -- Protocol: v1.ProtocolTCP, -- }, -- { -- Port: 5, -- Protocol: v1.ProtocolUDP, -- }, -- { -- Port: 6, -- Protocol: v1.ProtocolUDP, -- }, -- { -- Port: 7, -- Protocol: v1.ProtocolUDP, -- }, -- { -- Port: 8, -- Protocol: v1.ProtocolUDP, -- }, -- } -- assert.DeepEqual(t, s.Spec.Services[0].InternalPorts, expected) --} -diff --git a/cli/command/stack/kubernetes/deploy.go b/cli/command/stack/kubernetes/deploy.go -deleted file mode 100644 -index 84fdc638c2e..00000000000 ---- a/cli/command/stack/kubernetes/deploy.go -+++ /dev/null -@@ -1,171 +0,0 @@ --package kubernetes -- --import ( -- "fmt" -- "io" -- -- "github.com/docker/cli/cli/command/stack/options" -- composetypes "github.com/docker/cli/cli/compose/types" -- "github.com/docker/cli/cli/streams" -- "github.com/morikuni/aec" -- corev1 "k8s.io/client-go/kubernetes/typed/core/v1" --) -- --// RunDeploy is the kubernetes implementation of docker stack deploy --func RunDeploy(dockerCli *KubeCli, opts options.Deploy, cfg *composetypes.Config) error { -- cmdOut := dockerCli.Out() -- -- // Initialize clients -- composeClient, err := dockerCli.composeClient() -- if err != nil { -- return err -- } -- stacks, err := composeClient.Stacks(false) -- if err != nil { -- return err -- } -- -- stack, err := stacks.FromCompose(dockerCli.Err(), opts.Namespace, cfg) -- if err != nil { -- return err -- } -- -- configMaps := composeClient.ConfigMaps() -- secrets := composeClient.Secrets() -- services := composeClient.Services() -- -- if err := stacks.IsColliding(services, stack); err != nil { -- return err -- } -- -- if err := createResources(stack, stacks, configMaps, secrets); err != nil { -- return err -- } -- -- fmt.Fprintln(cmdOut, "Waiting for the stack to be stable and running...") -- v1beta1Cli, err := dockerCli.stacksv1beta1() -- if err != nil { -- return err -- } -- -- pods := composeClient.Pods() -- watcher := &deployWatcher{ -- stacks: v1beta1Cli, -- pods: pods, -- } -- statusUpdates := make(chan serviceStatus) -- displayDone := make(chan struct{}) -- go func() { -- defer close(displayDone) -- display := newStatusDisplay(dockerCli.Out()) -- for status := range statusUpdates { -- display.OnStatus(status) -- } -- }() -- -- err = watcher.Watch(stack.Name, stack.getServices(), statusUpdates) -- close(statusUpdates) -- <-displayDone -- if err != nil { -- return err -- } -- fmt.Fprintf(cmdOut, "\nStack %s is stable and running\n\n", stack.Name) -- return nil -- --} -- --func createResources(stack Stack, stacks StackClient, configMaps corev1.ConfigMapInterface, secrets corev1.SecretInterface) error { -- var childResources []childResource -- -- cr, err := stack.createFileBasedConfigMaps(configMaps) -- childResources = append(childResources, cr...) // make sure we collect childresources already created in case of failure -- if err != nil { -- deleteChildResources(childResources) -- return err -- } -- -- cr, err = stack.createFileBasedSecrets(secrets) -- childResources = append(childResources, cr...) // make sure we collect childresources already created in case of failure -- if err != nil { -- deleteChildResources(childResources) -- return err -- } -- -- return stacks.CreateOrUpdate(stack, childResources) --} -- --type statusDisplay interface { -- OnStatus(serviceStatus) --} --type metaServiceState string -- --const ( -- metaServiceStateReady = metaServiceState("Ready") -- metaServiceStatePending = metaServiceState("Pending") -- metaServiceStateFailed = metaServiceState("Failed") --) -- --func metaStateFromStatus(status serviceStatus) metaServiceState { -- switch { -- case status.podsReady > 0: -- return metaServiceStateReady -- case status.podsPending > 0: -- return metaServiceStatePending -- default: -- return metaServiceStateFailed -- } --} -- --type forwardOnlyStatusDisplay struct { -- o *streams.Out -- states map[string]metaServiceState --} -- --func (d *forwardOnlyStatusDisplay) OnStatus(status serviceStatus) { -- state := metaStateFromStatus(status) -- if d.states[status.name] != state { -- d.states[status.name] = state -- fmt.Fprintf(d.o, "%s: %s\n", status.name, state) -- } --} -- --type interactiveStatusDisplay struct { -- o *streams.Out -- statuses []serviceStatus --} -- --func (d *interactiveStatusDisplay) OnStatus(status serviceStatus) { -- b := aec.EmptyBuilder -- for ix := 0; ix < len(d.statuses); ix++ { -- b = b.Up(1).EraseLine(aec.EraseModes.All) -- } -- b = b.Column(0) -- fmt.Fprint(d.o, b.ANSI) -- updated := false -- for ix, s := range d.statuses { -- if s.name == status.name { -- d.statuses[ix] = status -- s = status -- updated = true -- } -- displayInteractiveServiceStatus(s, d.o) -- } -- if !updated { -- d.statuses = append(d.statuses, status) -- displayInteractiveServiceStatus(status, d.o) -- } --} -- --func displayInteractiveServiceStatus(status serviceStatus, o io.Writer) { -- state := metaStateFromStatus(status) -- totalFailed := status.podsFailed + status.podsSucceeded + status.podsUnknown -- fmt.Fprintf(o, "%[1]s: %[2]s\t\t[pod status: %[3]d/%[6]d ready, %[4]d/%[6]d pending, %[5]d/%[6]d failed]\n", status.name, state, -- status.podsReady, status.podsPending, totalFailed, status.podsTotal) --} -- --func newStatusDisplay(o *streams.Out) statusDisplay { -- if !o.IsTerminal() { -- return &forwardOnlyStatusDisplay{o: o, states: map[string]metaServiceState{}} -- } -- return &interactiveStatusDisplay{o: o} --} -diff --git a/cli/command/stack/kubernetes/deploy_test.go b/cli/command/stack/kubernetes/deploy_test.go -deleted file mode 100644 -index cee1089cc9e..00000000000 ---- a/cli/command/stack/kubernetes/deploy_test.go -+++ /dev/null -@@ -1,299 +0,0 @@ --package kubernetes -- --import ( -- "errors" -- "testing" -- -- composev1alpha3 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1alpha3" -- composev1beta1 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1" -- composev1beta2 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2" -- "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3" -- "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" -- "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" -- "gotest.tools/v3/assert" -- kerrors "k8s.io/apimachinery/pkg/api/errors" -- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -- types "k8s.io/apimachinery/pkg/types" -- watch "k8s.io/apimachinery/pkg/watch" -- "k8s.io/client-go/kubernetes/fake" --) -- --func testStack() Stack { -- return Stack{ -- Name: "test", -- Namespace: "test", -- ComposeFile: `version: "3.3" --services: -- test: -- image: nginx --secrets: -- test: -- file: testdata/secret --configs: -- test: -- file: testdata/config --`, -- Spec: &v1alpha3.StackSpec{ -- Configs: map[string]v1alpha3.ConfigObjConfig{ -- "test": {Name: "test", File: "testdata/config"}, -- }, -- Secrets: map[string]v1alpha3.SecretConfig{ -- "test": {Name: "test", File: "testdata/secret"}, -- }, -- }, -- } --} -- --func TestCreateChildResourcesV1Beta1(t *testing.T) { -- k8sclientSet := fake.NewSimpleClientset() -- stack := testStack() -- configs := k8sclientSet.CoreV1().ConfigMaps("test") -- secrets := k8sclientSet.CoreV1().Secrets("test") -- assert.NilError(t, createResources( -- stack, -- &stackV1Beta1{stacks: &fakeV1beta1Client{}}, -- configs, -- secrets)) -- c, err := configs.Get("test", metav1.GetOptions{}) -- assert.NilError(t, err) -- checkOwnerReferences(t, c.ObjectMeta, v1beta1.SchemeGroupVersion.String()) -- s, err := secrets.Get("test", metav1.GetOptions{}) -- assert.NilError(t, err) -- checkOwnerReferences(t, s.ObjectMeta, v1beta1.SchemeGroupVersion.String()) --} -- --func checkOwnerReferences(t *testing.T, objMeta metav1.ObjectMeta, stackVersion string) { -- t.Helper() -- assert.Equal(t, len(objMeta.OwnerReferences), 1) -- assert.Equal(t, objMeta.OwnerReferences[0].Name, "test") -- assert.Equal(t, objMeta.OwnerReferences[0].Kind, "Stack") -- assert.Equal(t, objMeta.OwnerReferences[0].APIVersion, stackVersion) --} -- --func TestCreateChildResourcesV1Beta2(t *testing.T) { -- k8sclientSet := fake.NewSimpleClientset() -- stack := testStack() -- configs := k8sclientSet.CoreV1().ConfigMaps("test") -- secrets := k8sclientSet.CoreV1().Secrets("test") -- assert.NilError(t, createResources( -- stack, -- &stackV1Beta2{stacks: &fakeV1beta2Client{}}, -- configs, -- secrets)) -- c, err := configs.Get("test", metav1.GetOptions{}) -- assert.NilError(t, err) -- checkOwnerReferences(t, c.ObjectMeta, v1beta2.SchemeGroupVersion.String()) -- s, err := secrets.Get("test", metav1.GetOptions{}) -- assert.NilError(t, err) -- checkOwnerReferences(t, s.ObjectMeta, v1beta2.SchemeGroupVersion.String()) --} -- --func TestCreateChildResourcesV1Alpha3(t *testing.T) { -- k8sclientSet := fake.NewSimpleClientset() -- stack := testStack() -- configs := k8sclientSet.CoreV1().ConfigMaps("test") -- secrets := k8sclientSet.CoreV1().Secrets("test") -- assert.NilError(t, createResources( -- stack, -- &stackV1Alpha3{stacks: &fakeV1alpha3Client{}}, -- configs, -- secrets)) -- c, err := configs.Get("test", metav1.GetOptions{}) -- assert.NilError(t, err) -- checkOwnerReferences(t, c.ObjectMeta, v1alpha3.SchemeGroupVersion.String()) -- s, err := secrets.Get("test", metav1.GetOptions{}) -- assert.NilError(t, err) -- checkOwnerReferences(t, s.ObjectMeta, v1alpha3.SchemeGroupVersion.String()) --} -- --func TestCreateChildResourcesWithStackCreationErrorV1Beta1(t *testing.T) { -- k8sclientSet := fake.NewSimpleClientset() -- stack := testStack() -- configs := k8sclientSet.CoreV1().ConfigMaps("test") -- secrets := k8sclientSet.CoreV1().Secrets("test") -- err := createResources( -- stack, -- &stackV1Beta1{stacks: &fakeV1beta1Client{errorOnCreate: true}}, -- configs, -- secrets) -- assert.Error(t, err, "some error") -- _, err = configs.Get("test", metav1.GetOptions{}) -- assert.Check(t, kerrors.IsNotFound(err)) -- _, err = secrets.Get("test", metav1.GetOptions{}) -- assert.Check(t, kerrors.IsNotFound(err)) --} -- --func TestCreateChildResourcesWithStackCreationErrorV1Beta2(t *testing.T) { -- k8sclientSet := fake.NewSimpleClientset() -- stack := testStack() -- configs := k8sclientSet.CoreV1().ConfigMaps("test") -- secrets := k8sclientSet.CoreV1().Secrets("test") -- err := createResources( -- stack, -- &stackV1Beta2{stacks: &fakeV1beta2Client{errorOnCreate: true}}, -- configs, -- secrets) -- assert.Error(t, err, "some error") -- _, err = configs.Get("test", metav1.GetOptions{}) -- assert.Check(t, kerrors.IsNotFound(err)) -- _, err = secrets.Get("test", metav1.GetOptions{}) -- assert.Check(t, kerrors.IsNotFound(err)) --} -- --func TestCreateChildResourcesWithStackCreationErrorV1Alpha3(t *testing.T) { -- k8sclientSet := fake.NewSimpleClientset() -- stack := testStack() -- configs := k8sclientSet.CoreV1().ConfigMaps("test") -- secrets := k8sclientSet.CoreV1().Secrets("test") -- err := createResources( -- stack, -- &stackV1Alpha3{stacks: &fakeV1alpha3Client{errorOnCreate: true}}, -- configs, -- secrets) -- assert.Error(t, err, "some error") -- _, err = configs.Get("test", metav1.GetOptions{}) -- assert.Check(t, kerrors.IsNotFound(err)) -- _, err = secrets.Get("test", metav1.GetOptions{}) -- assert.Check(t, kerrors.IsNotFound(err)) --} -- --type fakeV1beta1Client struct { -- errorOnCreate bool --} -- --func (c *fakeV1beta1Client) Create(s *v1beta1.Stack) (*v1beta1.Stack, error) { -- if c.errorOnCreate { -- return nil, errors.New("some error") -- } -- return s, nil --} -- --func (c *fakeV1beta1Client) Update(*v1beta1.Stack) (*v1beta1.Stack, error) { -- return nil, nil --} -- --func (c *fakeV1beta1Client) UpdateStatus(*v1beta1.Stack) (*v1beta1.Stack, error) { -- return nil, nil --} -- --func (c *fakeV1beta1Client) Delete(name string, options *metav1.DeleteOptions) error { -- return nil --} -- --func (c *fakeV1beta1Client) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error { -- return nil --} -- --func (c *fakeV1beta1Client) Get(name string, options metav1.GetOptions) (*v1beta1.Stack, error) { -- return nil, kerrors.NewNotFound(v1beta1.SchemeGroupVersion.WithResource("stacks").GroupResource(), name) --} -- --func (c *fakeV1beta1Client) List(opts metav1.ListOptions) (*v1beta1.StackList, error) { -- return nil, nil --} -- --func (c *fakeV1beta1Client) Watch(opts metav1.ListOptions) (watch.Interface, error) { -- return nil, nil --} -- --func (c *fakeV1beta1Client) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*v1beta1.Stack, error) { -- return nil, nil --} -- --func (c *fakeV1beta1Client) WithSkipValidation() composev1beta1.StackInterface { -- return c --} -- --type fakeV1beta2Client struct { -- errorOnCreate bool --} -- --func (c *fakeV1beta2Client) Create(s *v1beta2.Stack) (*v1beta2.Stack, error) { -- if c.errorOnCreate { -- return nil, errors.New("some error") -- } -- return s, nil --} -- --func (c *fakeV1beta2Client) Update(*v1beta2.Stack) (*v1beta2.Stack, error) { -- return nil, nil --} -- --func (c *fakeV1beta2Client) UpdateStatus(*v1beta2.Stack) (*v1beta2.Stack, error) { -- return nil, nil --} -- --func (c *fakeV1beta2Client) Delete(name string, options *metav1.DeleteOptions) error { -- return nil --} -- --func (c *fakeV1beta2Client) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error { -- return nil --} -- --func (c *fakeV1beta2Client) Get(name string, options metav1.GetOptions) (*v1beta2.Stack, error) { -- return nil, kerrors.NewNotFound(v1beta1.SchemeGroupVersion.WithResource("stacks").GroupResource(), name) --} -- --func (c *fakeV1beta2Client) List(opts metav1.ListOptions) (*v1beta2.StackList, error) { -- return nil, nil --} -- --func (c *fakeV1beta2Client) Watch(opts metav1.ListOptions) (watch.Interface, error) { -- return nil, nil --} -- --func (c *fakeV1beta2Client) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*v1beta2.Stack, error) { -- return nil, nil --} -- --func (c *fakeV1beta2Client) WithSkipValidation() composev1beta2.StackInterface { -- return c --} -- --type fakeV1alpha3Client struct { -- errorOnCreate bool --} -- --func (c *fakeV1alpha3Client) Create(s *v1alpha3.Stack) (*v1alpha3.Stack, error) { -- if c.errorOnCreate { -- return nil, errors.New("some error") -- } -- return s, nil --} -- --func (c *fakeV1alpha3Client) Update(*v1alpha3.Stack) (*v1alpha3.Stack, error) { -- return nil, nil --} -- --func (c *fakeV1alpha3Client) UpdateStatus(*v1alpha3.Stack) (*v1alpha3.Stack, error) { -- return nil, nil --} -- --func (c *fakeV1alpha3Client) Delete(name string, options *metav1.DeleteOptions) error { -- return nil --} -- --func (c *fakeV1alpha3Client) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error { -- return nil --} -- --func (c *fakeV1alpha3Client) Get(name string, options metav1.GetOptions) (*v1alpha3.Stack, error) { -- return nil, kerrors.NewNotFound(v1beta1.SchemeGroupVersion.WithResource("stacks").GroupResource(), name) --} -- --func (c *fakeV1alpha3Client) List(opts metav1.ListOptions) (*v1alpha3.StackList, error) { -- return nil, nil --} -- --func (c *fakeV1alpha3Client) Watch(opts metav1.ListOptions) (watch.Interface, error) { -- return nil, nil --} -- --func (c *fakeV1alpha3Client) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*v1alpha3.Stack, error) { -- return nil, nil --} -- --func (c *fakeV1alpha3Client) WithSkipValidation() composev1alpha3.StackInterface { -- return c --} -diff --git a/cli/command/stack/kubernetes/list.go b/cli/command/stack/kubernetes/list.go -deleted file mode 100644 -index facd401bb99..00000000000 ---- a/cli/command/stack/kubernetes/list.go -+++ /dev/null -@@ -1,136 +0,0 @@ --package kubernetes -- --import ( -- "encoding/json" -- "fmt" -- "io/ioutil" -- "net/http" -- "net/url" -- -- "github.com/docker/cli/cli/command" -- "github.com/docker/cli/cli/command/stack/formatter" -- "github.com/docker/cli/cli/command/stack/options" -- "github.com/docker/cli/cli/config/configfile" -- "github.com/pkg/errors" -- core_v1 "k8s.io/api/core/v1" -- apierrs "k8s.io/apimachinery/pkg/api/errors" -- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" --) -- --// GetStacks lists the kubernetes stacks --func GetStacks(kubeCli *KubeCli, opts options.List) ([]*formatter.Stack, error) { -- if opts.AllNamespaces || len(opts.Namespaces) == 0 { -- if isAllNamespacesDisabled(kubeCli.ConfigFile().Kubernetes) { -- opts.AllNamespaces = true -- } -- return getStacksWithAllNamespaces(kubeCli, opts) -- } -- return getStacksWithNamespaces(kubeCli, opts, removeDuplicates(opts.Namespaces)) --} -- --func isAllNamespacesDisabled(kubeCliConfig *configfile.KubernetesConfig) bool { -- return kubeCliConfig == nil || kubeCliConfig.AllNamespaces != "disabled" --} -- --func getStacks(kubeCli *KubeCli, opts options.List) ([]*formatter.Stack, error) { -- composeClient, err := kubeCli.composeClient() -- if err != nil { -- return nil, err -- } -- stackSvc, err := composeClient.Stacks(opts.AllNamespaces) -- if err != nil { -- return nil, err -- } -- stacks, err := stackSvc.List(metav1.ListOptions{}) -- if err != nil { -- return nil, err -- } -- var formattedStacks []*formatter.Stack -- for _, stack := range stacks { -- formattedStacks = append(formattedStacks, &formatter.Stack{ -- Name: stack.Name, -- Services: len(stack.getServices()), -- Orchestrator: "Kubernetes", -- Namespace: stack.Namespace, -- }) -- } -- return formattedStacks, nil --} -- --func getStacksWithAllNamespaces(kubeCli *KubeCli, opts options.List) ([]*formatter.Stack, error) { -- stacks, err := getStacks(kubeCli, opts) -- if !apierrs.IsForbidden(err) { -- return stacks, err -- } -- namespaces, err2 := getUserVisibleNamespaces(*kubeCli) -- if err2 != nil { -- return nil, errors.Wrap(err2, "failed to query user visible namespaces") -- } -- if namespaces == nil { -- // UCP API not present, fall back to Kubernetes error -- return nil, err -- } -- opts.AllNamespaces = false -- return getStacksWithNamespaces(kubeCli, opts, namespaces) --} -- --func getUserVisibleNamespaces(dockerCli command.Cli) ([]string, error) { -- host := dockerCli.Client().DaemonHost() -- endpoint, err := url.Parse(host) -- if err != nil { -- return nil, err -- } -- endpoint.Scheme = "https" -- endpoint.Path = "/kubernetesNamespaces" -- resp, err := dockerCli.Client().HTTPClient().Get(endpoint.String()) -- if err != nil { -- return nil, err -- } -- defer resp.Body.Close() -- body, err := ioutil.ReadAll(resp.Body) -- if err != nil { -- return nil, errors.Wrapf(err, "received %d status and unable to read response", resp.StatusCode) -- } -- switch resp.StatusCode { -- case http.StatusOK: -- nms := &core_v1.NamespaceList{} -- if err := json.Unmarshal(body, nms); err != nil { -- return nil, errors.Wrapf(err, "unmarshal failed: %s", string(body)) -- } -- namespaces := make([]string, len(nms.Items)) -- for i, namespace := range nms.Items { -- namespaces[i] = namespace.Name -- } -- return namespaces, nil -- case http.StatusNotFound: -- // UCP API not present -- return nil, nil -- default: -- return nil, fmt.Errorf("received %d status while retrieving namespaces: %s", resp.StatusCode, string(body)) -- } --} -- --func getStacksWithNamespaces(kubeCli *KubeCli, opts options.List, namespaces []string) ([]*formatter.Stack, error) { -- stacks := []*formatter.Stack{} -- for _, namespace := range namespaces { -- kubeCli.kubeNamespace = namespace -- ss, err := getStacks(kubeCli, opts) -- if err != nil { -- return nil, err -- } -- stacks = append(stacks, ss...) -- } -- return stacks, nil --} -- --func removeDuplicates(namespaces []string) []string { -- found := make(map[string]bool) -- results := namespaces[:0] -- for _, n := range namespaces { -- if !found[n] { -- results = append(results, n) -- found[n] = true -- } -- } -- return results --} -diff --git a/cli/command/stack/kubernetes/ps.go b/cli/command/stack/kubernetes/ps.go -deleted file mode 100644 -index c8a0600d649..00000000000 ---- a/cli/command/stack/kubernetes/ps.go -+++ /dev/null -@@ -1,112 +0,0 @@ --package kubernetes -- --import ( -- "fmt" -- "sort" -- -- "github.com/docker/cli/cli/command" -- "github.com/docker/cli/cli/command/stack/formatter" -- "github.com/docker/cli/cli/command/stack/options" -- "github.com/docker/cli/cli/command/task" -- "github.com/docker/docker/api/types/swarm" -- apiv1 "k8s.io/api/core/v1" -- apierrs "k8s.io/apimachinery/pkg/api/errors" -- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -- corev1 "k8s.io/client-go/kubernetes/typed/core/v1" --) -- --var supportedPSFilters = map[string]bool{ -- "name": true, -- "service": true, -- "node": true, --} -- --// RunPS is the kubernetes implementation of docker stack ps --func RunPS(dockerCli *KubeCli, options options.PS) error { -- filters := options.Filter.Value() -- if err := filters.Validate(supportedPSFilters); err != nil { -- return err -- } -- client, err := dockerCli.composeClient() -- if err != nil { -- return err -- } -- stacks, err := client.Stacks(false) -- if err != nil { -- return err -- } -- stackName := options.Namespace -- _, err = stacks.Get(stackName) -- if apierrs.IsNotFound(err) { -- return fmt.Errorf("nothing found in stack: %s", stackName) -- } -- if err != nil { -- return err -- } -- pods, err := fetchPods(stackName, client.Pods(), filters) -- if err != nil { -- return err -- } -- if len(pods) == 0 { -- return fmt.Errorf("nothing found in stack: %s", stackName) -- } -- return printTasks(dockerCli, options, stackName, client, pods) --} -- --func printTasks(dockerCli command.Cli, options options.PS, namespace string, client corev1.NodesGetter, pods []apiv1.Pod) error { -- format := options.Format -- if format == "" { -- format = task.DefaultFormat(dockerCli.ConfigFile(), options.Quiet) -- } -- -- tasks := make([]swarm.Task, len(pods)) -- for i, pod := range pods { -- tasks[i] = podToTask(pod) -- } -- sort.Stable(tasksBySlot(tasks)) -- -- names := map[string]string{} -- nodes := map[string]string{} -- -- n, err := listNodes(client, options.NoResolve) -- if err != nil { -- return err -- } -- for i, task := range tasks { -- nodeValue, err := resolveNode(pods[i].Spec.NodeName, n, options.NoResolve) -- if err != nil { -- return err -- } -- names[task.ID] = fmt.Sprintf("%s_%s", namespace, pods[i].Name) -- nodes[task.ID] = nodeValue -- } -- -- tasksCtx := formatter.Context{ -- Output: dockerCli.Out(), -- Format: task.NewTaskFormat(format, options.Quiet), -- Trunc: !options.NoTrunc, -- } -- -- return task.FormatWrite(tasksCtx, tasks, names, nodes) --} -- --func resolveNode(name string, nodes *apiv1.NodeList, noResolve bool) (string, error) { -- // Here we have a name and we need to resolve its identifier. To mimic swarm behavior -- // we need to resolve to the id when noResolve is set, otherwise we return the name. -- if noResolve { -- for _, node := range nodes.Items { -- if node.Name == name { -- return string(node.UID), nil -- } -- } -- return "", fmt.Errorf("could not find node '%s'", name) -- } -- return name, nil --} -- --func listNodes(client corev1.NodesGetter, noResolve bool) (*apiv1.NodeList, error) { -- if noResolve { -- return client.Nodes().List(metav1.ListOptions{}) -- } -- return nil, nil --} -diff --git a/cli/command/stack/kubernetes/remove.go b/cli/command/stack/kubernetes/remove.go -deleted file mode 100644 -index 311c7597af6..00000000000 ---- a/cli/command/stack/kubernetes/remove.go -+++ /dev/null -@@ -1,27 +0,0 @@ --package kubernetes -- --import ( -- "fmt" -- -- "github.com/docker/cli/cli/command/stack/options" -- "github.com/pkg/errors" --) -- --// RunRemove is the kubernetes implementation of docker stack remove --func RunRemove(dockerCli *KubeCli, opts options.Remove) error { -- composeClient, err := dockerCli.composeClient() -- if err != nil { -- return err -- } -- stacks, err := composeClient.Stacks(false) -- if err != nil { -- return err -- } -- for _, stack := range opts.Namespaces { -- fmt.Fprintf(dockerCli.Out(), "Removing stack: %s\n", stack) -- if err := stacks.Delete(stack); err != nil { -- return errors.Wrapf(err, "Failed to remove stack %s", stack) -- } -- } -- return nil --} -diff --git a/cli/command/stack/kubernetes/services.go b/cli/command/stack/kubernetes/services.go -deleted file mode 100644 -index 3a3bb63c34d..00000000000 ---- a/cli/command/stack/kubernetes/services.go -+++ /dev/null -@@ -1,140 +0,0 @@ --package kubernetes -- --import ( -- "fmt" -- "strings" -- -- "github.com/docker/cli/cli/command/stack/options" -- "github.com/docker/compose-on-kubernetes/api/labels" -- "github.com/docker/docker/api/types/filters" -- "github.com/docker/docker/api/types/swarm" -- appsv1beta2 "k8s.io/api/apps/v1beta2" -- corev1 "k8s.io/api/core/v1" -- apierrs "k8s.io/apimachinery/pkg/api/errors" -- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" --) -- --var supportedServicesFilters = map[string]bool{ -- "mode": true, -- "name": true, -- "label": true, --} -- --func generateSelector(labels map[string][]string) []string { -- var result []string -- for k, v := range labels { -- for _, val := range v { -- result = append(result, fmt.Sprintf("%s=%s", k, val)) -- } -- if len(v) == 0 { -- result = append(result, k) -- } -- } -- return result --} -- --func parseLabelFilters(rawFilters []string) map[string][]string { -- labels := map[string][]string{} -- for _, rawLabel := range rawFilters { -- v := strings.SplitN(rawLabel, "=", 2) -- key := v[0] -- if len(v) > 1 { -- labels[key] = append(labels[key], v[1]) -- } else if _, ok := labels[key]; !ok { -- labels[key] = []string{} -- } -- } -- return labels --} -- --func generateLabelSelector(f filters.Args, stackName string) string { -- selectors := append(generateSelector(parseLabelFilters(f.Get("label"))), labels.SelectorForStack(stackName)) -- return strings.Join(selectors, ",") --} -- --func getResourcesForServiceList(dockerCli *KubeCli, filters filters.Args, labelSelector string) (*appsv1beta2.ReplicaSetList, *appsv1beta2.DaemonSetList, *corev1.ServiceList, error) { -- client, err := dockerCli.composeClient() -- if err != nil { -- return nil, nil, nil, err -- } -- modes := filters.Get("mode") -- replicas := &appsv1beta2.ReplicaSetList{} -- if len(modes) == 0 || filters.ExactMatch("mode", "replicated") { -- if replicas, err = client.ReplicaSets().List(metav1.ListOptions{LabelSelector: labelSelector}); err != nil { -- return nil, nil, nil, err -- } -- } -- daemons := &appsv1beta2.DaemonSetList{} -- if len(modes) == 0 || filters.ExactMatch("mode", "global") { -- if daemons, err = client.DaemonSets().List(metav1.ListOptions{LabelSelector: labelSelector}); err != nil { -- return nil, nil, nil, err -- } -- } -- services, err := client.Services().List(metav1.ListOptions{LabelSelector: labelSelector}) -- if err != nil { -- return nil, nil, nil, err -- } -- return replicas, daemons, services, nil --} -- --// GetServices is the kubernetes implementation of listing stack services --func GetServices(dockerCli *KubeCli, opts options.Services) ([]swarm.Service, error) { -- filters := opts.Filter.Value() -- if err := filters.Validate(supportedServicesFilters); err != nil { -- return nil, err -- } -- client, err := dockerCli.composeClient() -- if err != nil { -- return nil, err -- } -- stacks, err := client.Stacks(false) -- if err != nil { -- return nil, err -- } -- stackName := opts.Namespace -- _, err = stacks.Get(stackName) -- if apierrs.IsNotFound(err) { -- return []swarm.Service{}, nil -- } -- if err != nil { -- return nil, err -- } -- -- labelSelector := generateLabelSelector(filters, stackName) -- replicasList, daemonsList, servicesList, err := getResourcesForServiceList(dockerCli, filters, labelSelector) -- if err != nil { -- return nil, err -- } -- -- // Convert Replicas sets and kubernetes services to swarm services and formatter information -- services, err := convertToServices(replicasList, daemonsList, servicesList) -- if err != nil { -- return nil, err -- } -- services = filterServicesByName(services, filters.Get("name"), stackName) -- -- return services, nil --} -- --func filterServicesByName(services []swarm.Service, names []string, stackName string) []swarm.Service { -- if len(names) == 0 { -- return services -- } -- prefix := stackName + "_" -- // Accepts unprefixed service name (for compatibility with existing swarm scripts where service names are prefixed by stack names) -- for i, n := range names { -- if !strings.HasPrefix(n, prefix) { -- names[i] = stackName + "_" + n -- } -- } -- // Filter services -- result := []swarm.Service{} -- for _, s := range services { -- for _, n := range names { -- if strings.HasPrefix(s.Spec.Name, n) { -- result = append(result, s) -- } -- } -- } -- return result --} -diff --git a/cli/command/stack/kubernetes/services_test.go b/cli/command/stack/kubernetes/services_test.go -deleted file mode 100644 -index 14337ab8e4b..00000000000 ---- a/cli/command/stack/kubernetes/services_test.go -+++ /dev/null -@@ -1,138 +0,0 @@ --package kubernetes -- --import ( -- "testing" -- -- "github.com/docker/docker/api/types/filters" -- "github.com/docker/docker/api/types/swarm" -- "gotest.tools/v3/assert" -- "gotest.tools/v3/assert/cmp" --) -- --func TestServiceFiltersLabelSelectorGen(t *testing.T) { -- cases := []struct { -- name string -- stackName string -- filters filters.Args -- expectedSelectorParts []string -- }{ -- { -- name: "no-filter", -- stackName: "test", -- filters: filters.NewArgs(), -- expectedSelectorParts: []string{ -- "com.docker.stack.namespace=test", -- }, -- }, -- { -- name: "label present filter", -- stackName: "test", -- filters: filters.NewArgs( -- filters.KeyValuePair{Key: "label", Value: "label-is-present"}, -- ), -- expectedSelectorParts: []string{ -- "com.docker.stack.namespace=test", -- "label-is-present", -- }, -- }, -- { -- name: "single value label filter", -- stackName: "test", -- filters: filters.NewArgs( -- filters.KeyValuePair{Key: "label", Value: "label1=test"}, -- ), -- expectedSelectorParts: []string{ -- "com.docker.stack.namespace=test", -- "label1=test", -- }, -- }, -- { -- name: "multi value label filter", -- stackName: "test", -- filters: filters.NewArgs( -- filters.KeyValuePair{Key: "label", Value: "label1=test"}, -- filters.KeyValuePair{Key: "label", Value: "label1=test2"}, -- ), -- expectedSelectorParts: []string{ -- "com.docker.stack.namespace=test", -- "label1=test", -- "label1=test2", -- }, -- }, -- { -- name: "2 different labels filter", -- stackName: "test", -- filters: filters.NewArgs( -- filters.KeyValuePair{Key: "label", Value: "label1=test"}, -- filters.KeyValuePair{Key: "label", Value: "label2=test2"}, -- ), -- expectedSelectorParts: []string{ -- "com.docker.stack.namespace=test", -- "label1=test", -- "label2=test2", -- }, -- }, -- } -- -- for _, c := range cases { -- t.Run(c.name, func(t *testing.T) { -- result := generateLabelSelector(c.filters, c.stackName) -- for _, toFind := range c.expectedSelectorParts { -- assert.Assert(t, cmp.Contains(result, toFind)) -- } -- }) -- } --} --func TestServiceFiltersServiceByName(t *testing.T) { -- cases := []struct { -- name string -- filters []string -- services []swarm.Service -- expectedServices []swarm.Service -- }{ -- { -- name: "no filter", -- filters: []string{}, -- services: makeServices("s1", "s2"), -- expectedServices: makeServices("s1", "s2"), -- }, -- { -- name: "single-name filter", -- filters: []string{"s1"}, -- services: makeServices("s1", "s2"), -- expectedServices: makeServices("s1"), -- }, -- { -- name: "filter by prefix", -- filters: []string{"prefix"}, -- services: makeServices("prefix-s1", "prefix-s2", "s2"), -- expectedServices: makeServices("prefix-s1", "prefix-s2"), -- }, -- { -- name: "multi-name filter", -- filters: []string{"s1", "s2"}, -- services: makeServices("s1", "s2", "s3"), -- expectedServices: makeServices("s1", "s2"), -- }, -- { -- name: "stack name prefix is valid", -- filters: []string{"stack_s1"}, -- services: makeServices("s1", "s11", "s2"), -- expectedServices: makeServices("s1", "s11"), -- }, -- } -- for _, c := range cases { -- t.Run(c.name, func(t *testing.T) { -- result := filterServicesByName(c.services, c.filters, "stack") -- assert.DeepEqual(t, c.expectedServices, result) -- }) -- } --} -- --func makeServices(names ...string) []swarm.Service { -- result := make([]swarm.Service, len(names)) -- for i, n := range names { -- result[i] = swarm.Service{Spec: swarm.ServiceSpec{Annotations: swarm.Annotations{Name: "stack_" + n}}} -- } -- return result --} -diff --git a/cli/command/stack/kubernetes/stack.go b/cli/command/stack/kubernetes/stack.go -deleted file mode 100644 -index e368d718de9..00000000000 ---- a/cli/command/stack/kubernetes/stack.go -+++ /dev/null -@@ -1,161 +0,0 @@ --package kubernetes -- --import ( -- "io/ioutil" -- "path/filepath" -- "sort" -- -- latest "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3" -- "github.com/docker/compose-on-kubernetes/api/labels" -- apiv1 "k8s.io/api/core/v1" -- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -- corev1 "k8s.io/client-go/kubernetes/typed/core/v1" --) -- --// 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 *latest.StackSpec --} -- --type childResource interface { -- setOwner(metav1.OwnerReference) error -- delete() // does not report error, as if a deletion failed, we want to continue deleting other child resources --} -- --func deleteChildResources(childResources []childResource) { -- for _, cr := range childResources { -- cr.delete() -- } --} -- --func setChildResourcesOwner(childResources []childResource, owner metav1.OwnerReference) error { -- for _, cr := range childResources { -- if err := cr.setOwner(owner); err != nil { -- return err -- } -- } -- return nil --} -- --// getServices returns all the stack service names, sorted lexicographically --func (s *Stack) getServices() []string { -- services := make([]string, len(s.Spec.Services)) -- for i, service := range s.Spec.Services { -- services[i] = service.Name -- } -- sort.Strings(services) -- return services --} -- --// createFileBasedConfigMaps creates a Kubernetes ConfigMap for each Compose global file-based config. --func (s *Stack) createFileBasedConfigMaps(configMaps corev1.ConfigMapInterface) ([]childResource, error) { -- var resources []childResource -- for name, config := range s.Spec.Configs { -- if config.File == "" { -- continue -- } -- -- fileName := filepath.Base(config.File) -- content, err := ioutil.ReadFile(config.File) -- if err != nil { -- return resources, err -- } -- -- configMap, err := configMaps.Create(toConfigMap(s.Name, name, fileName, content)) -- if err != nil { -- return resources, err -- } -- resources = append(resources, &configMapChildResource{client: configMaps, configMap: configMap}) -- } -- return resources, nil --} -- --type configMapChildResource struct { -- client corev1.ConfigMapInterface -- configMap *apiv1.ConfigMap --} -- --func (r *configMapChildResource) setOwner(ref metav1.OwnerReference) error { -- r.configMap.OwnerReferences = append(r.configMap.OwnerReferences, ref) -- _, err := r.client.Update(r.configMap) -- return err --} -- --func (r *configMapChildResource) delete() { -- r.client.Delete(r.configMap.Name, nil) --} -- --// toConfigMap converts a Compose Config to a Kube ConfigMap. --func toConfigMap(stackName, name, key string, content []byte) *apiv1.ConfigMap { -- return &apiv1.ConfigMap{ -- TypeMeta: metav1.TypeMeta{ -- Kind: "ConfigMap", -- APIVersion: "v1", -- }, -- ObjectMeta: metav1.ObjectMeta{ -- Name: name, -- Labels: map[string]string{ -- labels.ForStackName: stackName, -- }, -- }, -- Data: map[string]string{ -- key: string(content), -- }, -- } --} -- --// createFileBasedSecrets creates a Kubernetes Secret for each Compose global file-based secret. --func (s *Stack) createFileBasedSecrets(secrets corev1.SecretInterface) ([]childResource, error) { -- var resources []childResource -- for name, secret := range s.Spec.Secrets { -- if secret.File == "" { -- continue -- } -- -- fileName := filepath.Base(secret.File) -- content, err := ioutil.ReadFile(secret.File) -- if err != nil { -- return resources, err -- } -- -- secret, err := secrets.Create(toSecret(s.Name, name, fileName, content)) -- if err != nil { -- return resources, err -- } -- resources = append(resources, &secretChildResource{client: secrets, secret: secret}) -- } -- return resources, nil --} -- --type secretChildResource struct { -- client corev1.SecretInterface -- secret *apiv1.Secret --} -- --func (r *secretChildResource) setOwner(ref metav1.OwnerReference) error { -- r.secret.OwnerReferences = append(r.secret.OwnerReferences, ref) -- _, err := r.client.Update(r.secret) -- return err --} -- --func (r *secretChildResource) delete() { -- r.client.Delete(r.secret.Name, nil) --} -- --// toSecret converts a Compose Secret to a Kube Secret. --func toSecret(stackName, name, key string, content []byte) *apiv1.Secret { -- return &apiv1.Secret{ -- ObjectMeta: metav1.ObjectMeta{ -- Name: name, -- Labels: map[string]string{ -- labels.ForStackName: stackName, -- }, -- }, -- Data: map[string][]byte{ -- key: content, -- }, -- } --} -diff --git a/cli/command/stack/kubernetes/stackclient.go b/cli/command/stack/kubernetes/stackclient.go -deleted file mode 100644 -index 5ce4480ebb0..00000000000 ---- a/cli/command/stack/kubernetes/stackclient.go -+++ /dev/null -@@ -1,274 +0,0 @@ --package kubernetes -- --import ( -- "fmt" -- -- composev1alpha3 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1alpha3" -- composev1beta1 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1" -- composev1beta2 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2" -- "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3" -- "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" -- "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" -- "github.com/docker/compose-on-kubernetes/api/labels" -- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -- corev1 "k8s.io/client-go/kubernetes/typed/core/v1" -- "k8s.io/client-go/rest" --) -- --// StackClient talks to a kubernetes compose component. --type StackClient interface { -- StackConverter -- CreateOrUpdate(s Stack, childResources []childResource) error -- Delete(name string) error -- Get(name string) (Stack, error) -- List(opts metav1.ListOptions) ([]Stack, error) -- IsColliding(servicesClient corev1.ServiceInterface, s Stack) error --} -- --// stackV1Beta1 implements stackClient interface and talks to compose component v1beta1. --type stackV1Beta1 struct { -- stackV1Beta1Converter -- stacks composev1beta1.StackInterface --} -- --func newStackV1Beta1(config *rest.Config, namespace string) (*stackV1Beta1, error) { -- client, err := composev1beta1.NewForConfig(config) -- if err != nil { -- return nil, err -- } -- return &stackV1Beta1{stacks: client.Stacks(namespace)}, nil --} -- --func (s *stackV1Beta1) CreateOrUpdate(internalStack Stack, childResources []childResource) error { -- // If it already exists, update the stack -- var ( -- stack *v1beta1.Stack -- err error -- ) -- if stack, err = s.stacks.Get(internalStack.Name, metav1.GetOptions{}); err == nil { -- stack.Spec.ComposeFile = internalStack.ComposeFile -- stack, err = s.stacks.Update(stack) -- } else { -- // Or create it -- stack, err = s.stacks.Create(stackToV1beta1(internalStack)) -- } -- if err != nil { -- deleteChildResources(childResources) -- return err -- } -- blockOwnerDeletion := true -- isController := true -- return setChildResourcesOwner(childResources, metav1.OwnerReference{ -- APIVersion: v1beta1.SchemeGroupVersion.String(), -- Kind: "Stack", -- Name: stack.Name, -- UID: stack.UID, -- BlockOwnerDeletion: &blockOwnerDeletion, -- Controller: &isController, -- }) --} -- --func (s *stackV1Beta1) Delete(name string) error { -- return s.stacks.Delete(name, &metav1.DeleteOptions{}) --} -- --func (s *stackV1Beta1) Get(name string) (Stack, error) { -- stackBeta1, err := s.stacks.Get(name, metav1.GetOptions{}) -- if err != nil { -- return Stack{}, err -- } -- return stackFromV1beta1(stackBeta1) --} -- --func (s *stackV1Beta1) List(opts metav1.ListOptions) ([]Stack, error) { -- list, err := s.stacks.List(opts) -- if err != nil { -- return nil, err -- } -- stacks := make([]Stack, len(list.Items)) -- for i := range list.Items { -- stack, err := stackFromV1beta1(&list.Items[i]) -- if err != nil { -- return nil, err -- } -- stacks[i] = stack -- } -- return stacks, nil --} -- --// IsColliding verifies that services defined in the stack collides with already deployed services --func (s *stackV1Beta1) IsColliding(servicesClient corev1.ServiceInterface, st Stack) error { -- for _, srv := range st.getServices() { -- if err := verify(servicesClient, st.Name, srv); err != nil { -- return err -- } -- } -- return nil --} -- --// verify checks whether the service is already present in kubernetes. --// If we find the service by name but it doesn't have our label or it has a different value --// than the stack name for the label, we fail (i.e. it will collide) --func verify(services corev1.ServiceInterface, stackName string, service string) error { -- svc, err := services.Get(service, metav1.GetOptions{}) -- if err == nil { -- if key, ok := svc.ObjectMeta.Labels[labels.ForStackName]; ok { -- if key != stackName { -- return fmt.Errorf("service %s already present in stack named %s", service, key) -- } -- return nil -- } -- return fmt.Errorf("service %s already present in the cluster", service) -- } -- return nil --} -- --// stackV1Beta2 implements stackClient interface and talks to compose component v1beta2. --type stackV1Beta2 struct { -- stackV1Beta2Converter -- stacks composev1beta2.StackInterface --} -- --func newStackV1Beta2(config *rest.Config, namespace string) (*stackV1Beta2, error) { -- client, err := composev1beta2.NewForConfig(config) -- if err != nil { -- return nil, err -- } -- return &stackV1Beta2{stacks: client.Stacks(namespace)}, nil --} -- --func (s *stackV1Beta2) CreateOrUpdate(internalStack Stack, childResources []childResource) error { -- var ( -- stack *v1beta2.Stack -- err error -- ) -- resolved, err := stackToV1beta2(internalStack) -- if err != nil { -- deleteChildResources(childResources) -- return err -- } -- if stack, err = s.stacks.Get(internalStack.Name, metav1.GetOptions{}); err == nil { -- stack.Spec = resolved.Spec -- stack, err = s.stacks.Update(stack) -- } else { -- // Or create it -- stack, err = s.stacks.Create(resolved) -- } -- if err != nil { -- deleteChildResources(childResources) -- return err -- } -- blockOwnerDeletion := true -- isController := true -- return setChildResourcesOwner(childResources, metav1.OwnerReference{ -- APIVersion: v1beta2.SchemeGroupVersion.String(), -- Kind: "Stack", -- Name: stack.Name, -- UID: stack.UID, -- BlockOwnerDeletion: &blockOwnerDeletion, -- Controller: &isController, -- }) --} -- --func (s *stackV1Beta2) Delete(name string) error { -- return s.stacks.Delete(name, &metav1.DeleteOptions{}) --} -- --func (s *stackV1Beta2) Get(name string) (Stack, error) { -- stackBeta2, err := s.stacks.Get(name, metav1.GetOptions{}) -- if err != nil { -- return Stack{}, err -- } -- return stackFromV1beta2(stackBeta2) --} -- --func (s *stackV1Beta2) List(opts metav1.ListOptions) ([]Stack, error) { -- list, err := s.stacks.List(opts) -- if err != nil { -- return nil, err -- } -- stacks := make([]Stack, len(list.Items)) -- for i := range list.Items { -- if stacks[i], err = stackFromV1beta2(&list.Items[i]); err != nil { -- return nil, err -- } -- } -- return stacks, nil --} -- --// IsColliding is handle server side with the compose api v1beta2, so nothing to do here --func (s *stackV1Beta2) IsColliding(servicesClient corev1.ServiceInterface, st Stack) error { -- return nil --} -- --// stackV1Beta2 implements stackClient interface and talks to compose component v1beta2. --type stackV1Alpha3 struct { -- stackV1Alpha3Converter -- stacks composev1alpha3.StackInterface --} -- --func newStackV1Alpha3(config *rest.Config, namespace string) (*stackV1Alpha3, error) { -- client, err := composev1alpha3.NewForConfig(config) -- if err != nil { -- return nil, err -- } -- return &stackV1Alpha3{stacks: client.Stacks(namespace)}, nil --} -- --func (s *stackV1Alpha3) CreateOrUpdate(internalStack Stack, childResources []childResource) error { -- var ( -- stack *v1alpha3.Stack -- err error -- ) -- resolved := stackToV1alpha3(internalStack) -- if stack, err = s.stacks.Get(internalStack.Name, metav1.GetOptions{}); err == nil { -- stack.Spec = resolved.Spec -- stack, err = s.stacks.Update(stack) -- } else { -- // Or create it -- stack, err = s.stacks.Create(resolved) -- } -- if err != nil { -- deleteChildResources(childResources) -- return err -- } -- blockOwnerDeletion := true -- isController := true -- return setChildResourcesOwner(childResources, metav1.OwnerReference{ -- APIVersion: v1alpha3.SchemeGroupVersion.String(), -- Kind: "Stack", -- Name: stack.Name, -- UID: stack.UID, -- BlockOwnerDeletion: &blockOwnerDeletion, -- Controller: &isController, -- }) --} -- --func (s *stackV1Alpha3) Delete(name string) error { -- return s.stacks.Delete(name, &metav1.DeleteOptions{}) --} -- --func (s *stackV1Alpha3) Get(name string) (Stack, error) { -- stackAlpha3, err := s.stacks.Get(name, metav1.GetOptions{}) -- if err != nil { -- return Stack{}, err -- } -- return stackFromV1alpha3(stackAlpha3), nil --} -- --func (s *stackV1Alpha3) List(opts metav1.ListOptions) ([]Stack, error) { -- list, err := s.stacks.List(opts) -- if err != nil { -- return nil, err -- } -- stacks := make([]Stack, len(list.Items)) -- for i := range list.Items { -- stacks[i] = stackFromV1alpha3(&list.Items[i]) -- } -- return stacks, nil --} -- --// IsColliding is handle server side with the compose api v1beta2, so nothing to do here --func (s *stackV1Alpha3) IsColliding(servicesClient corev1.ServiceInterface, st Stack) error { -- return nil --} -diff --git a/cli/command/stack/kubernetes/stackclient_test.go b/cli/command/stack/kubernetes/stackclient_test.go -deleted file mode 100644 -index 354d8c38977..00000000000 ---- a/cli/command/stack/kubernetes/stackclient_test.go -+++ /dev/null -@@ -1,60 +0,0 @@ --package kubernetes -- --import ( -- "io/ioutil" -- "testing" -- -- composetypes "github.com/docker/cli/cli/compose/types" -- "gotest.tools/v3/assert" --) -- --func TestFromCompose(t *testing.T) { -- stackClient := &stackV1Beta1{} -- s, err := stackClient.FromCompose(ioutil.Discard, "foo", &composetypes.Config{ -- Version: "3.1", -- Filename: "banana", -- Services: []composetypes.ServiceConfig{ -- { -- Name: "foo", -- Image: "foo", -- }, -- { -- Name: "bar", -- Image: "bar", -- }, -- }, -- }) -- assert.NilError(t, err) -- assert.Equal(t, "foo", s.Name) -- assert.Equal(t, string(`version: "3.5" --services: -- bar: -- image: bar -- foo: -- image: foo --`), s.ComposeFile) --} -- --func TestFromComposeUnsupportedVersion(t *testing.T) { -- stackClient := &stackV1Beta1{} -- _, err := stackClient.FromCompose(ioutil.Discard, "foo", &composetypes.Config{ -- Version: "3.6", -- Filename: "banana", -- Services: []composetypes.ServiceConfig{ -- { -- Name: "foo", -- Image: "foo", -- Volumes: []composetypes.ServiceVolumeConfig{ -- { -- Type: "tmpfs", -- Target: "/app", -- Tmpfs: &composetypes.ServiceVolumeTmpfs{ -- Size: 10000, -- }, -- }, -- }, -- }, -- }, -- }) -- assert.ErrorContains(t, err, "the compose yaml file is invalid with v3.5: services.foo.volumes.0 Additional property tmpfs is not allowed") --} -diff --git a/cli/command/stack/kubernetes/testdata/compose-with-expose.yml b/cli/command/stack/kubernetes/testdata/compose-with-expose.yml -deleted file mode 100644 -index 4d0b6f7e7a2..00000000000 ---- a/cli/command/stack/kubernetes/testdata/compose-with-expose.yml -+++ /dev/null -@@ -1,9 +0,0 @@ --version: "3.7" --services: -- test: -- image: "some-image" -- expose: -- - "1" # default protocol, single port -- - "2-4" # default protocol, port range -- - "5/udp" # specific protocol, single port -- - "6-8/udp" # specific protocol, port range -\ No newline at end of file -diff --git a/cli/command/stack/kubernetes/testdata/compose-with-pull-policy.yml b/cli/command/stack/kubernetes/testdata/compose-with-pull-policy.yml -deleted file mode 100644 -index 471988f574b..00000000000 ---- a/cli/command/stack/kubernetes/testdata/compose-with-pull-policy.yml -+++ /dev/null -@@ -1,6 +0,0 @@ --version: "3.7" --services: -- test: -- image: "some-image" -- x-kubernetes: -- pull_policy: "Never" -\ No newline at end of file -diff --git a/cli/command/stack/kubernetes/testdata/compose-with-pull-secret.yml b/cli/command/stack/kubernetes/testdata/compose-with-pull-secret.yml -deleted file mode 100644 -index 8c2653787d1..00000000000 ---- a/cli/command/stack/kubernetes/testdata/compose-with-pull-secret.yml -+++ /dev/null -@@ -1,6 +0,0 @@ --version: "3.7" --services: -- test: -- image: "some-private-image" -- x-kubernetes: -- pull_secret: "some-secret" -\ No newline at end of file -diff --git a/cli/command/stack/kubernetes/testdata/config b/cli/command/stack/kubernetes/testdata/config -deleted file mode 100644 -index 6ce433f2998..00000000000 ---- a/cli/command/stack/kubernetes/testdata/config -+++ /dev/null -@@ -1 +0,0 @@ --this is a config -\ No newline at end of file -diff --git a/cli/command/stack/kubernetes/testdata/secret b/cli/command/stack/kubernetes/testdata/secret -deleted file mode 100644 -index 51ed8039237..00000000000 ---- a/cli/command/stack/kubernetes/testdata/secret -+++ /dev/null -@@ -1 +0,0 @@ --this is a secret -\ No newline at end of file -diff --git a/cli/command/stack/kubernetes/testdata/warnings.golden b/cli/command/stack/kubernetes/testdata/warnings.golden -deleted file mode 100644 -index 1ee14b0fc92..00000000000 ---- a/cli/command/stack/kubernetes/testdata/warnings.golden -+++ /dev/null -@@ -1,31 +0,0 @@ --top-level network "global" is ignored --service "front": network "private" is ignored --service "front": update_config.delay is not supported --service "front": update_config.failure_action is not supported --service "front": update_config.monitor is not supported --service "front": update_config.max_failure_ratio is not supported --service "front": restart_policy.delay is ignored --service "front": restart_policy.max_attempts is ignored --service "front": restart_policy.window is ignored --service "front": container_name is deprecated --service "front": expose is deprecated --service "front": build is ignored --service "front": cgroup_parent is ignored --service "front": devices are ignored --service "front": domainname is ignored --service "front": external_links are ignored --service "front": links are ignored --service "front": mac_address is ignored --service "front": network_mode is ignored --service "front": restart is ignored --service "front": security_opt are ignored --service "front": ulimits are ignored --service "front": depends_on are ignored --service "front": credential_spec is ignored --service "front": dns are ignored --service "front": dns_search are ignored --service "front": env_file are ignored --service "front": stop_signal is ignored --service "front": logging is ignored --service "front": volume.propagation is ignored --service "front": volume.nocopy is ignored -diff --git a/cli/command/stack/kubernetes/warnings.go b/cli/command/stack/kubernetes/warnings.go -deleted file mode 100644 -index eb4598db45d..00000000000 ---- a/cli/command/stack/kubernetes/warnings.go -+++ /dev/null -@@ -1,145 +0,0 @@ --package kubernetes -- --import ( -- "fmt" -- "io" -- -- composetypes "github.com/docker/cli/cli/compose/types" --) -- --func warnUnsupportedFeatures(stderr io.Writer, cfg *composetypes.Config) { -- warnForGlobalNetworks(stderr, cfg) -- for _, s := range cfg.Services { -- warnForServiceNetworks(stderr, s) -- warnForUnsupportedDeploymentStrategy(stderr, s) -- warnForUnsupportedRestartPolicy(stderr, s) -- warnForDeprecatedProperties(stderr, s) -- warnForUnsupportedProperties(stderr, s) -- } --} -- --func warnForGlobalNetworks(stderr io.Writer, config *composetypes.Config) { -- for network := range config.Networks { -- fmt.Fprintf(stderr, "top-level network %q is ignored\n", network) -- } --} -- --func warnServicef(stderr io.Writer, service, format string, args ...interface{}) { -- fmt.Fprintf(stderr, "service "%s": %s\n", service, fmt.Sprintf(format, args...)) --} -- --func warnForServiceNetworks(stderr io.Writer, s composetypes.ServiceConfig) { -- for network := range s.Networks { -- warnServicef(stderr, s.Name, "network %q is ignored", network) -- } --} -- --func warnForDeprecatedProperties(stderr io.Writer, s composetypes.ServiceConfig) { -- if s.ContainerName != "" { -- warnServicef(stderr, s.Name, "container_name is deprecated") -- } -- if len(s.Expose) > 0 { -- warnServicef(stderr, s.Name, "expose is deprecated") -- } --} -- --func warnForUnsupportedDeploymentStrategy(stderr io.Writer, s composetypes.ServiceConfig) { -- config := s.Deploy.UpdateConfig -- if config == nil { -- return -- } -- if config.Delay != 0 { -- warnServicef(stderr, s.Name, "update_config.delay is not supported") -- } -- if config.FailureAction != "" { -- warnServicef(stderr, s.Name, "update_config.failure_action is not supported") -- } -- if config.Monitor != 0 { -- warnServicef(stderr, s.Name, "update_config.monitor is not supported") -- } -- if config.MaxFailureRatio != 0 { -- warnServicef(stderr, s.Name, "update_config.max_failure_ratio is not supported") -- } --} -- --func warnForUnsupportedRestartPolicy(stderr io.Writer, s composetypes.ServiceConfig) { -- policy := s.Deploy.RestartPolicy -- if policy == nil { -- return -- } -- -- if policy.Delay != nil { -- warnServicef(stderr, s.Name, "restart_policy.delay is ignored") -- } -- if policy.MaxAttempts != nil { -- warnServicef(stderr, s.Name, "restart_policy.max_attempts is ignored") -- } -- if policy.Window != nil { -- warnServicef(stderr, s.Name, "restart_policy.window is ignored") -- } --} -- --func warnForUnsupportedProperties(stderr io.Writer, s composetypes.ServiceConfig) { // nolint: gocyclo -- if build := s.Build; build.Context != "" || build.Dockerfile != "" || len(build.Args) > 0 || len(build.Labels) > 0 || len(build.CacheFrom) > 0 || build.Network != "" || build.Target != "" { -- warnServicef(stderr, s.Name, "build is ignored") -- } -- if s.CgroupParent != "" { -- warnServicef(stderr, s.Name, "cgroup_parent is ignored") -- } -- if len(s.Devices) > 0 { -- warnServicef(stderr, s.Name, "devices are ignored") -- } -- if s.DomainName != "" { -- warnServicef(stderr, s.Name, "domainname is ignored") -- } -- if len(s.ExternalLinks) > 0 { -- warnServicef(stderr, s.Name, "external_links are ignored") -- } -- if len(s.Links) > 0 { -- warnServicef(stderr, s.Name, "links are ignored") -- } -- if s.MacAddress != "" { -- warnServicef(stderr, s.Name, "mac_address is ignored") -- } -- if s.NetworkMode != "" { -- warnServicef(stderr, s.Name, "network_mode is ignored") -- } -- if s.Restart != "" { -- warnServicef(stderr, s.Name, "restart is ignored") -- } -- if len(s.SecurityOpt) > 0 { -- warnServicef(stderr, s.Name, "security_opt are ignored") -- } -- if len(s.Ulimits) > 0 { -- warnServicef(stderr, s.Name, "ulimits are ignored") -- } -- if len(s.DependsOn) > 0 { -- warnServicef(stderr, s.Name, "depends_on are ignored") -- } -- if s.CredentialSpec.File != "" { -- warnServicef(stderr, s.Name, "credential_spec is ignored") -- } -- if len(s.DNS) > 0 { -- warnServicef(stderr, s.Name, "dns are ignored") -- } -- if len(s.DNSSearch) > 0 { -- warnServicef(stderr, s.Name, "dns_search are ignored") -- } -- if len(s.EnvFile) > 0 { -- warnServicef(stderr, s.Name, "env_file are ignored") -- } -- if s.StopSignal != "" { -- warnServicef(stderr, s.Name, "stop_signal is ignored") -- } -- if s.Logging != nil { -- warnServicef(stderr, s.Name, "logging is ignored") -- } -- for _, m := range s.Volumes { -- if m.Volume != nil && m.Volume.NoCopy { -- warnServicef(stderr, s.Name, "volume.nocopy is ignored") -- } -- if m.Bind != nil && m.Bind.Propagation != "" { -- warnServicef(stderr, s.Name, "volume.propagation is ignored") -- } -- } --} -diff --git a/cli/command/stack/kubernetes/warnings_test.go b/cli/command/stack/kubernetes/warnings_test.go -deleted file mode 100644 -index 755f4e6d6a7..00000000000 ---- a/cli/command/stack/kubernetes/warnings_test.go -+++ /dev/null -@@ -1,78 +0,0 @@ --package kubernetes -- --import ( -- "bytes" -- "testing" -- "time" -- -- composetypes "github.com/docker/cli/cli/compose/types" -- "gotest.tools/v3/golden" --) -- --func TestWarnings(t *testing.T) { -- duration := composetypes.Duration(5 * time.Second) -- attempts := uint64(3) -- config := &composetypes.Config{ -- Version: "3.4", -- Services: []composetypes.ServiceConfig{ -- { -- Name: "front", -- Build: composetypes.BuildConfig{ -- Context: "ignored", -- }, -- ContainerName: "ignored", -- CgroupParent: "ignored", -- CredentialSpec: composetypes.CredentialSpecConfig{File: "ignored"}, -- DependsOn: []string{"ignored"}, -- Deploy: composetypes.DeployConfig{ -- UpdateConfig: &composetypes.UpdateConfig{ -- Delay: composetypes.Duration(5 * time.Second), -- FailureAction: "rollback", -- Monitor: composetypes.Duration(10 * time.Second), -- MaxFailureRatio: 0.5, -- }, -- RestartPolicy: &composetypes.RestartPolicy{ -- Delay: &duration, -- MaxAttempts: &attempts, -- Window: &duration, -- }, -- }, -- Devices: []string{"ignored"}, -- DNSSearch: []string{"ignored"}, -- DNS: []string{"ignored"}, -- DomainName: "ignored", -- EnvFile: []string{"ignored"}, -- Expose: []string{"80"}, -- ExternalLinks: []string{"ignored"}, -- Image: "dockerdemos/front", -- Links: []string{"ignored"}, -- Logging: &composetypes.LoggingConfig{Driver: "syslog"}, -- MacAddress: "ignored", -- Networks: map[string]*composetypes.ServiceNetworkConfig{"private": {}}, -- NetworkMode: "ignored", -- Restart: "ignored", -- SecurityOpt: []string{"ignored"}, -- StopSignal: "ignored", -- Ulimits: map[string]*composetypes.UlimitsConfig{"nproc": {Hard: 65535}}, -- User: "ignored", -- Volumes: []composetypes.ServiceVolumeConfig{ -- { -- Type: "bind", -- Bind: &composetypes.ServiceVolumeBind{Propagation: "ignored"}, -- }, -- { -- Type: "volume", -- Volume: &composetypes.ServiceVolumeVolume{NoCopy: true}, -- }, -- }, -- }, -- }, -- Networks: map[string]composetypes.NetworkConfig{ -- "global": {}, -- }, -- } -- var buf bytes.Buffer -- warnUnsupportedFeatures(&buf, config) -- warnings := buf.String() -- golden.Assert(t, warnings, "warnings.golden") --} -diff --git a/cli/command/stack/kubernetes/watcher.go b/cli/command/stack/kubernetes/watcher.go -deleted file mode 100644 -index 4e73287d5c3..00000000000 ---- a/cli/command/stack/kubernetes/watcher.go -+++ /dev/null -@@ -1,262 +0,0 @@ --package kubernetes -- --import ( -- "context" -- "sync" -- "time" -- -- apiv1beta1 "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" -- "github.com/docker/compose-on-kubernetes/api/labels" -- "github.com/pkg/errors" -- apiv1 "k8s.io/api/core/v1" -- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -- "k8s.io/apimachinery/pkg/fields" -- "k8s.io/apimachinery/pkg/runtime" -- runtimeutil "k8s.io/apimachinery/pkg/util/runtime" -- "k8s.io/apimachinery/pkg/watch" -- cache "k8s.io/client-go/tools/cache" --) -- --type stackListWatch interface { -- List(opts metav1.ListOptions) (*apiv1beta1.StackList, error) -- Watch(opts metav1.ListOptions) (watch.Interface, error) --} -- --type podListWatch interface { -- List(opts metav1.ListOptions) (*apiv1.PodList, error) -- Watch(opts metav1.ListOptions) (watch.Interface, error) --} -- --// DeployWatcher watches a stack deployement --type deployWatcher struct { -- pods podListWatch -- stacks stackListWatch --} -- --// Watch watches a stuck deployement and return a chan that will holds the state of the stack --func (w *deployWatcher) Watch(name string, serviceNames []string, statusUpdates chan serviceStatus) error { -- errC := make(chan error, 1) -- defer close(errC) -- -- handlers := runtimeutil.ErrorHandlers -- -- // informer errors are reported using global error handlers -- runtimeutil.ErrorHandlers = append(handlers, func(err error) { -- errC <- err -- }) -- defer func() { -- runtimeutil.ErrorHandlers = handlers -- }() -- -- ctx, cancel := context.WithCancel(context.Background()) -- wg := sync.WaitGroup{} -- defer func() { -- cancel() -- wg.Wait() -- }() -- wg.Add(2) -- go func() { -- defer wg.Done() -- w.watchStackStatus(ctx, name, errC) -- }() -- go func() { -- defer wg.Done() -- w.waitForPods(ctx, name, serviceNames, errC, statusUpdates) -- }() -- -- return <-errC --} -- --type stackWatcher struct { -- resultChan chan error -- stackName string --} -- --var _ cache.ResourceEventHandler = &stackWatcher{} -- --func (sw *stackWatcher) OnAdd(obj interface{}) { -- stack, ok := obj.(*apiv1beta1.Stack) -- switch { -- case !ok: -- sw.resultChan <- errors.Errorf("stack %s has incorrect type", sw.stackName) -- case stack.Status.Phase == apiv1beta1.StackFailure: -- sw.resultChan <- errors.Errorf("stack %s failed with status %s: %s", sw.stackName, stack.Status.Phase, stack.Status.Message) -- } --} -- --func (sw *stackWatcher) OnUpdate(oldObj, newObj interface{}) { -- sw.OnAdd(newObj) --} -- --func (sw *stackWatcher) OnDelete(obj interface{}) { --} -- --func (w *deployWatcher) watchStackStatus(ctx context.Context, stackname string, e chan error) { -- informer := newStackInformer(w.stacks, stackname) -- sw := &stackWatcher{ -- resultChan: e, -- } -- informer.AddEventHandler(sw) -- informer.Run(ctx.Done()) --} -- --type serviceStatus struct { -- name string -- podsPending int -- podsRunning int -- podsSucceeded int -- podsFailed int -- podsUnknown int -- podsReady int -- podsTotal int --} -- --type podWatcher struct { -- stackName string -- services map[string]serviceStatus -- resultChan chan error -- starts map[string]int32 -- indexer cache.Indexer -- statusUpdates chan serviceStatus --} -- --var _ cache.ResourceEventHandler = &podWatcher{} -- --func (pw *podWatcher) handlePod(obj interface{}) { -- pod, ok := obj.(*apiv1.Pod) -- if !ok { -- pw.resultChan <- errors.Errorf("Pod has incorrect type in stack %s", pw.stackName) -- return -- } -- serviceName := pod.Labels[labels.ForServiceName] -- pw.updateServiceStatus(serviceName) -- if pw.allReady() { -- select { -- case pw.resultChan <- nil: -- default: -- // result has already been reported, just don't block -- } -- } --} -- --func (pw *podWatcher) updateServiceStatus(serviceName string) { -- pods, _ := pw.indexer.ByIndex("byservice", serviceName) -- status := serviceStatus{name: serviceName} -- for _, obj := range pods { -- if pod, ok := obj.(*apiv1.Pod); ok { -- switch pod.Status.Phase { -- case apiv1.PodPending: -- status.podsPending++ -- case apiv1.PodRunning: -- status.podsRunning++ -- case apiv1.PodSucceeded: -- status.podsSucceeded++ -- case apiv1.PodFailed: -- status.podsFailed++ -- case apiv1.PodUnknown: -- status.podsUnknown++ -- } -- if pw.isPodReady(pod) { -- status.podsReady++ -- } -- } -- } -- status.podsTotal = len(pods) -- oldStatus := pw.services[serviceName] -- if oldStatus != status { -- pw.statusUpdates <- status -- } -- pw.services[serviceName] = status --} -- --func (pw *podWatcher) isPodReady(pod *apiv1.Pod) bool { -- for _, condition := range pod.Status.Conditions { -- if condition.Status == apiv1.ConditionTrue && condition.Type == apiv1.PodReady { -- return true -- } -- } -- return false --} -- --func (pw *podWatcher) allReady() bool { -- for _, status := range pw.services { -- if status.podsReady == 0 { -- return false -- } -- } -- return true --} -- --func (pw *podWatcher) OnAdd(obj interface{}) { -- pw.handlePod(obj) --} -- --func (pw *podWatcher) OnUpdate(oldObj, newObj interface{}) { -- pw.handlePod(newObj) --} -- --func (pw *podWatcher) OnDelete(obj interface{}) { -- pw.handlePod(obj) --} -- --func (w *deployWatcher) waitForPods(ctx context.Context, stackName string, serviceNames []string, e chan error, statusUpdates chan serviceStatus) { -- informer := newPodInformer(w.pods, stackName, cache.Indexers{ -- "byservice": func(obj interface{}) ([]string, error) { -- pod, ok := obj.(*apiv1.Pod) -- if !ok { -- return nil, errors.Errorf("Pod has incorrect type in stack %s", stackName) -- } -- return []string{pod.Labels[labels.ForServiceName]}, nil -- }}) -- services := map[string]serviceStatus{} -- for _, name := range serviceNames { -- services[name] = serviceStatus{name: name} -- } -- pw := &podWatcher{ -- stackName: stackName, -- services: services, -- resultChan: e, -- starts: map[string]int32{}, -- indexer: informer.GetIndexer(), -- statusUpdates: statusUpdates, -- } -- informer.AddEventHandler(pw) -- informer.Run(ctx.Done()) --} -- --func newPodInformer(podsClient podListWatch, stackName string, indexers cache.Indexers) cache.SharedIndexInformer { -- return cache.NewSharedIndexInformer( -- &cache.ListWatch{ -- ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { -- options.LabelSelector = labels.SelectorForStack(stackName) -- return podsClient.List(options) -- }, -- -- WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { -- options.LabelSelector = labels.SelectorForStack(stackName) -- return podsClient.Watch(options) -- }, -- }, -- &apiv1.Pod{}, -- time.Second*5, -- indexers, -- ) --} -- --func newStackInformer(stacksClient stackListWatch, stackName string) cache.SharedInformer { -- return cache.NewSharedInformer( -- &cache.ListWatch{ -- ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { -- options.FieldSelector = fields.OneTermEqualSelector("metadata.name", stackName).String() -- return stacksClient.List(options) -- }, -- -- WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { -- options.FieldSelector = fields.OneTermEqualSelector("metadata.name", stackName).String() -- return stacksClient.Watch(options) -- }, -- }, -- &apiv1beta1.Stack{}, -- time.Second*5, -- ) --} -diff --git a/cli/command/stack/kubernetes/watcher_test.go b/cli/command/stack/kubernetes/watcher_test.go -deleted file mode 100644 -index 71ba7e44d51..00000000000 ---- a/cli/command/stack/kubernetes/watcher_test.go -+++ /dev/null -@@ -1,220 +0,0 @@ --package kubernetes -- --import ( -- "testing" -- -- apiv1beta1 "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" -- composelabels "github.com/docker/compose-on-kubernetes/api/labels" -- "gotest.tools/v3/assert" -- apiv1 "k8s.io/api/core/v1" -- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -- "k8s.io/apimachinery/pkg/labels" -- "k8s.io/apimachinery/pkg/runtime" -- "k8s.io/apimachinery/pkg/runtime/schema" -- "k8s.io/apimachinery/pkg/runtime/serializer" -- "k8s.io/apimachinery/pkg/watch" -- k8stesting "k8s.io/client-go/testing" --) -- --var podsResource = apiv1.SchemeGroupVersion.WithResource("pods") --var podKind = apiv1.SchemeGroupVersion.WithKind("Pod") --var stacksResource = apiv1beta1.SchemeGroupVersion.WithResource("stacks") --var stackKind = apiv1beta1.SchemeGroupVersion.WithKind("Stack") -- --type testPodAndStackRepository struct { -- fake *k8stesting.Fake --} -- --func (r *testPodAndStackRepository) stackListWatchForNamespace(ns string) *testStackListWatch { -- return &testStackListWatch{fake: r.fake, ns: ns} --} --func (r *testPodAndStackRepository) podListWatchForNamespace(ns string) *testPodListWatch { -- return &testPodListWatch{fake: r.fake, ns: ns} --} -- --func newTestPodAndStackRepository(initialPods []apiv1.Pod, initialStacks []apiv1beta1.Stack, podWatchHandler, stackWatchHandler k8stesting.WatchReactionFunc) *testPodAndStackRepository { -- var scheme = runtime.NewScheme() -- var codecs = serializer.NewCodecFactory(scheme) -- metav1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) -- apiv1.AddToScheme(scheme) -- apiv1beta1.AddToScheme(scheme) -- -- o := k8stesting.NewObjectTracker(scheme, codecs.UniversalDecoder()) -- for _, obj := range initialPods { -- obj := obj -- if err := o.Add(&obj); err != nil { -- panic(err) -- } -- } -- for _, obj := range initialStacks { -- obj := obj -- if err := o.Add(&obj); err != nil { -- panic(err) -- } -- } -- fakePtr := &k8stesting.Fake{} -- fakePtr.AddReactor("*", "*", k8stesting.ObjectReaction(o)) -- if podWatchHandler != nil { -- fakePtr.AddWatchReactor(podsResource.Resource, podWatchHandler) -- } -- if stackWatchHandler != nil { -- fakePtr.AddWatchReactor(stacksResource.Resource, stackWatchHandler) -- } -- fakePtr.AddWatchReactor("*", k8stesting.DefaultWatchReactor(watch.NewFake(), nil)) -- return &testPodAndStackRepository{fake: fakePtr} --} -- --type testStackListWatch struct { -- fake *k8stesting.Fake -- ns string --} -- --func (s *testStackListWatch) List(opts metav1.ListOptions) (*apiv1beta1.StackList, error) { -- obj, err := s.fake.Invokes(k8stesting.NewListAction(stacksResource, stackKind, s.ns, opts), &apiv1beta1.StackList{}) -- -- if obj == nil { -- return nil, err -- } -- -- label, _, _ := k8stesting.ExtractFromListOptions(opts) -- if label == nil { -- label = labels.Everything() -- } -- list := &apiv1beta1.StackList{} -- for _, item := range obj.(*apiv1beta1.StackList).Items { -- if label.Matches(labels.Set(item.Labels)) { -- list.Items = append(list.Items, item) -- } -- } -- return list, err --} --func (s *testStackListWatch) Watch(opts metav1.ListOptions) (watch.Interface, error) { -- return s.fake.InvokesWatch(k8stesting.NewWatchAction(stacksResource, s.ns, opts)) --} -- --type testPodListWatch struct { -- fake *k8stesting.Fake -- ns string --} -- --func (p *testPodListWatch) List(opts metav1.ListOptions) (*apiv1.PodList, error) { -- obj, err := p.fake.Invokes(k8stesting.NewListAction(podsResource, podKind, p.ns, opts), &apiv1.PodList{}) -- -- if obj == nil { -- return nil, err -- } -- -- label, _, _ := k8stesting.ExtractFromListOptions(opts) -- if label == nil { -- label = labels.Everything() -- } -- list := &apiv1.PodList{} -- for _, item := range obj.(*apiv1.PodList).Items { -- if label.Matches(labels.Set(item.Labels)) { -- list.Items = append(list.Items, item) -- } -- } -- return list, err -- --} --func (p *testPodListWatch) Watch(opts metav1.ListOptions) (watch.Interface, error) { -- return p.fake.InvokesWatch(k8stesting.NewWatchAction(podsResource, p.ns, opts)) --} -- --func TestDeployWatchOk(t *testing.T) { -- stack := apiv1beta1.Stack{ -- ObjectMeta: metav1.ObjectMeta{Name: "test-stack", Namespace: "test-ns"}, -- } -- -- serviceNames := []string{"svc1", "svc2"} -- testRepo := newTestPodAndStackRepository(nil, []apiv1beta1.Stack{stack}, func(action k8stesting.Action) (handled bool, ret watch.Interface, err error) { -- res := watch.NewFake() -- go func() { -- pod1 := &apiv1.Pod{ -- ObjectMeta: metav1.ObjectMeta{ -- Name: "test1", -- Namespace: "test-ns", -- Labels: composelabels.ForService("test-stack", "svc1"), -- }, -- Status: apiv1.PodStatus{ -- Phase: apiv1.PodRunning, -- Conditions: []apiv1.PodCondition{ -- { -- Type: apiv1.PodReady, -- Status: apiv1.ConditionTrue, -- }, -- }, -- }, -- } -- pod2 := &apiv1.Pod{ -- ObjectMeta: metav1.ObjectMeta{ -- Name: "test2", -- Namespace: "test-ns", -- Labels: composelabels.ForService("test-stack", "svc2"), -- }, -- Status: apiv1.PodStatus{ -- Phase: apiv1.PodRunning, -- Conditions: []apiv1.PodCondition{ -- { -- Type: apiv1.PodReady, -- Status: apiv1.ConditionTrue, -- }, -- }, -- }, -- } -- res.Add(pod1) -- res.Add(pod2) -- }() -- -- return true, res, nil -- }, nil) -- -- testee := &deployWatcher{ -- stacks: testRepo.stackListWatchForNamespace("test-ns"), -- pods: testRepo.podListWatchForNamespace("test-ns"), -- } -- -- statusUpdates := make(chan serviceStatus) -- go func() { -- for range statusUpdates { -- } -- }() -- defer close(statusUpdates) -- err := testee.Watch(stack.Name, serviceNames, statusUpdates) -- assert.NilError(t, err) --} -- --func TestDeployReconcileFailure(t *testing.T) { -- stack := apiv1beta1.Stack{ -- ObjectMeta: metav1.ObjectMeta{Name: "test-stack", Namespace: "test-ns"}, -- } -- -- serviceNames := []string{"svc1", "svc2"} -- testRepo := newTestPodAndStackRepository(nil, []apiv1beta1.Stack{stack}, nil, func(action k8stesting.Action) (handled bool, ret watch.Interface, err error) { -- res := watch.NewFake() -- go func() { -- sfailed := stack -- sfailed.Status = apiv1beta1.StackStatus{ -- Phase: apiv1beta1.StackFailure, -- Message: "test error", -- } -- res.Modify(&sfailed) -- }() -- -- return true, res, nil -- }) -- -- testee := &deployWatcher{ -- stacks: testRepo.stackListWatchForNamespace("test-ns"), -- pods: testRepo.podListWatchForNamespace("test-ns"), -- } -- -- statusUpdates := make(chan serviceStatus) -- go func() { -- for range statusUpdates { -- } -- }() -- defer close(statusUpdates) -- err := testee.Watch(stack.Name, serviceNames, statusUpdates) -- assert.ErrorContains(t, err, "Failure: test error") --} -diff --git a/cli/command/stack/list.go b/cli/command/stack/list.go -index 5175ea371c8..861ae1be2fb 100644 ---- a/cli/command/stack/list.go -+++ b/cli/command/stack/list.go -@@ -6,7 +6,6 @@ import ( - "github.com/docker/cli/cli" - "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/command/stack/formatter" -- "github.com/docker/cli/cli/command/stack/kubernetes" - "github.com/docker/cli/cli/command/stack/options" - "github.com/docker/cli/cli/command/stack/swarm" - "github.com/fvbommel/sortorder" -@@ -28,10 +27,6 @@ func newListCommand(dockerCli command.Cli, common *commonOptions) *cobra.Command - - flags := cmd.Flags() - flags.StringVar(&opts.Format, "format", "", "Pretty-print stacks using a Go template") -- flags.StringSliceVar(&opts.Namespaces, "namespace", []string{}, "Kubernetes namespaces to use") -- flags.SetAnnotation("namespace", "kubernetes", nil) -- flags.BoolVarP(&opts.AllNamespaces, "all-namespaces", "", false, "List stacks from all Kubernetes namespaces") -- flags.SetAnnotation("all-namespaces", "kubernetes", nil) - return cmd - } - -@@ -45,27 +40,13 @@ func RunList(cmd *cobra.Command, dockerCli command.Cli, opts options.List, orche - } - stacks = append(stacks, ss...) - } -- if orchestrator.HasKubernetes() { -- kubeCli, err := kubernetes.WrapCli(dockerCli, kubernetes.NewOptions(cmd.Flags(), orchestrator)) -- if err != nil { -- return err -- } -- ss, err := kubernetes.GetStacks(kubeCli, opts) -- if err != nil { -- return err -- } -- stacks = append(stacks, ss...) -- } -- return format(dockerCli, opts, orchestrator, stacks) -+ return format(dockerCli, opts, stacks) - } - --func format(dockerCli command.Cli, opts options.List, orchestrator command.Orchestrator, stacks []*formatter.Stack) error { -+func format(dockerCli command.Cli, opts options.List, stacks []*formatter.Stack) error { - format := formatter.Format(opts.Format) - if format == "" || format == formatter.TableFormatKey { - format = formatter.SwarmStackTableFormat -- if orchestrator.HasKubernetes() { -- format = formatter.KubernetesStackTableFormat -- } - } - stackCtx := formatter.Context{ - Output: dockerCli.Out(), -@@ -73,8 +54,7 @@ func format(dockerCli command.Cli, opts options.List, orchestrator command.Orche - } - sort.Slice(stacks, func(i, j int) bool { - return sortorder.NaturalLess(stacks[i].Name, stacks[j].Name) || -- !sortorder.NaturalLess(stacks[j].Name, stacks[i].Name) && -- sortorder.NaturalLess(stacks[j].Namespace, stacks[i].Namespace) -+ !sortorder.NaturalLess(stacks[j].Name, stacks[i].Name) - }) - return formatter.StackWrite(stackCtx, stacks) - } -diff --git a/cli/command/stack/options/opts.go b/cli/command/stack/options/opts.go -index fb45dc45580..9842b4995c0 100644 ---- a/cli/command/stack/options/opts.go -+++ b/cli/command/stack/options/opts.go -@@ -15,7 +15,6 @@ type Deploy struct { - type List struct { - Format string - AllNamespaces bool -- Namespaces []string - } - - // PS holds docker stack ps options -diff --git a/cli/command/stack/ps.go b/cli/command/stack/ps.go -index ae692d4e13c..6d77d9f0065 100644 ---- a/cli/command/stack/ps.go -+++ b/cli/command/stack/ps.go -@@ -3,7 +3,6 @@ package stack - import ( - "github.com/docker/cli/cli" - "github.com/docker/cli/cli/command" -- "github.com/docker/cli/cli/command/stack/kubernetes" - "github.com/docker/cli/cli/command/stack/options" - "github.com/docker/cli/cli/command/stack/swarm" - cliopts "github.com/docker/cli/opts" -@@ -32,13 +31,10 @@ func newPsCommand(dockerCli command.Cli, common *commonOptions) *cobra.Command { - flags.VarP(&opts.Filter, "filter", "f", "Filter output based on conditions provided") - flags.BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display task IDs") - flags.StringVar(&opts.Format, "format", "", "Pretty-print tasks using a Go template") -- kubernetes.AddNamespaceFlag(flags) - return cmd - } - - // RunPs performs a stack ps against the specified orchestrator - func RunPs(dockerCli command.Cli, flags *pflag.FlagSet, commonOrchestrator command.Orchestrator, opts options.PS) error { -- return runOrchestratedCommand(dockerCli, flags, commonOrchestrator, -- func() error { return swarm.RunPS(dockerCli, opts) }, -- func(kli *kubernetes.KubeCli) error { return kubernetes.RunPS(kli, opts) }) -+ return swarm.RunPS(dockerCli, opts) - } -diff --git a/cli/command/stack/remove.go b/cli/command/stack/remove.go -index a8dceff2bb3..4ffe73ec249 100644 ---- a/cli/command/stack/remove.go -+++ b/cli/command/stack/remove.go -@@ -3,7 +3,6 @@ package stack - import ( - "github.com/docker/cli/cli" - "github.com/docker/cli/cli/command" -- "github.com/docker/cli/cli/command/stack/kubernetes" - "github.com/docker/cli/cli/command/stack/options" - "github.com/docker/cli/cli/command/stack/swarm" - "github.com/spf13/cobra" -@@ -26,14 +25,10 @@ func newRemoveCommand(dockerCli command.Cli, common *commonOptions) *cobra.Comma - return RunRemove(dockerCli, cmd.Flags(), common.Orchestrator(), opts) - }, - } -- flags := cmd.Flags() -- kubernetes.AddNamespaceFlag(flags) - return cmd - } - - // RunRemove performs a stack remove against the specified orchestrator - func RunRemove(dockerCli command.Cli, flags *pflag.FlagSet, commonOrchestrator command.Orchestrator, opts options.Remove) error { -- return runOrchestratedCommand(dockerCli, flags, commonOrchestrator, -- func() error { return swarm.RunRemove(dockerCli, opts) }, -- func(kli *kubernetes.KubeCli) error { return kubernetes.RunRemove(kli, opts) }) -+ return swarm.RunRemove(dockerCli, opts) - } -diff --git a/cli/command/stack/services.go b/cli/command/stack/services.go -index 24ad3688871..a1e97b9f2e2 100644 ---- a/cli/command/stack/services.go -+++ b/cli/command/stack/services.go -@@ -8,7 +8,6 @@ import ( - "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/command/service" - "github.com/docker/cli/cli/command/stack/formatter" -- "github.com/docker/cli/cli/command/stack/kubernetes" - "github.com/docker/cli/cli/command/stack/options" - "github.com/docker/cli/cli/command/stack/swarm" - cliopts "github.com/docker/cli/opts" -@@ -37,7 +36,6 @@ func newServicesCommand(dockerCli command.Cli, common *commonOptions) *cobra.Com - flags.BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs") - flags.StringVar(&opts.Format, "format", "", "Pretty-print services using a Go template") - flags.VarP(&opts.Filter, "filter", "f", "Filter output based on conditions provided") -- kubernetes.AddNamespaceFlag(flags) - return cmd - } - -@@ -52,18 +50,7 @@ func RunServices(dockerCli command.Cli, flags *pflag.FlagSet, commonOrchestrator - - // GetServices returns the services for the specified orchestrator - func GetServices(dockerCli command.Cli, flags *pflag.FlagSet, commonOrchestrator command.Orchestrator, opts options.Services) ([]swarmtypes.Service, error) { -- switch { -- case commonOrchestrator.HasAll(): -- return nil, errUnsupportedAllOrchestrator -- case commonOrchestrator.HasKubernetes(): -- kli, err := kubernetes.WrapCli(dockerCli, kubernetes.NewOptions(flags, commonOrchestrator)) -- if err != nil { -- return nil, err -- } -- return kubernetes.GetServices(kli, opts) -- default: -- return swarm.GetServices(dockerCli, opts) -- } -+ return swarm.GetServices(dockerCli, opts) - } - - func formatWrite(dockerCli command.Cli, services []swarmtypes.Service, opts options.Services) error { -diff --git a/cli/context/kubernetes/constants.go b/cli/context/kubernetes/constants.go -deleted file mode 100644 -index 8998de989af..00000000000 ---- a/cli/context/kubernetes/constants.go -+++ /dev/null -@@ -1,6 +0,0 @@ --package kubernetes -- --const ( -- // KubernetesEndpoint is the kubernetes endpoint name in a stored context -- KubernetesEndpoint = "kubernetes" --) -diff --git a/cli/context/kubernetes/endpoint_test.go b/cli/context/kubernetes/endpoint_test.go -deleted file mode 100644 -index 89eeb9d61c5..00000000000 ---- a/cli/context/kubernetes/endpoint_test.go -+++ /dev/null -@@ -1,224 +0,0 @@ --package kubernetes -- --import ( -- "io/ioutil" -- "os" -- "testing" -- -- "github.com/docker/cli/cli/context" -- "github.com/docker/cli/cli/context/store" -- "gotest.tools/v3/assert" -- "k8s.io/client-go/tools/clientcmd" -- clientcmdapi "k8s.io/client-go/tools/clientcmd/api" --) -- --func testEndpoint(server, defaultNamespace string, ca, cert, key []byte, skipTLSVerify bool) Endpoint { -- var tlsData *context.TLSData -- if ca != nil || cert != nil || key != nil { -- tlsData = &context.TLSData{ -- CA: ca, -- Cert: cert, -- Key: key, -- } -- } -- return Endpoint{ -- EndpointMeta: EndpointMeta{ -- EndpointMetaBase: context.EndpointMetaBase{ -- Host: server, -- SkipTLSVerify: skipTLSVerify, -- }, -- DefaultNamespace: defaultNamespace, -- }, -- TLSData: tlsData, -- } --} -- --var testStoreCfg = store.NewConfig( -- func() interface{} { -- return &map[string]interface{}{} -- }, -- store.EndpointTypeGetter(KubernetesEndpoint, func() interface{} { return &EndpointMeta{} }), --) -- --func TestSaveLoadContexts(t *testing.T) { -- storeDir, err := ioutil.TempDir("", "test-load-save-k8-context") -- assert.NilError(t, err) -- defer os.RemoveAll(storeDir) -- store := store.New(storeDir, testStoreCfg) -- assert.NilError(t, save(store, testEndpoint("https://test", "test", nil, nil, nil, false), "raw-notls")) -- assert.NilError(t, save(store, testEndpoint("https://test", "test", nil, nil, nil, true), "raw-notls-skip")) -- assert.NilError(t, save(store, testEndpoint("https://test", "test", []byte("ca"), []byte("cert"), []byte("key"), true), "raw-tls")) -- -- kcFile, err := ioutil.TempFile(os.TempDir(), "test-load-save-k8-context") -- assert.NilError(t, err) -- defer os.Remove(kcFile.Name()) -- defer kcFile.Close() -- cfg := clientcmdapi.NewConfig() -- cfg.AuthInfos["user"] = clientcmdapi.NewAuthInfo() -- cfg.Contexts["context1"] = clientcmdapi.NewContext() -- cfg.Clusters["cluster1"] = clientcmdapi.NewCluster() -- cfg.Contexts["context2"] = clientcmdapi.NewContext() -- cfg.Clusters["cluster2"] = clientcmdapi.NewCluster() -- cfg.AuthInfos["user"].ClientCertificateData = []byte("cert") -- cfg.AuthInfos["user"].ClientKeyData = []byte("key") -- cfg.Clusters["cluster1"].Server = "https://server1" -- cfg.Clusters["cluster1"].InsecureSkipTLSVerify = true -- cfg.Clusters["cluster2"].Server = "https://server2" -- cfg.Clusters["cluster2"].CertificateAuthorityData = []byte("ca") -- cfg.Contexts["context1"].AuthInfo = "user" -- cfg.Contexts["context1"].Cluster = "cluster1" -- cfg.Contexts["context1"].Namespace = "namespace1" -- cfg.Contexts["context2"].AuthInfo = "user" -- cfg.Contexts["context2"].Cluster = "cluster2" -- cfg.Contexts["context2"].Namespace = "namespace2" -- cfg.CurrentContext = "context1" -- cfgData, err := clientcmd.Write(*cfg) -- assert.NilError(t, err) -- _, err = kcFile.Write(cfgData) -- assert.NilError(t, err) -- kcFile.Close() -- -- epDefault, err := FromKubeConfig(kcFile.Name(), "", "") -- assert.NilError(t, err) -- epContext2, err := FromKubeConfig(kcFile.Name(), "context2", "namespace-override") -- assert.NilError(t, err) -- assert.NilError(t, save(store, epDefault, "embed-default-context")) -- assert.NilError(t, save(store, epContext2, "embed-context2")) -- -- rawNoTLSMeta, err := store.GetMetadata("raw-notls") -- assert.NilError(t, err) -- rawNoTLSSkipMeta, err := store.GetMetadata("raw-notls-skip") -- assert.NilError(t, err) -- rawTLSMeta, err := store.GetMetadata("raw-tls") -- assert.NilError(t, err) -- embededDefaultMeta, err := store.GetMetadata("embed-default-context") -- assert.NilError(t, err) -- embededContext2Meta, err := store.GetMetadata("embed-context2") -- assert.NilError(t, err) -- -- rawNoTLS := EndpointFromContext(rawNoTLSMeta) -- rawNoTLSSkip := EndpointFromContext(rawNoTLSSkipMeta) -- rawTLS := EndpointFromContext(rawTLSMeta) -- embededDefault := EndpointFromContext(embededDefaultMeta) -- embededContext2 := EndpointFromContext(embededContext2Meta) -- -- rawNoTLSEP, err := rawNoTLS.WithTLSData(store, "raw-notls") -- assert.NilError(t, err) -- checkClientConfig(t, rawNoTLSEP, "https://test", "test", nil, nil, nil, false) -- rawNoTLSSkipEP, err := rawNoTLSSkip.WithTLSData(store, "raw-notls-skip") -- assert.NilError(t, err) -- checkClientConfig(t, rawNoTLSSkipEP, "https://test", "test", nil, nil, nil, true) -- rawTLSEP, err := rawTLS.WithTLSData(store, "raw-tls") -- assert.NilError(t, err) -- checkClientConfig(t, rawTLSEP, "https://test", "test", []byte("ca"), []byte("cert"), []byte("key"), true) -- embededDefaultEP, err := embededDefault.WithTLSData(store, "embed-default-context") -- assert.NilError(t, err) -- checkClientConfig(t, embededDefaultEP, "https://server1", "namespace1", nil, []byte("cert"), []byte("key"), true) -- embededContext2EP, err := embededContext2.WithTLSData(store, "embed-context2") -- assert.NilError(t, err) -- checkClientConfig(t, embededContext2EP, "https://server2", "namespace-override", []byte("ca"), []byte("cert"), []byte("key"), false) --} -- --func checkClientConfig(t *testing.T, ep Endpoint, server, namespace string, ca, cert, key []byte, skipTLSVerify bool) { -- config := ep.KubernetesConfig() -- cfg, err := config.ClientConfig() -- assert.NilError(t, err) -- ns, _, _ := config.Namespace() -- assert.Equal(t, server, cfg.Host) -- assert.Equal(t, namespace, ns) -- assert.DeepEqual(t, ca, cfg.CAData) -- assert.DeepEqual(t, cert, cfg.CertData) -- assert.DeepEqual(t, key, cfg.KeyData) -- assert.Equal(t, skipTLSVerify, cfg.Insecure) --} -- --func save(s store.Writer, ep Endpoint, name string) error { -- meta := store.Metadata{ -- Endpoints: map[string]interface{}{ -- KubernetesEndpoint: ep.EndpointMeta, -- }, -- Name: name, -- } -- if err := s.CreateOrUpdate(meta); err != nil { -- return err -- } -- return s.ResetEndpointTLSMaterial(name, KubernetesEndpoint, ep.TLSData.ToStoreTLSData()) --} -- --func TestSaveLoadGKEConfig(t *testing.T) { -- storeDir, err := ioutil.TempDir("", t.Name()) -- assert.NilError(t, err) -- defer os.RemoveAll(storeDir) -- store := store.New(storeDir, testStoreCfg) -- cfg, err := clientcmd.LoadFromFile("testdata/gke-kubeconfig") -- assert.NilError(t, err) -- clientCfg := clientcmd.NewDefaultClientConfig(*cfg, &clientcmd.ConfigOverrides{}) -- expectedCfg, err := clientCfg.ClientConfig() -- assert.NilError(t, err) -- ep, err := FromKubeConfig("testdata/gke-kubeconfig", "", "") -- assert.NilError(t, err) -- assert.NilError(t, save(store, ep, "gke-context")) -- persistedMetadata, err := store.GetMetadata("gke-context") -- assert.NilError(t, err) -- persistedEPMeta := EndpointFromContext(persistedMetadata) -- assert.Check(t, persistedEPMeta != nil) -- persistedEP, err := persistedEPMeta.WithTLSData(store, "gke-context") -- assert.NilError(t, err) -- persistedCfg := persistedEP.KubernetesConfig() -- actualCfg, err := persistedCfg.ClientConfig() -- assert.NilError(t, err) -- assert.DeepEqual(t, expectedCfg.AuthProvider, actualCfg.AuthProvider) --} -- --func TestSaveLoadEKSConfig(t *testing.T) { -- storeDir, err := ioutil.TempDir("", t.Name()) -- assert.NilError(t, err) -- defer os.RemoveAll(storeDir) -- store := store.New(storeDir, testStoreCfg) -- cfg, err := clientcmd.LoadFromFile("testdata/eks-kubeconfig") -- assert.NilError(t, err) -- clientCfg := clientcmd.NewDefaultClientConfig(*cfg, &clientcmd.ConfigOverrides{}) -- expectedCfg, err := clientCfg.ClientConfig() -- assert.NilError(t, err) -- ep, err := FromKubeConfig("testdata/eks-kubeconfig", "", "") -- assert.NilError(t, err) -- assert.NilError(t, save(store, ep, "eks-context")) -- persistedMetadata, err := store.GetMetadata("eks-context") -- assert.NilError(t, err) -- persistedEPMeta := EndpointFromContext(persistedMetadata) -- assert.Check(t, persistedEPMeta != nil) -- persistedEP, err := persistedEPMeta.WithTLSData(store, "eks-context") -- assert.NilError(t, err) -- persistedCfg := persistedEP.KubernetesConfig() -- actualCfg, err := persistedCfg.ClientConfig() -- assert.NilError(t, err) -- assert.DeepEqual(t, expectedCfg.ExecProvider, actualCfg.ExecProvider) --} -- --func TestSaveLoadK3SConfig(t *testing.T) { -- storeDir, err := ioutil.TempDir("", t.Name()) -- assert.NilError(t, err) -- defer os.RemoveAll(storeDir) -- store := store.New(storeDir, testStoreCfg) -- cfg, err := clientcmd.LoadFromFile("testdata/k3s-kubeconfig") -- assert.NilError(t, err) -- clientCfg := clientcmd.NewDefaultClientConfig(*cfg, &clientcmd.ConfigOverrides{}) -- expectedCfg, err := clientCfg.ClientConfig() -- assert.NilError(t, err) -- ep, err := FromKubeConfig("testdata/k3s-kubeconfig", "", "") -- assert.NilError(t, err) -- assert.NilError(t, save(store, ep, "k3s-context")) -- persistedMetadata, err := store.GetMetadata("k3s-context") -- assert.NilError(t, err) -- persistedEPMeta := EndpointFromContext(persistedMetadata) -- assert.Check(t, persistedEPMeta != nil) -- persistedEP, err := persistedEPMeta.WithTLSData(store, "k3s-context") -- assert.NilError(t, err) -- persistedCfg := persistedEP.KubernetesConfig() -- actualCfg, err := persistedCfg.ClientConfig() -- assert.NilError(t, err) -- assert.Check(t, len(actualCfg.Username) > 0) -- assert.Check(t, len(actualCfg.Password) > 0) -- assert.Equal(t, expectedCfg.Username, actualCfg.Username) -- assert.Equal(t, expectedCfg.Password, actualCfg.Password) --} -diff --git a/cli/context/kubernetes/load.go b/cli/context/kubernetes/load.go -deleted file mode 100644 -index 99f2a00ea0c..00000000000 ---- a/cli/context/kubernetes/load.go -+++ /dev/null -@@ -1,146 +0,0 @@ --package kubernetes -- --import ( -- "os" -- "path/filepath" -- -- "github.com/docker/cli/cli/command" -- "github.com/docker/cli/cli/context" -- "github.com/docker/cli/cli/context/store" -- api "github.com/docker/compose-on-kubernetes/api" -- "github.com/docker/docker/pkg/homedir" -- "github.com/pkg/errors" -- "k8s.io/client-go/tools/clientcmd" -- clientcmdapi "k8s.io/client-go/tools/clientcmd/api" --) -- --// EndpointMeta is a typed wrapper around a context-store generic endpoint describing --// a Kubernetes endpoint, without TLS data --type EndpointMeta struct { -- context.EndpointMetaBase -- DefaultNamespace string `json:",omitempty"` -- AuthProvider *clientcmdapi.AuthProviderConfig `json:",omitempty"` -- Exec *clientcmdapi.ExecConfig `json:",omitempty"` -- UsernamePassword *UsernamePassword `json:"usernamePassword,omitempty"` --} -- --// UsernamePassword contains username/password auth info --type UsernamePassword struct { -- Username string `json:"username,omitempty"` -- Password string `json:"password,omitempty"` --} -- --var _ command.EndpointDefaultResolver = &EndpointMeta{} -- --// Endpoint is a typed wrapper around a context-store generic endpoint describing --// a Kubernetes endpoint, with TLS data --type Endpoint struct { -- EndpointMeta -- TLSData *context.TLSData --} -- --func init() { -- command.RegisterDefaultStoreEndpoints( -- store.EndpointTypeGetter(KubernetesEndpoint, func() interface{} { return &EndpointMeta{} }), -- ) --} -- --// WithTLSData loads TLS materials for the endpoint --func (c *EndpointMeta) WithTLSData(s store.Reader, contextName string) (Endpoint, error) { -- tlsData, err := context.LoadTLSData(s, contextName, KubernetesEndpoint) -- if err != nil { -- return Endpoint{}, err -- } -- return Endpoint{ -- EndpointMeta: *c, -- TLSData: tlsData, -- }, nil --} -- --// KubernetesConfig creates the kubernetes client config from the endpoint --func (c *Endpoint) KubernetesConfig() clientcmd.ClientConfig { -- cfg := clientcmdapi.NewConfig() -- cluster := clientcmdapi.NewCluster() -- cluster.Server = c.Host -- cluster.InsecureSkipTLSVerify = c.SkipTLSVerify -- authInfo := clientcmdapi.NewAuthInfo() -- if c.TLSData != nil { -- cluster.CertificateAuthorityData = c.TLSData.CA -- authInfo.ClientCertificateData = c.TLSData.Cert -- authInfo.ClientKeyData = c.TLSData.Key -- } -- if c.UsernamePassword != nil { -- authInfo.Username = c.UsernamePassword.Username -- authInfo.Password = c.UsernamePassword.Password -- } -- authInfo.AuthProvider = c.AuthProvider -- authInfo.Exec = c.Exec -- cfg.Clusters["cluster"] = cluster -- cfg.AuthInfos["authInfo"] = authInfo -- ctx := clientcmdapi.NewContext() -- ctx.AuthInfo = "authInfo" -- ctx.Cluster = "cluster" -- ctx.Namespace = c.DefaultNamespace -- cfg.Contexts["context"] = ctx -- cfg.CurrentContext = "context" -- return clientcmd.NewDefaultClientConfig(*cfg, &clientcmd.ConfigOverrides{}) --} -- --// ResolveDefault returns endpoint metadata for the default Kubernetes --// endpoint, which is derived from the env-based kubeconfig. --func (c *EndpointMeta) ResolveDefault(stackOrchestrator command.Orchestrator) (interface{}, *store.EndpointTLSData, error) { -- kubeconfig := os.Getenv("KUBECONFIG") -- if kubeconfig == "" { -- kubeconfig = filepath.Join(homedir.Get(), ".kube/config") -- } -- kubeEP, err := FromKubeConfig(kubeconfig, "", "") -- if err != nil { -- if stackOrchestrator == command.OrchestratorKubernetes || stackOrchestrator == command.OrchestratorAll { -- return nil, nil, errors.Wrapf(err, "default orchestrator is %s but unable to resolve kubernetes endpoint", stackOrchestrator) -- } -- -- // We deliberately quash the error here, returning nil -- // for the first argument is sufficient to indicate we weren't able to -- // provide a default -- return nil, nil, nil -- } -- -- var tls *store.EndpointTLSData -- if kubeEP.TLSData != nil { -- tls = kubeEP.TLSData.ToStoreTLSData() -- } -- return kubeEP.EndpointMeta, tls, nil --} -- --// EndpointFromContext extracts kubernetes endpoint info from current context --func EndpointFromContext(metadata store.Metadata) *EndpointMeta { -- ep, ok := metadata.Endpoints[KubernetesEndpoint] -- if !ok { -- return nil -- } -- typed, ok := ep.(EndpointMeta) -- if !ok { -- return nil -- } -- return &typed --} -- --// ConfigFromContext resolves a kubernetes client config for the specified context. --// If kubeconfigOverride is specified, use this config file instead of the context defaults.ConfigFromContext --// if command.ContextDockerHost is specified as the context name, fallsback to the default user's kubeconfig file --func ConfigFromContext(name string, s store.Reader) (clientcmd.ClientConfig, error) { -- ctxMeta, err := s.GetMetadata(name) -- if err != nil { -- return nil, err -- } -- epMeta := EndpointFromContext(ctxMeta) -- if epMeta != nil { -- ep, err := epMeta.WithTLSData(s, name) -- if err != nil { -- return nil, err -- } -- return ep.KubernetesConfig(), nil -- } -- // context has no kubernetes endpoint -- return api.NewKubernetesConfig(""), nil --} -diff --git a/cli/context/kubernetes/load_test.go b/cli/context/kubernetes/load_test.go -deleted file mode 100644 -index a35276fcc4c..00000000000 ---- a/cli/context/kubernetes/load_test.go -+++ /dev/null -@@ -1,25 +0,0 @@ --package kubernetes -- --import ( -- "testing" -- -- "github.com/docker/cli/cli/command" -- "github.com/docker/cli/cli/config/configfile" -- cliflags "github.com/docker/cli/cli/flags" -- "gotest.tools/v3/assert" -- "gotest.tools/v3/env" --) -- --func TestDefaultContextInitializer(t *testing.T) { -- cli, err := command.NewDockerCli() -- assert.NilError(t, err) -- defer env.Patch(t, "KUBECONFIG", "./testdata/test-kubeconfig")() -- configFile := &configfile.ConfigFile{ -- StackOrchestrator: "all", -- } -- ctx, err := command.ResolveDefaultContext(&cliflags.CommonOptions{}, configFile, command.DefaultContextStoreConfig(), cli.Err()) -- assert.NilError(t, err) -- assert.Equal(t, "default", ctx.Meta.Name) -- assert.Equal(t, command.OrchestratorAll, ctx.Meta.Metadata.(command.DockerContext).StackOrchestrator) -- assert.DeepEqual(t, "zoinx", ctx.Meta.Endpoints[KubernetesEndpoint].(EndpointMeta).DefaultNamespace) --} -diff --git a/cli/context/kubernetes/save.go b/cli/context/kubernetes/save.go -deleted file mode 100644 -index 032a01d46ab..00000000000 ---- a/cli/context/kubernetes/save.go -+++ /dev/null -@@ -1,69 +0,0 @@ --package kubernetes -- --import ( -- "io/ioutil" -- -- "github.com/docker/cli/cli/context" -- "k8s.io/client-go/tools/clientcmd" -- clientcmdapi "k8s.io/client-go/tools/clientcmd/api" --) -- --// FromKubeConfig creates a Kubernetes endpoint from a Kubeconfig file --func FromKubeConfig(kubeconfig, kubeContext, namespaceOverride string) (Endpoint, error) { -- cfg := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( -- &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig}, -- &clientcmd.ConfigOverrides{CurrentContext: kubeContext, Context: clientcmdapi.Context{Namespace: namespaceOverride}}) -- ns, _, err := cfg.Namespace() -- if err != nil { -- return Endpoint{}, err -- } -- clientcfg, err := cfg.ClientConfig() -- if err != nil { -- return Endpoint{}, err -- } -- var ca, key, cert []byte -- if ca, err = readFileOrDefault(clientcfg.CAFile, clientcfg.CAData); err != nil { -- return Endpoint{}, err -- } -- if key, err = readFileOrDefault(clientcfg.KeyFile, clientcfg.KeyData); err != nil { -- return Endpoint{}, err -- } -- if cert, err = readFileOrDefault(clientcfg.CertFile, clientcfg.CertData); err != nil { -- return Endpoint{}, err -- } -- var tlsData *context.TLSData -- if ca != nil || cert != nil || key != nil { -- tlsData = &context.TLSData{ -- CA: ca, -- Cert: cert, -- Key: key, -- } -- } -- var usernamePassword *UsernamePassword -- if clientcfg.Username != "" || clientcfg.Password != "" { -- usernamePassword = &UsernamePassword{ -- Username: clientcfg.Username, -- Password: clientcfg.Password, -- } -- } -- return Endpoint{ -- EndpointMeta: EndpointMeta{ -- EndpointMetaBase: context.EndpointMetaBase{ -- Host: clientcfg.Host, -- SkipTLSVerify: clientcfg.Insecure, -- }, -- DefaultNamespace: ns, -- AuthProvider: clientcfg.AuthProvider, -- Exec: clientcfg.ExecProvider, -- UsernamePassword: usernamePassword, -- }, -- TLSData: tlsData, -- }, nil --} -- --func readFileOrDefault(path string, defaultValue []byte) ([]byte, error) { -- if path != "" { -- return ioutil.ReadFile(path) -- } -- return defaultValue, nil --} -diff --git a/cli/context/kubernetes/testdata/gke-kubeconfig b/cli/context/kubernetes/testdata/gke-kubeconfig -deleted file mode 100644 -index 5a6384cbae9..00000000000 ---- a/cli/context/kubernetes/testdata/gke-kubeconfig -+++ /dev/null -@@ -1,23 +0,0 @@ --apiVersion: v1 --clusters: --- cluster: -- server: https://some-server -- name: gke_sample --contexts: --- context: -- cluster: gke_sample -- user: gke_sample -- name: gke_sample --current-context: gke_sample --kind: Config --preferences: {} --users: --- name: gke_sample -- user: -- auth-provider: -- config: -- cmd-args: config config-helper --format=json -- cmd-path: /google/google-cloud-sdk/bin/gcloud -- expiry-key: '{.credential.token_expiry}' -- token-key: '{.credential.access_token}' -- name: gcp -diff --git a/cli/context/kubernetes/testdata/k3s-kubeconfig b/cli/context/kubernetes/testdata/k3s-kubeconfig -deleted file mode 100644 -index 7bb48863f0c..00000000000 ---- a/cli/context/kubernetes/testdata/k3s-kubeconfig -+++ /dev/null -@@ -1,20 +0,0 @@ --apiVersion: v1 --clusters: --- cluster: -- certificate-authority-data: dGhlLWNh -- server: https://someserver -- name: test-cluster --contexts: --- context: -- cluster: test-cluster -- user: test-user -- namespace: zoinx -- name: test --current-context: test --kind: Config --preferences: {} --users: --- name: test-user -- user: -- username: admin -- password: testpwd -diff --git a/cli/context/kubernetes/testdata/test-kubeconfig b/cli/context/kubernetes/testdata/test-kubeconfig -deleted file mode 100644 -index e96df74a508..00000000000 ---- a/cli/context/kubernetes/testdata/test-kubeconfig -+++ /dev/null -@@ -1,20 +0,0 @@ --apiVersion: v1 --clusters: --- cluster: -- certificate-authority-data: dGhlLWNh -- server: https://someserver -- name: test-cluster --contexts: --- context: -- cluster: test-cluster -- user: test-user -- namespace: zoinx -- name: test --current-context: test --kind: Config --preferences: {} --users: --- name: test-user -- user: -- client-certificate-data: dGhlLWNlcnQ= -- client-key-data: dGhlLWtleQ== -diff --git a/cli/context/store/doc.go b/cli/context/store/doc.go -index 5626a64d912..d5304b1d98d 100644 ---- a/cli/context/store/doc.go -+++ b/cli/context/store/doc.go -@@ -12,7 +12,7 @@ - // - tls/ - // - <context id>/endpoint1/: directory containing TLS data for the endpoint1 in the corresponding context - // --// The context store itself has absolutely no knowledge about what a docker or a kubernetes endpoint should contain in term of metadata or TLS config. -+// The context store itself has absolutely no knowledge about what a docker endpoint should contain in term of metadata or TLS config. - // Client code is responsible for generating and parsing endpoint metadata and TLS files. - // The multi-endpoints approach of this package allows to combine many different endpoints in the same "context" (e.g., the Docker CLI - // is able for a single context to define both a docker endpoint and a Kubernetes endpoint for the same cluster, and also specify which -diff --git a/e2e/stack/deploy_test.go b/e2e/stack/deploy_test.go -index 2b797aeab59..94275c57e7c 100644 ---- a/e2e/stack/deploy_test.go -+++ b/e2e/stack/deploy_test.go -@@ -6,24 +6,15 @@ import ( - "strings" - "testing" - -- "github.com/docker/cli/internal/test/environment" - "gotest.tools/v3/assert" - "gotest.tools/v3/golden" - "gotest.tools/v3/icmd" -- "gotest.tools/v3/skip" - ) - - func TestDeployWithNamedResources(t *testing.T) { - t.Run("Swarm", func(t *testing.T) { - testDeployWithNamedResources(t, "swarm") - }) -- t.Run("Kubernetes", func(t *testing.T) { -- // FIXME(chris-crone): currently does not work with compose for kubernetes. -- t.Skip("FIXME(chris-crone): currently does not work with compose for kubernetes.") -- skip.If(t, !environment.KubernetesEnabled()) -- -- testDeployWithNamedResources(t, "kubernetes") -- }) - } - - func testDeployWithNamedResources(t *testing.T, orchestrator string) { -diff --git a/e2e/stack/help_test.go b/e2e/stack/help_test.go -index e7f55c0cc0c..1235eef8d1e 100644 ---- a/e2e/stack/help_test.go -+++ b/e2e/stack/help_test.go -@@ -12,9 +12,6 @@ func TestStackDeployHelp(t *testing.T) { - t.Run("Swarm", func(t *testing.T) { - testStackDeployHelp(t, "swarm") - }) -- t.Run("Kubernetes", func(t *testing.T) { -- testStackDeployHelp(t, "kubernetes") -- }) - } - - func testStackDeployHelp(t *testing.T, orchestrator string) { -diff --git a/e2e/stack/remove_test.go b/e2e/stack/remove_test.go -index f6222000d62..8469786acb5 100644 ---- a/e2e/stack/remove_test.go -+++ b/e2e/stack/remove_test.go -@@ -9,7 +9,6 @@ import ( - "gotest.tools/v3/golden" - "gotest.tools/v3/icmd" - "gotest.tools/v3/poll" -- "gotest.tools/v3/skip" - ) - - var pollSettings = environment.DefaultPollSettings -@@ -18,11 +17,6 @@ func TestRemove(t *testing.T) { - t.Run("Swarm", func(t *testing.T) { - testRemove(t, "swarm") - }) -- t.Run("Kubernetes", func(t *testing.T) { -- skip.If(t, !environment.KubernetesEnabled()) -- -- testRemove(t, "kubernetes") -- }) - } - - func testRemove(t *testing.T, orchestrator string) { -diff --git a/e2e/stack/testdata/stack-deploy-help-kubernetes.golden b/e2e/stack/testdata/stack-deploy-help-kubernetes.golden -deleted file mode 100644 -index e3f8e30f561..00000000000 ---- a/e2e/stack/testdata/stack-deploy-help-kubernetes.golden -+++ /dev/null -@@ -1,14 +0,0 @@ -- --Usage: docker stack deploy [OPTIONS] STACK -- --Deploy a new stack or update an existing stack -- --Aliases: -- deploy, up -- --Options: -- -c, --compose-file strings Path to a Compose file, or "-" to read -- from stdin -- --kubeconfig string Kubernetes config file -- --namespace string Kubernetes namespace to use -- --orchestrator string Orchestrator to use (swarm|kubernetes|all) -diff --git a/e2e/stack/testdata/stack-deploy-help-swarm.golden b/e2e/stack/testdata/stack-deploy-help-swarm.golden -index f21c5d316e4..5f8ed3de7b2 100644 ---- a/e2e/stack/testdata/stack-deploy-help-swarm.golden -+++ b/e2e/stack/testdata/stack-deploy-help-swarm.golden -@@ -9,7 +9,7 @@ Aliases: - Options: - -c, --compose-file strings Path to a Compose file, or "-" to read - from stdin -- --orchestrator string Orchestrator to use (swarm|kubernetes|all) -+ --orchestrator string Orchestrator to use (swarm|all) - --prune Prune services that are no longer referenced - --resolve-image string Query the registry to resolve image digest - and supported platforms -diff --git a/e2e/stack/testdata/stack-deploy-with-names-kubernetes.golden b/e2e/stack/testdata/stack-deploy-with-names-kubernetes.golden -deleted file mode 100644 -index f97dd682cf6..00000000000 ---- a/e2e/stack/testdata/stack-deploy-with-names-kubernetes.golden -+++ /dev/null -@@ -1,7 +0,0 @@ --Creating network test-stack-deploy-with-names_network2 --Creating network named-network --Creating secret named-secret --Creating secret test-stack-deploy-with-names_secret2 --Creating config test-stack-deploy-with-names_config2 --Creating config named-config --Creating service test-stack-deploy-with-names_web -diff --git a/e2e/stack/testdata/stack-remove-kubernetes-success.golden b/e2e/stack/testdata/stack-remove-kubernetes-success.golden -deleted file mode 100644 -index 3c136713983..00000000000 ---- a/e2e/stack/testdata/stack-remove-kubernetes-success.golden -+++ /dev/null -@@ -1 +0,0 @@ --Removing stack: test-stack-remove-kubernetes diff --git a/0001-Revert-vendor-github.com-docker-docker.patch b/0001-Revert-vendor-github.com-docker-docker.patch new file mode 100644 index 0000000..61a8443 --- /dev/null +++ b/0001-Revert-vendor-github.com-docker-docker.patch @@ -0,0 +1,195 @@ +From 14a11f4bbc396dfa99e5755d24d3854eb6070cda Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Robert-Andr=C3=A9=20Mauchin?= zebob.m@gmail.com +Date: Sun, 9 Oct 2022 18:31:59 +0200 +Subject: [PATCH] Revert "vendor: github.com/docker/docker + +This reverts commit 7aa0b273e545182abddacceb79ea7e6c7dcc03fb. +--- + cli/command/container/restart.go | 28 +++++++++++---------------- + cli/command/container/stop.go | 18 +++++++---------- + docs/reference/commandline/restart.md | 4 ++-- + docs/reference/commandline/stop.md | 4 ++-- + 4 files changed, 22 insertions(+), 32 deletions(-) + +diff --git a/cli/command/container/restart.go b/cli/command/container/restart.go +index b86b822e1c..e710daac8a 100644 +--- a/cli/command/container/restart.go ++++ b/cli/command/container/restart.go +@@ -4,19 +4,18 @@ import ( + "context" + "fmt" + "strings" ++ "time" + + "github.com/docker/cli/cli" + "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/command/completion" +- "github.com/docker/docker/api/types/container" + "github.com/pkg/errors" + "github.com/spf13/cobra" + ) + + type restartOptions struct { +- signal string +- timeout int +- timeoutChanged bool ++ nSeconds int ++ nSecondsChanged bool + + containers []string + } +@@ -31,35 +30,31 @@ func NewRestartCommand(dockerCli command + Args: cli.RequiresMinArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + opts.containers = args +- opts.timeoutChanged = cmd.Flags().Changed("time") ++ opts.nSecondsChanged = cmd.Flags().Changed("time") + return runRestart(dockerCli, &opts) + }, + ValidArgsFunction: completion.ContainerNames(dockerCli, true), + } + + flags := cmd.Flags() +- flags.StringVarP(&opts.signal, "signal", "s", "", "Signal to send to the container") +- flags.IntVarP(&opts.timeout, "time", "t", 0, "Seconds to wait before killing the container") ++ flags.IntVarP(&opts.nSeconds, "time", "t", 10, "Seconds to wait for stop before killing the container") + return cmd + } + + func runRestart(dockerCli command.Cli, opts *restartOptions) error { + ctx := context.Background() + var errs []string +- var timeout *int +- if opts.timeoutChanged { +- timeout = &opts.timeout ++ var timeout *time.Duration ++ if opts.nSecondsChanged { ++ timeoutValue := time.Duration(opts.nSeconds) * time.Second ++ timeout = &timeoutValue + } + for _, name := range opts.containers { +- err := dockerCli.Client().ContainerRestart(ctx, name, container.StopOptions{ +- Signal: opts.signal, +- Timeout: timeout, +- }) +- if err != nil { ++ if err := dockerCli.Client().ContainerRestart(ctx, name, timeout); err != nil { + errs = append(errs, err.Error()) + continue + } +- _, _ = fmt.Fprintln(dockerCli.Out(), name) ++ fmt.Fprintln(dockerCli.Out(), name) + } + if len(errs) > 0 { + return errors.New(strings.Join(errs, "\n")) +diff --git a/cli/command/container/stop.go b/cli/command/container/stop.go +index e5664617b1..aceb6e2a0d 100644 +--- a/cli/command/container/stop.go ++++ b/cli/command/container/stop.go +@@ -4,19 +4,18 @@ import ( + "context" + "fmt" + "strings" ++ "time" + + "github.com/docker/cli/cli" + "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/command/completion" +- "github.com/docker/docker/api/types/container" + "github.com/pkg/errors" + "github.com/spf13/cobra" + ) + + type stopOptions struct { +- signal string +- timeout int +- timeoutChanged bool ++ time int ++ timeChanged bool + + containers []string + } +@@ -31,37 +31,36 @@ func NewStopCommand(dockerCli command.Cl + Args: cli.RequiresMinArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + opts.containers = args +- opts.timeoutChanged = cmd.Flags().Changed("time") ++ opts.timeChanged = cmd.Flags().Changed("time") + return runStop(dockerCli, &opts) + }, + ValidArgsFunction: completion.ContainerNames(dockerCli, false), + } + + flags := cmd.Flags() +- flags.StringVarP(&opts.signal, "signal", "s", "", "Signal to send to the container") +- flags.IntVarP(&opts.timeout, "time", "t", 0, "Seconds to wait before killing the container") ++ flags.IntVarP(&opts.time, "time", "t", 10, "Seconds to wait for stop before killing it") + return cmd + } + + func runStop(dockerCli command.Cli, opts *stopOptions) error { +- var timeout *int +- if opts.timeoutChanged { +- timeout = &opts.timeout ++ ctx := context.Background() ++ ++ var timeout *time.Duration ++ if opts.timeChanged { ++ timeoutValue := time.Duration(opts.time) * time.Second ++ timeout = &timeoutValue + } + +- errChan := parallelOperation(context.Background(), opts.containers, func(ctx context.Context, id string) error { +- return dockerCli.Client().ContainerStop(ctx, id, container.StopOptions{ +- Signal: opts.signal, +- Timeout: timeout, +- }) +- }) + var errs []string +- for _, ctr := range opts.containers { ++ errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, id string) error { ++ return dockerCli.Client().ContainerStop(ctx, id, timeout) ++ }) ++ for _, container := range opts.containers { + if err := <-errChan; err != nil { + errs = append(errs, err.Error()) + continue + } +- _, _ = fmt.Fprintln(dockerCli.Out(), ctr) ++ fmt.Fprintln(dockerCli.Out(), container) + } + if len(errs) > 0 { + return errors.New(strings.Join(errs, "\n")) +diff --git a/docs/reference/commandline/restart.md b/docs/reference/commandline/restart.md +index 30cd15812b..aa64257d86 100644 +--- a/docs/reference/commandline/restart.md ++++ b/docs/reference/commandline/restart.md +@@ -12,8 +12,8 @@ Usage: docker restart [OPTIONS] CONTAINER [CONTAINER...] + Restart one or more containers + + Options: +- -s, --signal string Signal to send to the container +- -t, --time int Seconds to wait before killing the container ++ --help Print usage ++ -t, --time int Seconds to wait for stop before killing the container (default 10) + ``` + + ## Examples +diff --git a/docs/reference/commandline/stop.md b/docs/reference/commandline/stop.md +index 04df574a03..fdc7d0ae72 100644 +--- a/docs/reference/commandline/stop.md ++++ b/docs/reference/commandline/stop.md +@@ -12,8 +12,8 @@ Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...] + Stop one or more running containers + + Options: +- -s, --signal string Signal to send to the container +- -t, --time int Seconds to wait before killing the container ++ --help Print usage ++ -t, --time int Seconds to wait for stop before killing it (default 10) + ``` + + ## Description +-- +2.37.3 + diff --git a/0002-Remove-kubeapi-from-version.patch b/0002-Remove-kubeapi-from-version.patch deleted file mode 100644 index 81a42a0..0000000 --- a/0002-Remove-kubeapi-from-version.patch +++ /dev/null @@ -1,22 +0,0 @@ -diff -up cli-20.10.7/cli/command/system/version.go.orig cli-20.10.7/cli/command/system/version.go ---- cli-20.10.7/cli/command/system/version.go.orig 2021-05-31 11:40:07.000000000 +0200 -+++ cli-20.10.7/cli/command/system/version.go 2021-08-03 12:49:14.080765782 +0200 -@@ -15,7 +15,6 @@ import ( - "github.com/docker/cli/cli/version" - "github.com/docker/cli/kubernetes" - "github.com/docker/cli/templates" -- kubeapi "github.com/docker/compose-on-kubernetes/api" - "github.com/docker/docker/api/types" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -@@ -255,9 +254,7 @@ func getKubernetesVersion(dockerCli comm - clientConfig clientcmd.ClientConfig - err error - ) -- if dockerCli.CurrentContext() == "" { -- clientConfig = kubeapi.NewKubernetesConfig(kubeConfig) -- } else { -+ if dockerCli.CurrentContext() != "" { - clientConfig, err = kubecontext.ConfigFromContext(dockerCli.CurrentContext(), dockerCli.ContextStore()) - } - if err != nil { diff --git a/golang-github-docker-cli.spec b/golang-github-docker-cli.spec index 327243d..5cee025 100644 --- a/golang-github-docker-cli.spec +++ b/golang-github-docker-cli.spec @@ -1,23 +1,14 @@ -# Generated by go2rpm 1.5.0 +# Generated by go2rpm 1.8.2 %bcond_without check -%bcond_with bootstrap -%if %{with bootstrap} -%global __requires_exclude %{?__requires_exclude:%{__requires_exclude}|}^golang\(.*\)$ -%endif +%global debug_package %{nil}
# https://github.com/docker/cli %global goipath github.com/docker/cli -Version: 20.10.7 +Version: 22.06.0~beta.0 +%global tag v22.06.0-beta.0
%gometa
-%global goipaths0 github.com/docker/cli -%global goipathsex0 github.com/docker/cli/cli/command/image - -%if %{without bootstrap} -%global goipaths1 github.com/docker/cli/cli/command/image -%endif - %global common_description %{expand: The docker CLI.}
@@ -28,138 +19,24 @@ Name: %{goname} Release: %autorelease Summary: The docker CLI
-# Upstream license specification: Apache-2.0 -License: ASL 2.0 +License: Apache-2.0 URL: %{gourl} -Source0: %{gosource} -# https://github.com/docker/cli/issues/2967 -Patch0: 0001-Drop-obsolete-compose-on-kubernetes.patch -Patch1: 0002-Remove-kubeapi-from-version.patch - -%if %{without bootstrap} -BuildRequires: golang(github.com/containerd/console) -BuildRequires: golang(github.com/containerd/containerd/platforms) -BuildRequires: golang(github.com/docker/distribution) -BuildRequires: golang(github.com/docker/distribution/manifest/manifestlist) -BuildRequires: golang(github.com/docker/distribution/manifest/schema2) -BuildRequires: golang(github.com/docker/distribution/reference) -BuildRequires: golang(github.com/docker/distribution/registry/api/errcode) -BuildRequires: golang(github.com/docker/distribution/registry/api/v2) -BuildRequires: golang(github.com/docker/distribution/registry/client) -BuildRequires: golang(github.com/docker/distribution/registry/client/auth) -BuildRequires: golang(github.com/docker/distribution/registry/client/auth/challenge) -BuildRequires: golang(github.com/docker/distribution/registry/client/transport) -BuildRequires: golang(github.com/docker/docker-credential-helpers/client) -BuildRequires: golang(github.com/docker/docker-credential-helpers/credentials) -BuildRequires: golang(github.com/docker/docker/api) -BuildRequires: golang(github.com/docker/docker/api/types) -BuildRequires: golang(github.com/docker/docker/api/types/blkiodev) -BuildRequires: golang(github.com/docker/docker/api/types/container) -BuildRequires: golang(github.com/docker/docker/api/types/events) -BuildRequires: golang(github.com/docker/docker/api/types/filters) -BuildRequires: golang(github.com/docker/docker/api/types/image) -BuildRequires: golang(github.com/docker/docker/api/types/mount) -BuildRequires: golang(github.com/docker/docker/api/types/network) -BuildRequires: golang(github.com/docker/docker/api/types/registry) -BuildRequires: golang(github.com/docker/docker/api/types/strslice) -BuildRequires: golang(github.com/docker/docker/api/types/swarm) -BuildRequires: golang(github.com/docker/docker/api/types/versions) -BuildRequires: golang(github.com/docker/docker/api/types/volume) -BuildRequires: golang(github.com/docker/docker/builder/remotecontext/git) -BuildRequires: golang(github.com/docker/docker/client) -BuildRequires: golang(github.com/docker/docker/errdefs) -BuildRequires: golang(github.com/docker/docker/pkg/archive) -BuildRequires: golang(github.com/docker/docker/pkg/fileutils) -BuildRequires: golang(github.com/docker/docker/pkg/homedir) -BuildRequires: golang(github.com/docker/docker/pkg/idtools) -BuildRequires: golang(github.com/docker/docker/pkg/ioutils) -BuildRequires: golang(github.com/docker/docker/pkg/jsonmessage) -BuildRequires: golang(github.com/docker/docker/pkg/pools) -BuildRequires: golang(github.com/docker/docker/pkg/progress) -BuildRequires: golang(github.com/docker/docker/pkg/signal) -BuildRequires: golang(github.com/docker/docker/pkg/stdcopy) -BuildRequires: golang(github.com/docker/docker/pkg/streamformatter) -BuildRequires: golang(github.com/docker/docker/pkg/stringid) -BuildRequires: golang(github.com/docker/docker/pkg/system) -BuildRequires: golang(github.com/docker/docker/pkg/urlutil) -BuildRequires: golang(github.com/docker/docker/registry) -BuildRequires: golang(github.com/docker/go-connections/nat) -BuildRequires: golang(github.com/docker/go-connections/tlsconfig) -BuildRequires: golang(github.com/docker/go-units) -BuildRequires: golang(github.com/docker/swarmkit/api) -BuildRequires: golang(github.com/docker/swarmkit/api/defaults) -BuildRequires: golang(github.com/docker/swarmkit/api/genericresource) -BuildRequires: golang(github.com/fvbommel/sortorder) -BuildRequires: golang(github.com/gogo/protobuf/types) -BuildRequires: golang(github.com/google/shlex) -BuildRequires: golang(github.com/imdario/mergo) -BuildRequires: golang(github.com/mitchellh/mapstructure) -BuildRequires: golang(github.com/moby/buildkit/api/services/control) -BuildRequires: golang(github.com/moby/buildkit/client) -BuildRequires: golang(github.com/moby/buildkit/frontend/dockerfile/dockerignore) -BuildRequires: golang(github.com/moby/buildkit/session) -BuildRequires: golang(github.com/moby/buildkit/session/auth/authprovider) -BuildRequires: golang(github.com/moby/buildkit/session/filesync) -BuildRequires: golang(github.com/moby/buildkit/session/secrets/secretsprovider) -BuildRequires: golang(github.com/moby/buildkit/session/sshforward/sshprovider) -BuildRequires: golang(github.com/moby/buildkit/util/appcontext) -BuildRequires: golang(github.com/moby/buildkit/util/progress/progressui) -BuildRequires: golang(github.com/moby/buildkit/util/progress/progresswriter) -BuildRequires: golang(github.com/moby/term) -BuildRequires: golang(github.com/morikuni/aec) -BuildRequires: golang(github.com/opencontainers/go-digest) -BuildRequires: golang(github.com/opencontainers/image-spec/specs-go/v1) -BuildRequires: golang(github.com/pkg/errors) -BuildRequires: golang(github.com/sirupsen/logrus) -BuildRequires: golang(github.com/spf13/cobra) -BuildRequires: golang(github.com/spf13/cobra/doc) -BuildRequires: golang(github.com/spf13/pflag) -BuildRequires: golang(github.com/theupdateframework/notary) -BuildRequires: golang(github.com/theupdateframework/notary/client) -BuildRequires: golang(github.com/theupdateframework/notary/client/changelist) -BuildRequires: golang(github.com/theupdateframework/notary/cryptoservice) -BuildRequires: golang(github.com/theupdateframework/notary/passphrase) -BuildRequires: golang(github.com/theupdateframework/notary/storage) -BuildRequires: golang(github.com/theupdateframework/notary/trustmanager) -BuildRequires: golang(github.com/theupdateframework/notary/trustpinning) -BuildRequires: golang(github.com/theupdateframework/notary/tuf/data) -BuildRequires: golang(github.com/theupdateframework/notary/tuf/signed) -BuildRequires: golang(github.com/theupdateframework/notary/tuf/utils) -BuildRequires: golang(github.com/tonistiigi/fsutil/types) -BuildRequires: golang(github.com/tonistiigi/go-rosetta) -BuildRequires: golang(github.com/xeipuuv/gojsonschema) -BuildRequires: golang(golang.org/x/sync/errgroup) -BuildRequires: golang(golang.org/x/sys/execabs) -BuildRequires: golang(golang.org/x/sys/unix) -BuildRequires: golang(golang.org/x/term) -BuildRequires: golang(golang.org/x/text/width) -BuildRequires: golang(gopkg.in/yaml.v2) -BuildRequires: golang(gotest.tools/v3/assert) -BuildRequires: golang(gotest.tools/v3/assert/cmp) -BuildRequires: golang(gotest.tools/v3/fs) -BuildRequires: golang(gotest.tools/v3/icmd) -BuildRequires: golang(gotest.tools/v3/poll) -BuildRequires: golang(gotest.tools/v3/skip) - -%if %{with check} -# Tests -BuildRequires: golang(github.com/google/go-cmp/cmp) -BuildRequires: golang(github.com/google/go-cmp/cmp/cmpopts) -BuildRequires: golang(gotest.tools/v3/env) -BuildRequires: golang(gotest.tools/v3/golden) -%endif -%endif +Source: %{gosource} +# For compatibility with current Swarmkit +# https://github.com/moby/swarmkit/issues/3081 +Patch0: 0001-Revert-vendor-github.com-docker-docker.patch
-%description -%{common_description} +%description %{common_description}
%gopkg
%prep %goprep -%autopatch -p1 -# Dropping the rest of k8s -rm -rfv kubernetes +%patch0 -p1 +sed -i "s|github.com/docker/distribution|github.com/distribution/distribution/v3|" $(find . -iname "*.go" -type f) + +%generate_buildrequires +%go_generate_buildrequires
# %%build # for cmd in cmd/* ; do @@ -173,11 +50,7 @@ rm -rfv kubernetes
%%if %{with check} %check -%gocheck -t cmd \ - -t cli/command \ - -t cli/context \ - -d internal/containerizedengine \ - -t kubernetes +%gocheck -t cli %endif
# %%files diff --git a/sources b/sources index 8d233e5..7f896d4 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (cli-20.10.7.tar.gz) = 4523ae70cb27d848da119070171af2eb84e974ac39d70be4feee105e37c949487c7f72a9bc30c32ce71bffb0787e27b7b9194ce5a8aeae57bdfeb3f2d730010f +SHA512 (cli-22.06.0-beta.0.tar.gz) = 69e7b7d7df099344f1b7f4bb5f65258e2fdc1abdd9d7051d3e1dade478f0b38d66d2f3762adf45d8649168de0d25150e44182de15ad51858141a5267c650f652
https://src.fedoraproject.org/rpms/golang-github-docker-cli/c/e1e997eca7dabc...
scm-commits@lists.fedoraproject.org