Skip to content

Commit

Permalink
Merge pull request #164 from deviceinsight/feature/fix-kubectl-versio…
Browse files Browse the repository at this point in the history
…n-detection

fix kubectl version detection
  • Loading branch information
d-rk authored Sep 5, 2023
2 parents b5bde67 + 74ee314 commit 0e8d9f3
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 24 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Fixed
- [#162](https://github.com/deviceinsight/kafkactl/pull/162) Fix `consumer-group` crashes when a group member has no assignment
- [#160](https://github.com/deviceinsight/kafkactl/pull/160) Fix kubectl version detection

### Added
- [#159](https://github.com/deviceinsight/kafkactl/issues/159) Add ability to read config file from `$PWD/kafkactl.yml`

## 3.2.0 - 2023-08-17
Expand Down
98 changes: 94 additions & 4 deletions internal/k8s/executer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,26 @@ package k8s_test

import (
"encoding/json"
"strings"
"testing"

"github.com/deviceinsight/kafkactl/output"

"github.com/deviceinsight/kafkactl/internal"
"github.com/deviceinsight/kafkactl/internal/k8s"
"github.com/deviceinsight/kafkactl/testutil"
)

type TestRunner struct {
binary string
args []string
binary string
args []string
response []byte
}

func (runner *TestRunner) ExecuteAndReturn(binary string, args []string) ([]byte, error) {
runner.binary = binary
runner.args = args
return nil, nil
return runner.response, nil
}

func (runner *TestRunner) Execute(binary string, args []string) error {
Expand Down Expand Up @@ -52,7 +56,8 @@ func TestExecWithImageAndImagePullSecretProvided(t *testing.T) {
if err := json.Unmarshal([]byte(overrides), &podOverrides); err != nil {
t.Fatalf("unable to unmarshall overrides: %v", err)
}
if len(podOverrides.Spec.ImagePullSecrets) != 1 || podOverrides.Spec.ImagePullSecrets[0].Name != context.Kubernetes.ImagePullSecret {
if len(podOverrides.Spec.ImagePullSecrets) != 1 ||
podOverrides.Spec.ImagePullSecrets[0].Name != context.Kubernetes.ImagePullSecret {
t.Fatalf("wrong overrides: %s", overrides)
}
}
Expand All @@ -71,6 +76,91 @@ func TestExecWithImageAndTagFails(t *testing.T) {
testutil.AssertErrorContains(t, "image must not contain a tag", err)
}

//nolint:gocognit
func TestParseKubectlVersion(t *testing.T) {

var testRunner = TestRunner{}
var runner k8s.Runner = &testRunner

type tests struct {
description string
kubectlOutput string
wantErr string
wantVersion k8s.Version
}

for _, test := range []tests{
{
description: "parse_fails_for_unparsable_output",
kubectlOutput: "unknown output",
wantErr: "unable to extract kubectl version",
},
{
description: "parse_valid_kubectl_output_succeeds",
kubectlOutput: `
{
"ClientVersion": {
"major": "1",
"minor": "27",
"gitVersion": "v1.27.1",
"gitCommit": "4c9411232e10168d7b050c49a1b59f6df9d7ea4b",
"gitTreeState": "clean",
"buildDate": "2023-04-14T13:14:41Z",
"goVersion": "go1.20.3",
"compiler": "gc",
"platform": "linux/amd64"
},
"kustomizeVersion": "v5.0.1"
}`,
wantVersion: k8s.Version{
Major: 1,
Minor: 27,
GitVersion: "v1.27.1",
},
},
} {
t.Run(test.description, func(t *testing.T) {

var err error

output.Fail = func(failError error) {
err = failError
}

testRunner.response = []byte(test.kubectlOutput)

version := k8s.GetKubectlVersion("kubectl", &runner)

if test.wantErr != "" {
if err == nil {
t.Errorf("want error %q but got nil", test.wantErr)
}

if !strings.Contains(err.Error(), test.wantErr) {
t.Errorf("want error %q got %q", test.wantErr, err)
}

return
}
if err != nil {
t.Errorf("doesn't want error but got %s", err)
}

if test.wantVersion.GitVersion != version.GitVersion {
t.Fatalf("different version expexted %s != %s", test.wantVersion.GitVersion, version.GitVersion)
}

if test.wantVersion.Major != version.Major {
t.Fatalf("different major version expexted %d != %d", test.wantVersion.Major, version.Major)
}

if test.wantVersion.Minor != version.Minor {
t.Fatalf("different minor version expexted %d != %d", test.wantVersion.Minor, version.Minor)
}
})
}
}

func extractParam(t *testing.T, args []string, param string) string {

var paramIdx int
Expand Down
42 changes: 22 additions & 20 deletions internal/k8s/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"math/rand"
"regexp"
"strconv"
"strings"
"time"
Expand All @@ -16,9 +15,9 @@ import (
)

type Version struct {
Major int
Minor int
Patch int
Major int
Minor int
GitVersion string
}

type executor struct {
Expand Down Expand Up @@ -48,42 +47,45 @@ func randomString(n int) string {
}

func getKubectlVersion(kubectlBinary string, runner *Runner) Version {
bytes, err := (*runner).ExecuteAndReturn(kubectlBinary, []string{"version", "--client", "--short"})
bytes, err := (*runner).ExecuteAndReturn(kubectlBinary, []string{"version", "--client", "-o", "json"})
if err != nil {
output.Fail(err)
return Version{}
}

if len(bytes) == 0 {
return Version{}
}

re := regexp.MustCompile(`v(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)`)
matches := re.FindStringSubmatch(string(bytes))

result := make(map[string]string)
for i, name := range re.SubexpNames() {
result[name] = matches[i]
type versionOutput struct {
ClientVersion struct {
Major string `json:"major"`
Minor string `json:"minor"`
GitVersion string `json:"gitVersion"`
} `json:"clientVersion"`
}

major, err := strconv.Atoi(result["major"])
if err != nil {
output.Fail(err)
var jsonOutput versionOutput

if err := json.Unmarshal(bytes, &jsonOutput); err != nil {
output.Fail(fmt.Errorf("unable to extract kubectl version: %w", err))
return Version{}
}

minor, err := strconv.Atoi(result["minor"])
major, err := strconv.Atoi(jsonOutput.ClientVersion.Major)
if err != nil {
output.Fail(err)
}

patch, err := strconv.Atoi(result["patch"])
minor, err := strconv.Atoi(jsonOutput.ClientVersion.Minor)
if err != nil {
output.Fail(err)
}

return Version{
Major: major,
Minor: minor,
Patch: patch,
Major: major,
Minor: minor,
GitVersion: jsonOutput.ClientVersion.GitVersion,
}
}

Expand Down Expand Up @@ -192,7 +194,7 @@ func filter(slice []string, predicate func(string) bool) (ret []string) {

func (kubectl *executor) exec(args []string) error {
cmd := fmt.Sprintf("exec: %s %s", kubectl.kubectlBinary, join(args))
output.Debugf("kubectl version: %d.%d.%d", kubectl.version.Major, kubectl.version.Minor, kubectl.version.Patch)
output.Debugf("kubectl version: %s", kubectl.version.GitVersion)
output.Debugf(cmd)
err := (*kubectl.runner).Execute(kubectl.kubectlBinary, args)
return err
Expand Down
2 changes: 2 additions & 0 deletions internal/k8s/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ package k8s

var ParsePodEnvironment = parsePodEnvironment

var GetKubectlVersion = getKubectlVersion

var NewExecutor = newExecutor

0 comments on commit 0e8d9f3

Please sign in to comment.