/
pod.go
145 lines (129 loc) · 3.62 KB
/
pod.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package client
import (
"bytes"
"fmt"
"io"
"time"
"github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/remotecommand"
)
// Pod is a pod returned from Client.
type Pod struct {
UID types.UID
Name string
NodeName string
Cluster string
Namespace string
IP string
HostIP string
Created time.Time
URL string
Image string
ImageID string
pod *corev1.Pod
client *Client
}
func newPod(client *Client, pod *corev1.Pod) *Pod {
return &Pod{
Cluster: client.Cluster(),
UID: pod.GetUID(),
Name: pod.GetName(),
NodeName: pod.Spec.NodeName,
Namespace: pod.GetNamespace(),
IP: pod.Status.PodIP,
HostIP: pod.Status.HostIP,
Created: pod.GetCreationTimestamp().Time,
URL: pod.GetSelfLink(),
Image: getPodFirstContainer(pod).Image,
ImageID: getPodFirstContainerStatus(pod).ImageID,
pod: pod,
client: client,
}
}
// Strings returns name for this pod prefixed with cluster name and namespace.
func (pod Pod) String() string {
return fmt.Sprintf("%s::%s", pod.Namespace, pod.Name)
}
// Age returns duration since the pod started.
func (pod Pod) Age() time.Duration {
if pod.Created.IsZero() {
return -1
}
return time.Since(pod.Created)
}
// PortForward starts port forwarding of pod port to a local random port.
func (pod Pod) PortForward(podPort int) (*PortForwarder, error) {
return PortForward(pod.client, PortForwardOptions{
PodName: pod.Name,
PodNamespace: pod.Namespace,
LocalPort: 0, // pick random
PodPort: podPort,
})
}
// Exec executes a command in the pod.
func (pod Pod) Exec(command string, stdin io.Reader, stdout, stderr io.Writer) error {
container := getPodFirstContainer(pod.pod).Name
return pod.ExecContainer(container, command, stdin, stdout, stderr)
}
func getPodFirstContainer(pod *corev1.Pod) corev1.Container {
if len(pod.Spec.Containers) == 0 {
return corev1.Container{}
}
return pod.Spec.Containers[0]
}
func getPodFirstContainerStatus(pod *corev1.Pod) corev1.ContainerStatus {
if len(pod.Status.ContainerStatuses) == 0 {
return corev1.ContainerStatus{}
}
return pod.Status.ContainerStatuses[0]
}
// ExecContainer executes a command in a container of the pod.
func (pod Pod) ExecContainer(container, command string, stdin io.Reader, stdout, stderr io.Writer) error {
return podExec(pod.client, pod.Namespace, pod.Name, container, command, stdin, stdout, stderr)
}
// podExec exec command on specific pod and wait the command's output.
func podExec(client *Client, namespace, podName, container string,
cmd string, stdin io.Reader, stdout, stderr io.Writer,
) error {
opts := &corev1.PodExecOptions{
Container: container,
Command: []string{"sh", "-c", cmd},
Stdin: stdin != nil,
Stdout: stdout != nil,
Stderr: true,
TTY: false,
}
req := client.client.CoreV1().RESTClient().Post().
Resource("pods").
Name(podName).
Namespace(namespace).
SubResource("exec").
VersionedParams(opts, scheme.ParameterCodec).
Timeout(time.Second * 120)
logrus.WithFields(logrus.Fields{
"container": opts.Container,
"pod": podName,
"namespace": namespace,
"client": client.String(),
}).Tracef("running pod exec: %+v", opts.Command)
exec, err := remotecommand.NewSPDYExecutor(client.restConfig, "POST", req.URL())
if err != nil {
return err
}
if stderr == nil {
stderr = new(bytes.Buffer)
}
err = exec.Stream(remotecommand.StreamOptions{
Stdin: stdin,
Stdout: stdout,
Stderr: stderr,
Tty: false,
})
if err != nil {
return err
}
return nil
}