-
Notifications
You must be signed in to change notification settings - Fork 9
/
exec_to_pod.go
166 lines (144 loc) · 5.17 KB
/
exec_to_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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package main
import (
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"strings"
core_v1 "k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes"
// "k8s.io/client-go/kubernetes/scheme"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/remotecommand"
)
const debug = false
// GetClientConfig first tries to get a config object which uses the service account kubernetes gives to pods,
// if it is called from a process running in a kubernetes environment.
// Otherwise, it tries to build config from a default kubeconfig filepath if it fails, it fallback to the default config.
// Once it get the config, it returns the same.
func GetClientConfig() (*rest.Config, error) {
config, err := rest.InClusterConfig()
if err != nil {
if debug {
fmt.Printf("Unable to create config. Error: %+v\n", err)
}
err1 := err
kubeconfig := filepath.Join(os.Getenv("HOME"), ".kube", "config")
config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
err = fmt.Errorf("InClusterConfig as well as BuildConfigFromFlags Failed. Error in InClusterConfig: %+v\nError in BuildConfigFromFlags: %+v", err1, err)
return nil, err
}
}
return config, nil
}
// GetClientsetFromConfig takes REST config and Create a clientset based on that and return that clientset
func GetClientsetFromConfig(config *rest.Config) (*kubernetes.Clientset, error) {
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
err = fmt.Errorf("failed creating clientset. Error: %+v", err)
return nil, err
}
return clientset, nil
}
// GetClientset first tries to get a config object which uses the service account kubernetes gives to pods,
// if it is called from a process running in a kubernetes environment.
// Otherwise, it tries to build config from a default kubeconfig filepath if it fails, it fallback to the default config.
// Once it get the config, it creates a new Clientset for the given config and returns the clientset.
func GetClientset() (*kubernetes.Clientset, error) {
config, err := GetClientConfig()
if err != nil {
return nil, err
}
return GetClientsetFromConfig(config)
}
// GetRESTClient first tries to get a config object which uses the service account kubernetes gives to pods,
// if it is called from a process running in a kubernetes environment.
// Otherwise, it tries to build config from a default kubeconfig filepath if it fails, it fallback to the default config.
// Once it get the config, it
func GetRESTClient() (*rest.RESTClient, error) {
config, err := GetClientConfig()
if err != nil {
return &rest.RESTClient{}, err
}
return rest.RESTClientFor(config)
}
// ExecToPodThroughAPI uninterractively exec to the pod with the command specified.
// :param string command: list of the str which specify the command.
// :param string pod_name: Pod name
// :param string namespace: namespace of the Pod.
// :param io.Reader stdin: Standerd Input if necessary, otherwise `nil`
// :return: string: Output of the command. (STDOUT)
// string: Errors. (STDERR)
// error: If any error has occurred otherwise `nil`
func ExecToPodThroughAPI(command, containerName, podName, namespace string, stdin io.Reader) (string, string, error) {
config, err := GetClientConfig()
if err != nil {
return "", "", err
}
clientset, err := GetClientsetFromConfig(config)
if err != nil {
return "", "", err
}
req := clientset.Core().RESTClient().Post().
Resource("pods").
Name(podName).
Namespace(namespace).
SubResource("exec")
scheme := runtime.NewScheme()
if err := core_v1.AddToScheme(scheme); err != nil {
return "", "", fmt.Errorf("error adding to scheme: %v", err)
}
parameterCodec := runtime.NewParameterCodec(scheme)
req.VersionedParams(&core_v1.PodExecOptions{
Command: strings.Fields(command),
Container: containerName,
Stdin: stdin != nil,
Stdout: true,
Stderr: true,
TTY: false,
}, parameterCodec)
if debug {
fmt.Println("Request URL:", req.URL().String())
}
exec, err := remotecommand.NewSPDYExecutor(config, "POST", req.URL())
if err != nil {
return "", "", fmt.Errorf("error while creating Executor: %v", err)
}
var stdout, stderr bytes.Buffer
err = exec.Stream(remotecommand.StreamOptions{
Stdin: stdin,
Stdout: &stdout,
Stderr: &stderr,
Tty: false,
})
if err != nil {
return "", "", fmt.Errorf("error in Stream: %v", err)
}
return stdout.String(), stderr.String(), nil
}
func main() {
var namespace, containerName, podName, command string
fmt.Print("Enter namespace: ")
fmt.Scanln(&namespace)
fmt.Print("Enter name of the pod: ")
fmt.Scanln(&podName)
fmt.Print("Enter name of the container [leave empty if there is only one container]: ")
fmt.Scanln(&containerName)
fmt.Print("Enter the commmand to execute: ")
fmt.Scanln(&command)
// For now I am assuming stdin for the command to be nil
output, stderr, err := ExecToPodThroughAPI(command, containerName, podName, namespace, nil)
if len(stderr) != 0 {
fmt.Println("STDERR:", stderr)
}
if err != nil {
fmt.Printf("Error occured while `exec`ing to the Pod %q, namespace %q, command %q. Error: %+v\n", podName, namespace, command, err)
} else {
fmt.Println("Output:")
fmt.Println(output)
}
}