@@ -31,10 +31,12 @@ import (
31
31
"golang.org/x/sync/errgroup"
32
32
corev1 "k8s.io/api/core/v1"
33
33
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34
+ "k8s.io/apimachinery/pkg/runtime"
34
35
"k8s.io/cli-runtime/pkg/genericclioptions"
35
36
"k8s.io/client-go/kubernetes"
36
37
"k8s.io/client-go/rest"
37
38
"k8s.io/client-go/tools/portforward"
39
+ "k8s.io/client-go/tools/remotecommand"
38
40
"k8s.io/client-go/transport/spdy"
39
41
)
40
42
@@ -43,6 +45,7 @@ type KubeClient struct {
43
45
client * kubernetes.Clientset
44
46
namespace string
45
47
config * rest.Config
48
+ ioStreams genericclioptions.IOStreams
46
49
}
47
50
48
51
// NewKubeClient new kubernetes client
@@ -54,7 +57,7 @@ func NewKubeClient(config genericclioptions.RESTClientGetter) (*KubeClient, erro
54
57
55
58
clientset , err := kubernetes .NewForConfig (restConfig )
56
59
if err != nil {
57
- return nil , err
60
+ return nil , fmt . Errorf ( "failed creating clientset. Error: %+v" , err )
58
61
}
59
62
60
63
namespace , _ , err := config .ToRawKubeConfigLoader ().Namespace ()
@@ -66,9 +69,83 @@ func NewKubeClient(config genericclioptions.RESTClientGetter) (*KubeClient, erro
66
69
client : clientset ,
67
70
namespace : namespace ,
68
71
config : restConfig ,
72
+ ioStreams : genericclioptions.IOStreams {In : os .Stdin , Out : os .Stdout , ErrOut : os .Stderr },
69
73
}, nil
70
74
}
71
75
76
+ // GetContainers get containers for a given compose project
77
+ func (kc KubeClient ) GetPod (ctx context.Context , projectName , serviceName string ) (* corev1.Pod , error ) {
78
+ pods , err := kc .client .CoreV1 ().Pods (kc .namespace ).List (ctx , metav1.ListOptions {
79
+ LabelSelector : fmt .Sprintf ("%s=%s" , compose .ProjectTag , projectName ),
80
+ })
81
+ if err != nil {
82
+ return nil , err
83
+ }
84
+ if pods == nil {
85
+ return nil , nil
86
+ }
87
+ var pod corev1.Pod
88
+ for _ , p := range pods .Items {
89
+ service := p .Labels [compose .ServiceTag ]
90
+ if service == serviceName {
91
+ pod = p
92
+ break
93
+ }
94
+ }
95
+ return & pod , nil
96
+ }
97
+
98
+ // Exec executes a command in a container
99
+ func (kc KubeClient ) Exec (ctx context.Context , projectName string , opts compose.RunOptions ) error {
100
+ pod , err := kc .GetPod (ctx , projectName , opts .Service )
101
+ if err != nil || pod == nil {
102
+ return err
103
+ }
104
+ if len (pod .Spec .Containers ) == 0 {
105
+ return fmt .Errorf ("no containers running in pod %s" , pod .Name )
106
+ }
107
+ // get first container in the pod
108
+ container := & pod .Spec .Containers [0 ]
109
+ containerName := container .Name
110
+
111
+ req := kc .client .CoreV1 ().RESTClient ().Post ().
112
+ Resource ("pods" ).
113
+ Name (pod .Name ).
114
+ Namespace (kc .namespace ).
115
+ SubResource ("exec" )
116
+
117
+ option := & corev1.PodExecOptions {
118
+ Container : containerName ,
119
+ Command : opts .Command ,
120
+ Stdin : true ,
121
+ Stdout : true ,
122
+ Stderr : true ,
123
+ TTY : opts .Tty ,
124
+ }
125
+
126
+ if opts .Reader == nil {
127
+ option .Stdin = false
128
+ }
129
+
130
+ scheme := runtime .NewScheme ()
131
+ if err := corev1 .AddToScheme (scheme ); err != nil {
132
+ return fmt .Errorf ("error adding to scheme: %v" , err )
133
+ }
134
+ parameterCodec := runtime .NewParameterCodec (scheme )
135
+ req .VersionedParams (option , parameterCodec )
136
+
137
+ exec , err := remotecommand .NewSPDYExecutor (kc .config , "POST" , req .URL ())
138
+ if err != nil {
139
+ return err
140
+ }
141
+ return exec .Stream (remotecommand.StreamOptions {
142
+ Stdin : opts .Reader ,
143
+ Stdout : opts .Writer ,
144
+ Stderr : opts .Writer ,
145
+ Tty : opts .Tty ,
146
+ })
147
+ }
148
+
72
149
// GetContainers get containers for a given compose project
73
150
func (kc KubeClient ) GetContainers (ctx context.Context , projectName string , all bool ) ([]compose.ContainerSummary , error ) {
74
151
fieldSelector := ""
@@ -178,9 +255,13 @@ func (kc KubeClient) MapPorts(ctx context.Context, opts PortMappingOptions) erro
178
255
for serviceName , servicePorts := range opts .Services {
179
256
serviceName = serviceName
180
257
servicePorts = servicePorts
258
+ pod , err := kc .GetPod (ctx , opts .ProjectName , serviceName )
259
+ if err != nil {
260
+ return err
261
+ }
181
262
eg .Go (func () error {
182
263
183
- req := kc .client .RESTClient ().Post ().Resource ("services " ).Namespace (kc .namespace ).Name (serviceName ).SubResource ("portforward" )
264
+ req := kc .client .RESTClient ().Post ().Resource ("pods " ).Namespace (kc .namespace ).Name (pod . Name ). SubResource ( "portforward" ) //fmt.Sprintf("service/%s", serviceName) ).SubResource("portforward")
184
265
transport , upgrader , err := spdy .RoundTripperFor (kc .config )
185
266
if err != nil {
186
267
return err
0 commit comments