Support auto-detect platform when running on EKS or GKE (#683)

* Support auto-detect platform when running on EKS or GKE

* Change to get platform name from `kubectl version`

* fix regexp and add test

* Update Server Version match for EKS

* try to get version info from api sever at first
pull/785/head
Huang Huang 3 years ago committed by GitHub
parent 0fd4dd97b5
commit 0f171657bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -317,13 +317,17 @@ func getBenchmarkVersion(kubeVersion, benchmarkVersion string, v *viper.Viper) (
if !isEmpty(kubeVersion) && !isEmpty(benchmarkVersion) {
return "", fmt.Errorf("It is an error to specify both --version and --benchmark flags")
}
if isEmpty(benchmarkVersion) && isEmpty(kubeVersion) {
benchmarkVersion = getPlatformBenchmarkVersion(getPlatformName())
}
if isEmpty(benchmarkVersion) {
if isEmpty(kubeVersion) {
kubeVersion, err = getKubeVersion()
kv, err := getKubeVersion()
if err != nil {
return "", fmt.Errorf("Version check failed: %s\nAlternatively, you can specify the version with --version", err)
}
kubeVersion = kv.BaseVersion()
}
kubeToBenchmarkMap, err := loadVersionMapping(v)
@ -377,7 +381,7 @@ func isThisNodeRunning(nodeType check.NodeType) bool {
func exitCodeSelection(controlsCollection []*check.Controls) int {
for _, control := range controlsCollection {
if control.Fail > 0 {
if control.Fail > 0 {
return exitCode
}
}

@ -324,7 +324,7 @@ func TestGetBenchmarkVersion(t *testing.T) {
withFakeKubectl := func(kubeVersion, benchmarkVersion string, v *viper.Viper, fn getBenchmarkVersionFnToTest) (string, error) {
execCode := `#!/bin/sh
echo "Server Version: v1.15.10"
echo '{"serverVersion": {"major": "1", "minor": "15", "gitVersion": "v1.15.10"}}'
`
restore, err := fakeExecutableInPath("kubectl", execCode)
if err != nil {

@ -14,7 +14,25 @@ import (
"github.com/golang/glog"
)
func getKubeVersionFromRESTAPI() (string, error) {
type KubeVersion struct {
Major string
Minor string
baseVersion string
GitVersion string
}
func (k *KubeVersion) BaseVersion() string {
if k.baseVersion != "" {
return k.baseVersion
}
// Some provides return the minor version like "15+"
minor := strings.Replace(k.Minor, "+", "", -1)
ver := fmt.Sprintf("%s.%s", k.Major, minor)
k.baseVersion = ver
return ver
}
func getKubeVersionFromRESTAPI() (*KubeVersion, error) {
k8sVersionURL := getKubernetesURL()
serviceaccount := "/var/run/secrets/kubernetes.io/serviceaccount"
cacertfile := fmt.Sprintf("%s/ca.crt", serviceaccount)
@ -22,23 +40,23 @@ func getKubeVersionFromRESTAPI() (string, error) {
tlsCert, err := loadCertficate(cacertfile)
if err != nil {
return "", err
return nil, err
}
tb, err := ioutil.ReadFile(tokenfile)
if err != nil {
return "", err
return nil, err
}
token := strings.TrimSpace(string(tb))
data, err := getWebDataWithRetry(k8sVersionURL, token, tlsCert)
if err != nil {
return "", err
return nil, err
}
k8sVersion, err := extractVersion(data)
if err != nil {
return "", err
return nil, err
}
return k8sVersion, nil
}
@ -61,31 +79,32 @@ func getWebDataWithRetry(k8sVersionURL, token string, cacert *tls.Certificate) (
return
}
func extractVersion(data []byte) (string, error) {
type versionResponse struct {
Major string
Minor string
GitVersion string
GitCommit string
GitTreeState string
BuildDate string
GoVersion string
Compiler string
Platform string
}
type VersionResponse struct {
Major string
Minor string
GitVersion string
GitCommit string
GitTreeState string
BuildDate string
GoVersion string
Compiler string
Platform string
}
vrObj := &versionResponse{}
func extractVersion(data []byte) (*KubeVersion, error) {
vrObj := &VersionResponse{}
glog.V(2).Info(fmt.Sprintf("vd: %s\n", string(data)))
err := json.Unmarshal(data, vrObj)
if err != nil {
return "", err
return nil, err
}
glog.V(2).Info(fmt.Sprintf("vrObj: %#v\n", vrObj))
// Some provides return the minor version like "15+"
minor := strings.Replace(vrObj.Minor, "+", "", -1)
ver := fmt.Sprintf("%s.%s", vrObj.Major, minor)
return ver, nil
return &KubeVersion{
Major: vrObj.Major,
Minor: vrObj.Minor,
GitVersion: vrObj.GitVersion,
}, nil
}
func getWebData(srvURL, token string, cacert *tls.Certificate) ([]byte, error) {

@ -218,7 +218,7 @@ func TestExtractVersion(t *testing.T) {
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if c.expectedVer != ver {
if c.expectedVer != ver.BaseVersion() {
t.Errorf("Expected %q but Got %q", c.expectedVer, ver)
}
} else {

@ -1,6 +1,7 @@
package cmd
import (
"encoding/json"
"fmt"
"os"
"os/exec"
@ -279,7 +280,7 @@ Alternatively, you can specify the version with --version
kube-bench --version <VERSION> ...
`
func getKubeVersion() (string, error) {
func getKubeVersion() (*KubeVersion, error) {
if k8sVer, err := getKubeVersionFromRESTAPI(); err == nil {
glog.V(2).Info(fmt.Sprintf("Kubernetes REST API Reported version: %s", k8sVer))
@ -300,7 +301,7 @@ func getKubeVersion() (string, error) {
}
glog.Warning(missingKubectlKubeletMessage)
return "", fmt.Errorf("unable to find the programs kubectl or kubelet in the PATH")
return nil, fmt.Errorf("unable to find the programs kubectl or kubelet in the PATH")
}
return getKubeVersionFromKubelet(), nil
}
@ -308,8 +309,8 @@ func getKubeVersion() (string, error) {
return getKubeVersionFromKubectl(), nil
}
func getKubeVersionFromKubectl() string {
cmd := exec.Command("kubectl", "version", "--short")
func getKubeVersionFromKubectl() *KubeVersion {
cmd := exec.Command("kubectl", "version", "-o", "json")
out, err := cmd.CombinedOutput()
if err != nil {
glog.V(2).Info(err)
@ -318,7 +319,7 @@ func getKubeVersionFromKubectl() string {
return getVersionFromKubectlOutput(string(out))
}
func getKubeVersionFromKubelet() string {
func getKubeVersionFromKubelet() *KubeVersion {
cmd := exec.Command("kubelet", "--version")
out, err := cmd.CombinedOutput()
@ -329,28 +330,38 @@ func getKubeVersionFromKubelet() string {
return getVersionFromKubeletOutput(string(out))
}
func getVersionFromKubectlOutput(s string) string {
serverVersionRe := regexp.MustCompile(`Server Version: v(\d+.\d+)`)
subs := serverVersionRe.FindStringSubmatch(s)
if len(subs) < 2 {
func getVersionFromKubectlOutput(s string) *KubeVersion {
glog.V(2).Info(s)
type versionResult struct {
ServerVersion VersionResponse
}
vrObj := &versionResult{}
if err := json.Unmarshal([]byte(s), vrObj); err != nil {
glog.V(2).Info(err)
if strings.Contains(s, "The connection to the server") {
msg := fmt.Sprintf(`Warning: Kubernetes version was not auto-detected because kubectl could not connect to the Kubernetes server. This may be because the kubeconfig information is missing or has credentials that do not match the server. Assuming default version %s`, defaultKubeVersion)
fmt.Fprintln(os.Stderr, msg)
}
glog.V(1).Info(fmt.Sprintf("Unable to get Kubernetes version from kubectl, using default version: %s", defaultKubeVersion))
return defaultKubeVersion
return &KubeVersion{baseVersion: defaultKubeVersion}
}
sv := vrObj.ServerVersion
return &KubeVersion{
Major: sv.Major,
Minor: sv.Minor,
GitVersion: sv.GitVersion,
}
return subs[1]
}
func getVersionFromKubeletOutput(s string) string {
func getVersionFromKubeletOutput(s string) *KubeVersion {
glog.V(2).Info(s)
serverVersionRe := regexp.MustCompile(`Kubernetes v(\d+.\d+)`)
subs := serverVersionRe.FindStringSubmatch(s)
if len(subs) < 2 {
glog.V(1).Info(fmt.Sprintf("Unable to get Kubernetes version from kubelet, using default version: %s", defaultKubeVersion))
return defaultKubeVersion
return &KubeVersion{baseVersion: defaultKubeVersion}
}
return subs[1]
return &KubeVersion{baseVersion: subs[1]}
}
func makeSubstitutions(s string, ext string, m map[string]string) (string, []string) {
@ -408,3 +419,31 @@ These program names are provided in the config.yaml, section '%s.%s.bins'
return fmt.Sprintf(errMessageTemplate, component, componentRoleName, binList, componentType, component)
}
func getPlatformName() string {
kv, err := getKubeVersion()
if err != nil {
glog.V(2).Info(err)
return ""
}
return getPlatformNameFromVersion(kv.GitVersion)
}
func getPlatformNameFromVersion(s string) string {
versionRe := regexp.MustCompile(`v\d+\.\d+\.\d+-(\w+)(?:[.\-])\w+`)
subs := versionRe.FindStringSubmatch(s)
if len(subs) < 2 {
return ""
}
return subs[1]
}
func getPlatformBenchmarkVersion(platform string) string {
switch platform {
case "eks":
return "eks-1.0"
case "gke":
return "gke-1.0"
}
return ""
}

@ -202,17 +202,21 @@ func TestMultiWordReplace(t *testing.T) {
}
}
func TestKubeVersionRegex(t *testing.T) {
ver := getVersionFromKubectlOutput(`Client Version: v1.8.0
Server Version: v1.8.12
`)
if ver != "1.8" {
t.Fatalf("Expected 1.8 got %s", ver)
func Test_getVersionFromKubectlOutput(t *testing.T) {
ver := getVersionFromKubectlOutput(`{
"serverVersion": {
"major": "1",
"minor": "8",
"gitVersion": "v1.8.0"
}
}`)
if ver.BaseVersion() != "1.8" {
t.Fatalf("Expected 1.8 got %s", ver.BaseVersion())
}
ver = getVersionFromKubectlOutput("Something completely different")
if ver != defaultKubeVersion {
t.Fatalf("Expected %s got %s", defaultKubeVersion, ver)
if ver.BaseVersion() != defaultKubeVersion {
t.Fatalf("Expected %s got %s", defaultKubeVersion, ver.BaseVersion())
}
}
@ -512,3 +516,89 @@ func TestGetYamlFilesFromDir(t *testing.T) {
t.Fatalf("Expected to find something.yaml, found %s", files[0])
}
}
func Test_getPlatformNameFromKubectlOutput(t *testing.T) {
type args struct {
s string
}
tests := []struct {
name string
args args
want string
}{
{
name: "eks",
args: args{s: "v1.17.9-eks-4c6976"},
want: "eks",
},
{
name: "gke",
args: args{s: "v1.17.6-gke.1"},
want: "gke",
},
{
name: "unknown",
args: args{s: "v1.17.6"},
want: "",
},
{
name: "empty string",
args: args{s: ""},
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := getPlatformNameFromVersion(tt.args.s); got != tt.want {
t.Errorf("getPlatformNameFromKubectlOutput() = %v, want %v", got, tt.want)
}
})
}
}
func Test_getPlatformBenchmarkVersion(t *testing.T) {
type args struct {
platform string
}
tests := []struct {
name string
args args
want string
}{
{
name: "eks",
args: args{
platform: "eks",
},
want: "eks-1.0",
},
{
name: "gke",
args: args{
platform: "gke",
},
want: "gke-1.0",
},
{
name: "unknown",
args: args{
platform: "rh",
},
want: "",
},
{
name: "empty",
args: args{
platform: "",
},
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := getPlatformBenchmarkVersion(tt.args.platform); got != tt.want {
t.Errorf("getPlatformBenchmarkVersion() = %v, want %v", got, tt.want)
}
})
}
}

Loading…
Cancel
Save