k8s: Add scheduler information collection
[integration.git] / test / security / k8s / src / check / rancher / rancher.go
1 // Package rancher wraps Rancher commands necessary for K8s inspection.
2 package rancher
3
4 import (
5         "bytes"
6         "fmt"
7         "os/exec"
8
9         "check"
10 )
11
12 const (
13         bin                      = "rancher"
14         paramHost                = "--host"
15         cmdHosts                 = "hosts"
16         cmdHostsParams           = "--quiet"
17         cmdDocker                = "docker"
18         cmdDockerCmdPs           = "ps"
19         cmdDockerCmdPsParams     = "--no-trunc"
20         cmdDockerCmdPsFilter     = "--filter"
21         cmdDockerCmdPsFilterArgs = "label=io.rancher.stack_service.name="
22         cmdDockerCmdPsFormat     = "--format"
23         cmdDockerCmdPsFormatArgs = "{{.Command}}"
24 )
25
26 // Rancher implements Informer interface.
27 type Rancher struct {
28         check.Informer
29 }
30
31 // GetAPIParams returns parameters of running Kubernetes API server.
32 // It queries default environment set in configuration file.
33 func (r *Rancher) GetAPIParams() ([]string, error) {
34         return getProcessParams(check.APIProcess, check.APIService)
35 }
36
37 // GetSchedulerParams returns parameters of running Kubernetes scheduler.
38 // It queries default environment set in configuration file.
39 func (r *Rancher) GetSchedulerParams() ([]string, error) {
40         return getProcessParams(check.SchedulerProcess, check.SchedulerService)
41 }
42
43 func getProcessParams(process check.Command, service check.Service) ([]string, error) {
44         hosts, err := listHosts()
45         if err != nil {
46                 return []string{}, err
47         }
48
49         for _, host := range hosts {
50                 cmd, err := getPsCmdOutput(host, service)
51                 if err != nil {
52                         return []string{}, err
53                 }
54
55                 if len(cmd) > 0 {
56                         i := bytes.Index(cmd, []byte(process.String()))
57                         if i == -1 {
58                                 return []string{}, fmt.Errorf("missing %s command", process)
59                         }
60                         return btos(cmd[i+len(process.String()):]), nil
61                 }
62         }
63         return []string{}, nil
64 }
65
66 // listHosts lists IDs of active hosts.
67 // It queries default environment set in configuration file.
68 func listHosts() ([]string, error) {
69         cmd := exec.Command(bin, cmdHosts, cmdHostsParams)
70         out, err := cmd.Output()
71         if err != nil {
72                 return nil, err
73         }
74         return btos(out), nil
75 }
76
77 // getPsCmdOutput returns running Kubernetes service command with its parameters.
78 // It queries default environment set in configuration file.
79 func getPsCmdOutput(host string, service check.Service) ([]byte, error) {
80         // Following is equivalent to:
81         // $ rancher --host $HOST \
82         //   docker ps --no-trunc \
83         //   --filter "label=io.rancher.stack_service.name=$SERVICE" \
84         //   --format "{{.Command}}"
85         cmd := exec.Command(bin, paramHost, host,
86                 cmdDocker, cmdDockerCmdPs, cmdDockerCmdPsParams,
87                 cmdDockerCmdPsFilter, cmdDockerCmdPsFilterArgs+service.String(),
88                 cmdDockerCmdPsFormat, cmdDockerCmdPsFormatArgs)
89         out, err := cmd.Output()
90         if err != nil {
91                 return nil, err
92         }
93         return out, nil
94 }
95
96 // btos converts slice of bytes to slice of strings split by white space characters.
97 func btos(in []byte) []string {
98         var out []string
99         for _, b := range bytes.Fields(in) {
100                 out = append(out, string(b))
101         }
102         return out
103 }