Repository: docker/compose-cli
Pull request #1225 (2021-02-03 09:16:47)

Add kill command
**What I did**
Added `compose kill` command for parity with docker-compose

**(not mandatory) A picture of a cute animal, if possible in relation with what you did**
![image](https://user-images.githubusercontent.com/132757/106724764-e9032100-6608-11eb-86a9-4249943a0031.png)



Patch:
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index a80dc165..565cad2d 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -4,8 +4,7 @@
 <!-- If this is a bug fix, make sure your description includes "fixes #xxxx", or "closes #xxxx" -->
 
 <!-- optional tests
-You can add a / mention to run tests executed by default only on main branch :
-* `test-kube` to run Kube E2E tests
+You can add a / mention to run tests executed by default only on main branch : 
 * `test-aci` to run ACI E2E tests
 * `test-ecs` to run ECS E2E tests
 * `test-windows` to run tests & E2E tests on windows
diff --git a/Makefile b/Makefile
index 4c168a15..45486e63 100644
--- a/Makefile
+++ b/Makefile
@@ -49,9 +49,6 @@ e2e-local: ## Run End to end local tests. Set E2E_TEST=TestName to run a single
 e2e-win-ci: ## Run end to end local tests on Windows CI, no Docker for Linux containers available ATM. Set E2E_TEST=TestName to run a single test
 	go test -count=1 -v $(TEST_FLAGS) ./local/e2e/cli-only
 
-e2e-kube: ## Run End to end Kube tests. Set E2E_TEST=TestName to run a single test
-	go test -timeout 10m -count=1 -v $(TEST_FLAGS) ./kube/e2e
-
 e2e-aci: ## Run End to end ACI tests. Set E2E_TEST=TestName to run a single test
 	go test -timeout 15m -count=1 -v $(TEST_FLAGS) ./aci/e2e
 
diff --git a/aci/compose.go b/aci/compose.go
index 43c765cb..079acb98 100644
--- a/aci/compose.go
+++ b/aci/compose.go
@@ -203,6 +203,10 @@ func (cs *aciComposeService) Convert(ctx context.Context, project *types.Project
 	return nil, errdefs.ErrNotImplemented
 }
 
+func (cs *aciComposeService) Kill(ctx context.Context, project *types.Project, options compose.KillOptions) error {
+	return errdefs.ErrNotImplemented
+}
+
 func (cs *aciComposeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error {
 	return errdefs.ErrNotImplemented
 }
diff --git a/api/client/compose.go b/api/client/compose.go
index 16198c47..e201068f 100644
--- a/api/client/compose.go
+++ b/api/client/compose.go
@@ -76,6 +76,10 @@ func (c *composeService) Convert(context.Context, *types.Project, compose.Conver
 	return nil, errdefs.ErrNotImplemented
 }
 
+func (c *composeService) Kill(ctx context.Context, project *types.Project, options compose.KillOptions) error {
+	return errdefs.ErrNotImplemented
+}
+
 func (c *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error {
 	return errdefs.ErrNotImplemented
 }
diff --git a/api/compose/api.go b/api/compose/api.go
index 465b20b7..f73ead38 100644
--- a/api/compose/api.go
+++ b/api/compose/api.go
@@ -49,6 +49,8 @@ type Service interface {
 	List(ctx context.Context) ([]Stack, error)
 	// Convert translate compose model into backend's native format
 	Convert(ctx context.Context, project *types.Project, options ConvertOptions) ([]byte, error)
+	// Kill executes the equivalent to a `compose kill`
+	Kill(ctx context.Context, project *types.Project, options KillOptions) error
 	// RunOneOffContainer creates a service oneoff container and starts its dependencies
 	RunOneOffContainer(ctx context.Context, project *types.Project, opts RunOptions) error
 }
@@ -81,6 +83,12 @@ type ConvertOptions struct {
 	Format string
 }
 
+// KillOptions group options of the Kill API
+type KillOptions struct {
+	// Signal to send to containers
+	Signal string
+}
+
 // RunOptions options to execute compose run
 type RunOptions struct {
 	Service    string
diff --git a/api/progress/event.go b/api/progress/event.go
index c8473dec..2898a038 100644
--- a/api/progress/event.go
+++ b/api/progress/event.go
@@ -78,16 +78,26 @@ func CreatedEvent(ID string) Event {
 	return NewEvent(ID, Done, "Created")
 }
 
-// StoppingEvent stops a new Stopping in progress Event
+// StoppingEvent creates a new Stopping in progress Event
 func StoppingEvent(ID string) Event {
 	return NewEvent(ID, Working, "Stopping")
 }
 
-// StoppedEvent stops a new Stopping in progress Event
+// StoppedEvent creates a new Stopping in progress Event
 func StoppedEvent(ID string) Event {
 	return NewEvent(ID, Done, "Stopped")
 }
 
+// KillingEvent creates a new Killing in progress Event
+func KillingEvent(ID string) Event {
+	return NewEvent(ID, Working, "Killing")
+}
+
+// KilledEvent creates a new Killed in progress Event
+func KilledEvent(ID string) Event {
+	return NewEvent(ID, Done, "Killed")
+}
+
 // RemovingEvent creates a new Removing in progress Event
 func RemovingEvent(ID string) Event {
 	return NewEvent(ID, Working, "Removing")
diff --git a/cli/cmd/compose/compose.go b/cli/cmd/compose/compose.go
index c10d81ee..1ed1d85a 100644
--- a/cli/cmd/compose/compose.go
+++ b/cli/cmd/compose/compose.go
@@ -113,6 +113,7 @@ func Command(contextType string) *cobra.Command {
 		listCommand(),
 		logsCommand(&opts, contextType),
 		convertCommand(&opts),
+		killCommand(&opts),
 		runCommand(&opts),
 	)
 
diff --git a/cli/cmd/compose/kill.go b/cli/cmd/compose/kill.go
new file mode 100644
index 00000000..7dbf08ff
--- /dev/null
+++ b/cli/cmd/compose/kill.go
@@ -0,0 +1,63 @@
+/*
+   Copyright 2020 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compose
+
+import (
+	"context"
+
+	"github.com/spf13/cobra"
+
+	"github.com/docker/compose-cli/api/client"
+	"github.com/docker/compose-cli/api/compose"
+)
+
+type killOptions struct {
+	*projectOptions
+	Signal string
+}
+
+func killCommand(p *projectOptions) *cobra.Command {
+	opts := killOptions{
+		projectOptions: p,
+	}
+	cmd := &cobra.Command{
+		Use:   "kill [options] [SERVICE...]",
+		Short: "Force stop service containers.",
+		RunE: func(cmd *cobra.Command, args []string) error {
+			return runKill(cmd.Context(), opts, args)
+		},
+	}
+
+	flags := cmd.Flags()
+	flags.StringVarP(&opts.Signal, "signal", "s", "SIGKILL", "SIGNAL to send to the container.")
+
+	return cmd
+}
+
+func runKill(ctx context.Context, opts killOptions, services []string) error {
+	c, err := client.NewWithDefaultLocalBackend(ctx)
+	if err != nil {
+		return err
+	}
+	project, err := opts.toProject(services)
+	if err != nil {
+		return err
+	}
+	return c.ComposeService().Kill(ctx, project, compose.KillOptions{
+		Signal: opts.Signal,
+	})
+}
diff --git a/cli/cmd/context/create_kube.go b/cli/cmd/context/create_kube.go
index 609716e3..b42370e9 100644
--- a/cli/cmd/context/create_kube.go
+++ b/cli/cmd/context/create_kube.go
@@ -50,8 +50,7 @@ func createKubeCommand() *cobra.Command {
 	}
 
 	addDescriptionFlag(cmd, &opts.Description)
-	cmd.Flags().StringVar(&opts.KubeConfigPath, "kubeconfig", "", "The endpoint of the Kubernetes manager")
-	cmd.Flags().StringVar(&opts.KubeContextName, "kubecontext", "", "The name of the context to use in kubeconfig")
+	cmd.Flags().StringVar(&opts.KubeconfigPath, "kubeconfig", "", "The endpoint of the Kubernetes manager")
 	cmd.Flags().BoolVar(&opts.FromEnvironment, "from-env", false, "Get endpoint and creds from env vars")
 	return cmd
 }
diff --git a/ecs/cloudformation.go b/ecs/cloudformation.go
index afb50358..d162db0f 100644
--- a/ecs/cloudformation.go
+++ b/ecs/cloudformation.go
@@ -25,6 +25,7 @@ import (
 
 	"github.com/docker/compose-cli/api/compose"
 	"github.com/docker/compose-cli/api/config"
+	"github.com/docker/compose-cli/api/errdefs"
 
 	ecsapi "github.com/aws/aws-sdk-go/service/ecs"
 	"github.com/aws/aws-sdk-go/service/elbv2"
@@ -46,6 +47,10 @@ import (
 	"sigs.k8s.io/kustomize/kyaml/yaml/merge2"
 )
 
+func (b *ecsAPIService) Kill(ctx context.Context, project *types.Project, options compose.KillOptions) error {
+	return errdefs.ErrNotImplemented
+}
+
 func (b *ecsAPIService) Convert(ctx context.Context, project *types.Project, options compose.ConvertOptions) ([]byte, error) {
 	err := b.resolveServiceImagesDigests(ctx, project)
 	if err != nil {
diff --git a/ecs/local/compose.go b/ecs/local/compose.go
index a7bfd747..f110960c 100644
--- a/ecs/local/compose.go
+++ b/ecs/local/compose.go
@@ -65,6 +65,10 @@ func (e ecsLocalSimulation) Up(ctx context.Context, project *types.Project, opti
 	return errdefs.ErrNotImplemented
 }
 
+func (e ecsLocalSimulation) Kill(ctx context.Context, project *types.Project, options compose.KillOptions) error {
+	return e.compose.Kill(ctx, project, options)
+}
+
 func (e ecsLocalSimulation) Convert(ctx context.Context, project *types.Project, options compose.ConvertOptions) ([]byte, error) {
 	enhanced, err := e.enhanceForLocalSimulation(project)
 	if err != nil {
diff --git a/ecs/testdata/context-by-keys-config.golden b/ecs/testdata/context/by-keys/config.golden
similarity index 100%
rename from ecs/testdata/context-by-keys-config.golden
rename to ecs/testdata/context/by-keys/config.golden
diff --git a/ecs/testdata/context-by-keys-credentials.golden b/ecs/testdata/context/by-keys/credentials.golden
similarity index 100%
rename from ecs/testdata/context-by-keys-credentials.golden
rename to ecs/testdata/context/by-keys/credentials.golden
diff --git a/ecs/testdata/context-by-profile-config.golden b/ecs/testdata/context/by-profile/config.golden
similarity index 100%
rename from ecs/testdata/context-by-profile-config.golden
rename to ecs/testdata/context/by-profile/config.golden
diff --git a/ecs/testdata/context-by-profile-credentials.golden b/ecs/testdata/context/by-profile/credentials.golden
similarity index 100%
rename from ecs/testdata/context-by-profile-credentials.golden
rename to ecs/testdata/context/by-profile/credentials.golden
diff --git a/kube/compose.go b/kube/compose.go
index 22b80ada..c8611f35 100644
--- a/kube/compose.go
+++ b/kube/compose.go
@@ -103,6 +103,10 @@ func (s *composeService) Convert(ctx context.Context, project *types.Project, op
 	return nil, errdefs.ErrNotImplemented
 }
 
+func (s *composeService) Kill(ctx context.Context, project *types.Project, options compose.KillOptions) error {
+	return errdefs.ErrNotImplemented
+}
+
 // RunOneOffContainer creates a service oneoff container and starts its dependencies
 func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error {
 	return errdefs.ErrNotImplemented
diff --git a/kube/context.go b/kube/context.go
index 6749441c..0b9201b0 100644
--- a/kube/context.go
+++ b/kube/context.go
@@ -19,8 +19,6 @@
 package kube
 
 import (
-	"fmt"
-
 	"github.com/AlecAivazis/survey/v2/terminal"
 	"github.com/docker/compose-cli/api/context/store"
 	"github.com/docker/compose-cli/api/errdefs"
@@ -31,9 +29,9 @@ import (
 
 // ContextParams options for creating a Kubernetes context
 type ContextParams struct {
-	KubeContextName string
+	ContextName     string
 	Description     string
-	KubeConfigPath  string
+	KubeconfigPath  string
 	FromEnvironment bool
 }
 
@@ -43,11 +41,11 @@ func (cp ContextParams) CreateContextData() (interface{}, string, error) {
 		// we use the current kubectl context from a $KUBECONFIG path
 		return store.KubeContext{
 			FromEnvironment: cp.FromEnvironment,
-		}, cp.getDescription(), nil
+		}, cp.Description, nil
 	}
 	user := prompt.User{}
 	selectContext := func() error {
-		contexts, err := kubernetes.ListAvailableKubeConfigContexts(cp.KubeConfigPath)
+		contexts, err := kubernetes.ListAvailableKubeConfigContexts(cp.KubeconfigPath)
 		if err != nil {
 			return err
 		}
@@ -59,18 +57,11 @@ func (cp ContextParams) CreateContextData() (interface{}, string, error) {
 			}
 			return err
 		}
-		cp.KubeContextName = contexts[selected]
+		cp.ContextName = contexts[selected]
 		return nil
 	}
 
-	if cp.KubeConfigPath != "" {
-		if cp.KubeContextName != "" {
-			return store.KubeContext{
-				ContextName:     cp.KubeContextName,
-				KubeconfigPath:  cp.KubeConfigPath,
-				FromEnvironment: cp.FromEnvironment,
-			}, cp.getDescription(), nil
-		}
+	if cp.KubeconfigPath != "" {
 		err := selectContext()
 		if err != nil {
 			return nil, "", err
@@ -104,22 +95,8 @@ func (cp ContextParams) CreateContextData() (interface{}, string, error) {
 		}
 	}
 	return store.KubeContext{
-		ContextName:     cp.KubeContextName,
-		KubeconfigPath:  cp.KubeConfigPath,
+		ContextName:     cp.ContextName,
+		KubeconfigPath:  cp.KubeconfigPath,
 		FromEnvironment: cp.FromEnvironment,
-	}, cp.getDescription(), nil
-}
-
-func (cp ContextParams) getDescription() string {
-	if cp.Description != "" {
-		return cp.Description
-	}
-	if cp.FromEnvironment {
-		return "From environment variables"
-	}
-	configFile := "default kube config"
-	if cp.KubeConfigPath != "" {
-		configFile = cp.KubeConfigPath
-	}
-	return fmt.Sprintf("%s (in %s)", cp.KubeContextName, configFile)
+	}, cp.Description, nil
 }
diff --git a/local/compose/containers.go b/local/compose/containers.go
index 300700e2..ee13f9d2 100644
--- a/local/compose/containers.go
+++ b/local/compose/containers.go
@@ -70,3 +70,9 @@ func (containers Containers) names() []string {
 	}
 	return names
 }
+
+func (containers Containers) forEach(fn func(moby.Container)) {
+	for _, c := range containers {
+		fn(c)
+	}
+}
diff --git a/local/compose/kill.go b/local/compose/kill.go
new file mode 100644
index 00000000..1745a121
--- /dev/null
+++ b/local/compose/kill.go
@@ -0,0 +1,60 @@
+/*
+   Copyright 2020 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compose
+
+import (
+	"context"
+
+	"github.com/compose-spec/compose-go/types"
+	moby "github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/filters"
+	"golang.org/x/sync/errgroup"
+
+	"github.com/docker/compose-cli/api/compose"
+	"github.com/docker/compose-cli/api/progress"
+)
+
+func (s *composeService) Kill(ctx context.Context, project *types.Project, options compose.KillOptions) error {
+	w := progress.ContextWriter(ctx)
+
+	var containers Containers
+	containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{
+		Filters: filters.NewArgs(projectFilter(project.Name)),
+		All:     true,
+	})
+	if err != nil {
+		return err
+	}
+
+	eg, ctx := errgroup.WithContext(ctx)
+	containers.
+		filter(isService(project.ServiceNames()...)).
+		forEach(func(container moby.Container) {
+			eg.Go(func() error {
+				eventName := getContainerProgressName(container)
+				w.Event(progress.KillingEvent(eventName))
+				err := s.apiClient.ContainerKill(ctx, container.ID, options.Signal)
+				if err != nil {
+					w.Event(progress.ErrorMessageEvent(eventName, "Error while Killing"))
+					return err
+				}
+				w.Event(progress.KilledEvent(eventName))
+				return nil
+			})
+		})
+	return eg.Wait()
+}
diff --git a/local/compose/stop.go b/local/compose/stop.go
index 188aabf7..b1436f33 100644
--- a/local/compose/stop.go
+++ b/local/compose/stop.go
@@ -38,6 +38,8 @@ func (s *composeService) Stop(ctx context.Context, project *types.Project) error
 		return err
 	}
 
+	containers = containers.filter(isService(project.ServiceNames()...))
+
 	return InReverseDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error {
 		return s.stopContainers(ctx, w, containers.filter(isService(service.Name)))
 	})


Test patch:
diff --git a/.github/workflows/kube-tests.yml b/.github/workflows/kube-tests.yml
deleted file mode 100644
index a39d8c6b..00000000
--- a/.github/workflows/kube-tests.yml
+++ /dev/null
@@ -1,63 +0,0 @@
-name: Kube integration tests
-
-on:
-  push:
-    branches:
-      - main
-  pull_request:
-
-jobs:
-  check-optional-tests:
-    name: Check if needs to run Kube tests
-    runs-on: ubuntu-latest
-    outputs:
-      trigger-kube: ${{steps.runkubetest.outputs.triggered}}
-    steps:
-      - uses: khan/pull-request-comment-trigger@master
-        name: Check if test Kube
-        if: github.event_name == 'pull_request'
-        id: runkubetest
-        with:
-          trigger: '/test-kube'
-
-
-  kube-tests:
-    name: Kube e2e tests
-    runs-on: ubuntu-latest
-    env:
-      GO111MODULE: "on"
-    needs: check-optional-tests
-    if: github.ref == 'refs/heads/main' || needs.check-optional-tests.outputs.trigger-kube == 'true'
-    steps:
-      - name: Set up Go 1.15
-        uses: actions/setup-go@v1
-        with:
-          go-version: 1.15
-        id: go
-
-      - name: Setup docker CLI
-        run: |
-          curl https://download.docker.com/linux/static/stable/x86_64/docker-20.10.2.tgz | tar xz
-          sudo cp ./docker/docker /usr/bin/ && rm -rf docker && docker version
-
-      - name: Setup Kube tools
-        run: |
-          sudo apt-get install jq && jq --version
-          curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.10.0/kind-linux-amd64 && chmod +x ./kind && sudo mv ./kind /usr/bin/ && kind version
-          curl -LO "https://dl.k8s.io/release/v1.20.2/bin/linux/amd64/kubectl" && sudo mv kubectl /usr/bin/ && kubectl version --client
-
-      - name: Checkout code into the Go module directory
-        uses: actions/checkout@v2
-
-      - uses: actions/cache@v2
-        with:
-          path: ~/go/pkg/mod
-          key: go-${{ hashFiles('**/go.sum') }}
-
-      - name: Build for Kube e2e tests
-        env:
-          BUILD_TAGS: kube
-        run: make -f builder.Makefile cli
-
-      - name: Kube e2e Test
-        run: make e2e-kube
diff --git a/ecs/context_test.go b/ecs/context_test.go
index 9982a54f..f196edc4 100644
--- a/ecs/context_test.go
+++ b/ecs/context_test.go
@@ -65,15 +65,15 @@ func TestCreateContextDataByKeys(t *testing.T) {
 	assert.Equal(t, data.(store.EcsContext).Profile, "default")
 
 	s := golden.Get(t, dir.Join("config"))
-	golden.Assert(t, string(s), "context-by-keys-config.golden")
+	golden.Assert(t, string(s), "context/by-keys/config.golden")
 
 	s = golden.Get(t, dir.Join("credentials"))
-	golden.Assert(t, string(s), "context-by-keys-credentials.golden")
+	golden.Assert(t, string(s), "context/by-keys/credentials.golden")
 }
 
 func TestCreateContextDataFromProfile(t *testing.T) {
-	os.Setenv("AWS_CONFIG_FILE", "testdata/context-by-profile-config.golden")                  // nolint:errcheck
-	os.Setenv("AWS_SHARED_CREDENTIALS_FILE", "testdata/context-by-profile-credentials.golden") // nolint:errcheck
+	os.Setenv("AWS_CONFIG_FILE", "testdata/context/by-profile/config.golden")                  // nolint:errcheck
+	os.Setenv("AWS_SHARED_CREDENTIALS_FILE", "testdata/context/by-profile/credentials.golden") // nolint:errcheck
 
 	defer os.Unsetenv("AWS_CONFIG_FILE")             // nolint:errcheck
 	defer os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE") // nolint:errcheck
@@ -144,15 +144,15 @@ func TestCreateContextDataByKeysInteractive(t *testing.T) {
 	assert.Equal(t, data.(store.EcsContext).Profile, "default")
 
 	s := golden.Get(t, dir.Join("config"))
-	golden.Assert(t, string(s), "context-by-keys-config.golden")
+	golden.Assert(t, string(s), "context/by-keys/config.golden")
 
 	s = golden.Get(t, dir.Join("credentials"))
-	golden.Assert(t, string(s), "context-by-keys-credentials.golden")
+	golden.Assert(t, string(s), "context/by-keys/credentials.golden")
 }
 
 func TestCreateContextDataByProfileInteractive(t *testing.T) {
-	os.Setenv("AWS_CONFIG_FILE", "testdata/context-by-profile-config.golden")                  // nolint:errcheck
-	os.Setenv("AWS_SHARED_CREDENTIALS_FILE", "testdata/context-by-profile-credentials.golden") // nolint:errcheck
+	os.Setenv("AWS_CONFIG_FILE", "testdata/context/by-profile/config.golden")                  // nolint:errcheck
+	os.Setenv("AWS_SHARED_CREDENTIALS_FILE", "testdata/context/by-profile/credentials.golden") // nolint:errcheck
 
 	defer os.Unsetenv("AWS_CONFIG_FILE")             // nolint:errcheck
 	defer os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE") // nolint:errcheck
diff --git a/kube/context_test.go b/kube/context_test.go
deleted file mode 100644
index 12913ce5..00000000
--- a/kube/context_test.go
+++ /dev/null
@@ -1,58 +0,0 @@
-// +build kube
-
-/*
-   Copyright 2020 Docker Compose CLI authors
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-package kube
-
-import (
-	"testing"
-
-	"gotest.tools/v3/assert"
-)
-
-func TestContextDescriptionIfEnvVar(t *testing.T) {
-	cp := ContextParams{
-		FromEnvironment: true,
-	}
-	description := cp.getDescription()
-	assert.Equal(t, description, "From environment variables")
-}
-
-func TestContextDescriptionIfProvided(t *testing.T) {
-	cp := ContextParams{
-		Description:     "custom description",
-		FromEnvironment: true,
-	}
-	description := cp.getDescription()
-	assert.Equal(t, description, "custom description")
-}
-
-func TestContextDescriptionIfConfigFile(t *testing.T) {
-	cp := ContextParams{
-		KubeContextName: "my-context",
-		KubeConfigPath:  "~/.kube/config",
-	}
-	description := cp.getDescription()
-	assert.Equal(t, description, "my-context (in ~/.kube/config)")
-}
-func TestContextDescriptionIfDefaultConfigFile(t *testing.T) {
-	cp := ContextParams{
-		KubeContextName: "my-context",
-	}
-	description := cp.getDescription()
-	assert.Equal(t, description, "my-context (in default kube config)")
-}
diff --git a/kube/e2e/compose_test.go b/kube/e2e/compose_test.go
index 0d14c8f7..d435310c 100644
--- a/kube/e2e/compose_test.go
+++ b/kube/e2e/compose_test.go
@@ -18,8 +18,8 @@ package e2e
 
 import (
 	"fmt"
+	"net/http"
 	"os"
-	"path/filepath"
 	"strings"
 	"testing"
 	"time"
@@ -48,52 +48,31 @@ func TestComposeUp(t *testing.T) {
 	c := NewParallelE2eCLI(t, binDir)
 
 	const projectName = "compose-kube-demo"
-	kubeconfig := filepath.Join(c.ConfigDir, "kubeconfig")
-	kindClusterName := "e2e"
-	kubeContextName := "kind-" + kindClusterName
-	dockerContextName := "kube-e2e-ctx"
-
-	t.Run("create kube cluster", func(t *testing.T) {
-		c.RunCmd("kind", "create", "cluster", "--name", kindClusterName, "--kubeconfig", kubeconfig, "--wait", "180s")
-	})
-	defer func() {
-		c.RunDockerCmd("context", "use", "default")
-		c.RunCmd("kind", "delete", "cluster", "--name", kindClusterName, "--kubeconfig", kubeconfig)
-	}()
 
 	t.Run("create kube context", func(t *testing.T) {
-		res := c.RunDockerCmd("context", "create", "kubernetes", "--kubeconfig", kubeconfig, "--kubecontext", kubeContextName, dockerContextName)
-		res.Assert(t, icmd.Expected{Out: fmt.Sprintf("Successfully created kube context %q", dockerContextName)})
-		c.RunDockerCmd("context", "use", dockerContextName)
-		res = c.RunDockerCmd("context", "ls")
-		res.Assert(t, icmd.Expected{Out: fmt.Sprintf("%s *      kube                %s (in %s)", dockerContextName, kubeContextName, kubeconfig)})
+		res := c.RunDockerCmd("context", "create", "kubernetes", "--kubeconfig", "/Users/gtardif/.kube/config", "--kubecontext", "docker-desktop", "kube-e2e")
+		res.Assert(t, icmd.Expected{Out: `Successfully created kube context "kube-e2e"`})
+		c.RunDockerCmd("context", "use", "kube-e2e")
 	})
 
 	t.Run("up", func(t *testing.T) {
 		c.RunDockerCmd("compose", "-f", "./kube-simple-demo/demo_sentences.yaml", "--project-name", projectName, "up", "-d")
 	})
 
-	t.Run("compose ls", func(t *testing.T) {
-		res := c.RunDockerCmd("compose", "ls", "--format", "json")
-		res.Assert(t, icmd.Expected{Out: `[{"Name":"compose-kube-demo","Status":"deployed"}]`})
-	})
-
 	t.Run("check running project", func(t *testing.T) {
-		// Docker Desktop kube cluster automatically exposes ports on the host, this is not the case with kind on Desktop,
-		//we need to connect to the clusterIP, from the kind container
-		res := c.RunCmd("sh", "-c", "kubectl --kubeconfig "+kubeconfig+" get service/web -o json | jq -r '.spec.clusterIP'")
-		clusterIP := strings.ReplaceAll(strings.TrimSpace(res.Stdout()), `"`, "")
+		res := c.RunDockerCmd("compose", "-p", projectName, "ps")
+		res.Assert(t, icmd.Expected{Out: `web`})
 
-		endpoint := fmt.Sprintf("http://%s:80/words/noun", clusterIP)
-		c.WaitForCmdResult(icmd.Command("docker", "--context", "default", "exec", "e2e-control-plane", "curl", endpoint), StdoutContains(`"word":`), 3*time.Minute, 3*time.Second)
+		endpoint := "http://localhost:95"
+		output := HTTPGetWithRetry(t, endpoint+"/words/noun", http.StatusOK, 2*time.Second, 20*time.Second)
+		assert.Assert(t, strings.Contains(output, `"word":`))
 	})
-
 	t.Run("down", func(t *testing.T) {
 		_ = c.RunDockerCmd("compose", "--project-name", projectName, "down")
 	})
 
-	t.Run("check stack after down", func(t *testing.T) {
-		res := c.RunDockerCmd("compose", "ls")
+	t.Run("check containers after down", func(t *testing.T) {
+		res := c.RunDockerCmd("ps", "--all")
 		assert.Assert(t, !strings.Contains(res.Combined(), projectName), res.Combined())
 	})
 }
diff --git a/utils/e2e/framework.go b/utils/e2e/framework.go
index ce9360e4..a525bb9c 100644
--- a/utils/e2e/framework.go
+++ b/utils/e2e/framework.go
@@ -203,15 +203,6 @@ func (c *E2eCLI) RunDockerOrExitError(args ...string) *icmd.Result {
 	return icmd.RunCmd(c.NewDockerCmd(args...))
 }
 
-// RunCmd runs a command, expects no error and returns a result
-func (c *E2eCLI) RunCmd(args ...string) *icmd.Result {
-	fmt.Printf("	[%s] %s\n", c.test.Name(), strings.Join(args, " "))
-	assert.Assert(c.test, len(args) >= 1, "require at least one command in parameters")
-	res := icmd.RunCmd(c.NewCmd(args[0], args[1:]...))
-	res.Assert(c.test, icmd.Success)
-	return res
-}
-
 // RunDockerCmd runs a docker command, expects no error and returns a result
 func (c *E2eCLI) RunDockerCmd(args ...string) *icmd.Result {
 	res := c.RunDockerOrExitError(args...)
@@ -219,28 +210,6 @@ func (c *E2eCLI) RunDockerCmd(args ...string) *icmd.Result {
 	return res
 }
 
-// StdoutContains returns a predicate on command result expecting a string in stdout
-func StdoutContains(expected string) func(*icmd.Result) bool {
-	return func(res *icmd.Result) bool {
-		return strings.Contains(res.Stdout(), expected)
-	}
-}
-
-// WaitForCmdResult try to execute a cmd until resulting output matches given predicate
-func (c *E2eCLI) WaitForCmdResult(command icmd.Cmd, predicate func(*icmd.Result) bool, timeout time.Duration, delay time.Duration) {
-	assert.Assert(c.test, timeout.Nanoseconds() > delay.Nanoseconds(), "timeout must be greater than delay")
-	var res *icmd.Result
-	checkStopped := func(logt poll.LogT) poll.Result {
-		fmt.Printf("	[%s] %s\n", c.test.Name(), strings.Join(command.Command, " "))
-		res = icmd.RunCmd(command)
-		if !predicate(res) {
-			return poll.Continue("Cmd output did not match requirement: %q", res.Combined())
-		}
-		return poll.Success()
-	}
-	poll.WaitOn(c.test, checkStopped, poll.WithDelay(delay), poll.WithTimeout(timeout))
-}
-
 // PathEnvVar returns path (os sensitive) for running test
 func (c *E2eCLI) PathEnvVar() string {
 	path := c.BinDir + ":" + os.Getenv("PATH")