Skip to content
This repository was archived by the owner on Nov 27, 2023. It is now read-only.

Commit 2c22717

Browse files
authored
Merge pull request #1244 from gtardif/kube_compose_ps
Kube compose ps
2 parents de7ce70 + 677bfc9 commit 2c22717

File tree

4 files changed

+179
-3
lines changed

4 files changed

+179
-3
lines changed

kube/client/client.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// +build kube
2+
3+
/*
4+
Copyright 2020 Docker Compose CLI authors
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
*/
18+
19+
package client
20+
21+
import (
22+
"context"
23+
"fmt"
24+
25+
v1 "k8s.io/api/core/v1"
26+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+
"k8s.io/cli-runtime/pkg/genericclioptions"
28+
"k8s.io/client-go/kubernetes"
29+
30+
"github.com/docker/compose-cli/api/compose"
31+
)
32+
33+
// KubeClient API to access kube objects
34+
type KubeClient struct {
35+
client *kubernetes.Clientset
36+
namespace string
37+
}
38+
39+
// NewKubeClient new kubernetes client
40+
func NewKubeClient(config genericclioptions.RESTClientGetter) (*KubeClient, error) {
41+
restConfig, err := config.ToRESTConfig()
42+
if err != nil {
43+
return nil, err
44+
}
45+
46+
clientset, err := kubernetes.NewForConfig(restConfig)
47+
if err != nil {
48+
return nil, err
49+
}
50+
51+
namespace, _, err := config.ToRawKubeConfigLoader().Namespace()
52+
if err != nil {
53+
return nil, err
54+
}
55+
56+
return &KubeClient{
57+
client: clientset,
58+
namespace: namespace,
59+
}, nil
60+
}
61+
62+
// GetContainers get containers for a given compose project
63+
func (kc KubeClient) GetContainers(ctx context.Context, projectName string, all bool) ([]compose.ContainerSummary, error) {
64+
fieldSelector := ""
65+
if !all {
66+
fieldSelector = "status.phase=Running"
67+
}
68+
69+
pods, err := kc.client.CoreV1().Pods(kc.namespace).List(ctx, metav1.ListOptions{
70+
LabelSelector: fmt.Sprintf("%s=%s", compose.ProjectTag, projectName),
71+
FieldSelector: fieldSelector,
72+
})
73+
if err != nil {
74+
return nil, err
75+
}
76+
result := []compose.ContainerSummary{}
77+
for _, pod := range pods.Items {
78+
result = append(result, podToContainerSummary(pod))
79+
}
80+
81+
return result, nil
82+
}
83+
84+
func podToContainerSummary(pod v1.Pod) compose.ContainerSummary {
85+
return compose.ContainerSummary{
86+
ID: pod.GetObjectMeta().GetName(),
87+
Name: pod.GetObjectMeta().GetName(),
88+
Service: pod.GetObjectMeta().GetLabels()[compose.ServiceTag],
89+
State: string(pod.Status.Phase),
90+
Project: pod.GetObjectMeta().GetLabels()[compose.ProjectTag],
91+
}
92+
}

kube/client/client_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// +build kube
2+
3+
/*
4+
Copyright 2020 Docker Compose CLI authors
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
*/
18+
19+
package client
20+
21+
import (
22+
"testing"
23+
24+
v1 "k8s.io/api/core/v1"
25+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26+
27+
"gotest.tools/v3/assert"
28+
29+
"github.com/docker/compose-cli/api/compose"
30+
)
31+
32+
func TestPodToContainerSummary(t *testing.T) {
33+
pod := v1.Pod{
34+
ObjectMeta: metav1.ObjectMeta{
35+
Name: "c1-123",
36+
Labels: map[string]string{
37+
compose.ProjectTag: "myproject",
38+
compose.ServiceTag: "service1",
39+
},
40+
},
41+
Status: v1.PodStatus{
42+
Phase: "Running",
43+
},
44+
}
45+
46+
container := podToContainerSummary(pod)
47+
48+
expected := compose.ContainerSummary{
49+
ID: "c1-123",
50+
Name: "c1-123",
51+
Project: "myproject",
52+
Service: "service1",
53+
State: "Running",
54+
}
55+
assert.DeepEqual(t, container, expected)
56+
}

kube/compose.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,14 @@ import (
3030
"github.com/docker/compose-cli/api/context/store"
3131
"github.com/docker/compose-cli/api/errdefs"
3232
"github.com/docker/compose-cli/api/progress"
33+
"github.com/docker/compose-cli/kube/client"
3334
"github.com/docker/compose-cli/kube/helm"
3435
"github.com/docker/compose-cli/kube/resources"
3536
)
3637

3738
type composeService struct {
38-
sdk *helm.Actions
39+
sdk *helm.Actions
40+
client *client.KubeClient
3941
}
4042

4143
// NewComposeService create a kubernetes implementation of the compose.Service API
@@ -55,8 +57,14 @@ func NewComposeService(ctx context.Context) (compose.Service, error) {
5557
if err != nil {
5658
return nil, err
5759
}
60+
apiClient, err := client.NewKubeClient(config)
61+
if err != nil {
62+
return nil, err
63+
}
64+
5865
return &composeService{
59-
sdk: actions,
66+
sdk: actions,
67+
client: apiClient,
6068
}, nil
6169
}
6270

@@ -151,7 +159,7 @@ func (s *composeService) Logs(ctx context.Context, projectName string, consumer
151159

152160
// Ps executes the equivalent to a `compose ps`
153161
func (s *composeService) Ps(ctx context.Context, projectName string, options compose.PsOptions) ([]compose.ContainerSummary, error) {
154-
return nil, errdefs.ErrNotImplemented
162+
return s.client.GetContainers(ctx, projectName, options.All)
155163
}
156164

157165
// Convert translate compose model into backend's native format

kube/e2e/compose_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"testing"
2525
"time"
2626

27+
testify "github.com/stretchr/testify/assert"
2728
"gotest.tools/v3/assert"
2829
"gotest.tools/v3/icmd"
2930

@@ -78,6 +79,25 @@ func TestComposeUp(t *testing.T) {
7879
res.Assert(t, icmd.Expected{Out: `[{"Name":"compose-kube-demo","Status":"deployed"}]`})
7980
})
8081

82+
t.Run("compose ps --all", func(t *testing.T) {
83+
getServiceRegx := func(service string) string {
84+
// match output with random hash / spaces like:
85+
// db-698f4dd798-jd9gw db Running
86+
return fmt.Sprintf("%s-.*\\s+%s\\s+Pending\\s+", service, service)
87+
}
88+
res := c.RunDockerCmd("compose", "ps", "-p", projectName, "--all")
89+
testify.Regexp(t, getServiceRegx("db"), res.Stdout())
90+
testify.Regexp(t, getServiceRegx("words"), res.Stdout())
91+
testify.Regexp(t, getServiceRegx("web"), res.Stdout())
92+
93+
assert.Equal(t, len(Lines(res.Stdout())), 4, res.Stdout())
94+
})
95+
96+
t.Run("compose ps hides non running containers", func(t *testing.T) {
97+
res := c.RunDockerCmd("compose", "ps", "-p", projectName)
98+
assert.Equal(t, len(Lines(res.Stdout())), 1, res.Stdout())
99+
})
100+
81101
t.Run("check running project", func(t *testing.T) {
82102
// Docker Desktop kube cluster automatically exposes ports on the host, this is not the case with kind on Desktop,
83103
//we need to connect to the clusterIP, from the kind container

0 commit comments

Comments
 (0)