1 // Package raw wraps SSH commands necessary for K8s inspection.
11 "golang.org/x/crypto/ssh"
12 kh "golang.org/x/crypto/ssh/knownhosts"
19 controlplane = "controlplane"
23 knownHostsFile = "~/.ssh/known_hosts"
26 // Raw implements Informer interface.
31 // GetAPIParams returns parameters of running Kubernetes API servers.
32 // It queries only cluster nodes with "controlplane" role.
33 func (r *Raw) GetAPIParams() ([]string, error) {
34 return getProcessParams(check.APIProcess)
37 func getProcessParams(process check.Command) ([]string, error) {
38 nodes, err := config.GetNodesInfo()
40 return []string{}, err
43 for _, node := range nodes {
44 if isControlplaneNode(node.Role) {
45 cmd, err := getInspectCmdOutput(node, process)
47 return []string{}, err
51 i := bytes.Index(cmd, []byte(process.String()))
53 return []string{}, fmt.Errorf("missing %s command", process)
55 return btos(cmd[i+len(process.String()):]), nil
60 return []string{}, nil
63 func isControlplaneNode(roles []string) bool {
64 for _, role := range roles {
65 if role == controlplane {
72 func getInspectCmdOutput(node config.NodeInfo, cmd check.Command) ([]byte, error) {
73 path, err := expandPath(node.SSHKeyPath)
78 pubKey, err := parsePublicKey(path)
83 khPath, err := expandPath(knownHostsFile)
88 hostKeyCallback, err := kh.New(khPath)
93 config := &ssh.ClientConfig{
95 Auth: []ssh.AuthMethod{pubKey},
96 HostKeyCallback: hostKeyCallback,
99 conn, err := ssh.Dial("tcp", node.Address+":"+node.Port, config)
105 out, err := runCommand(fmt.Sprintf("docker inspect %s --format {{.Args}}", cmd), conn)
112 func expandPath(path string) (string, error) {
113 if len(path) == 0 || path[0] != '~' {
117 usr, err := user.Current()
121 return filepath.Join(usr.HomeDir, path[1:]), nil
124 func parsePublicKey(path string) (ssh.AuthMethod, error) {
125 key, err := ioutil.ReadFile(path)
129 signer, err := ssh.ParsePrivateKey(key)
133 return ssh.PublicKeys(signer), nil
136 func runCommand(cmd string, conn *ssh.Client) ([]byte, error) {
137 sess, err := conn.NewSession()
142 out, err := sess.Output(cmd)
149 // btos converts slice of bytes to slice of strings split by white space characters.
150 func btos(in []byte) []string {
152 for _, b := range bytes.Fields(in) {
153 out = append(out, string(b))