Skip to content

Commit 0a96204

Browse files
authored
Fix gitops check (weaveworks#4158)
* Fix `gitops check` The `--short` flag has been removed from `kubectl version` in 1.28 (https://github.com/kubernetes/kubernetes/blob/7fe31be11fbe9b44af262d5f5cffb1e73648aa96/CHANGELOG/CHANGELOG-1.28.md#L1718) so the command obviously fails now. This commit changes the behaviour of the `gitops check` command to create a client-go DiscoveryClient and use that to retrieve the server version. That way we don't have to rely on forking a `kubectl` process and the output being consistent. The code is now much cleaner, easier to read and properly tested. closes weaveworks#4157 * Bump supported K8s version The support policy of Weave GitOps is to "test Weave GitOps against the latest supported Kubernetes releases" which means that only 1.26, 1.27 and 1.28 are supported at this point. This change doesn't prevent Weave GitOps from being run on older versions of Kubernetes as the constraint is only used by the `gitops check` command which is purely informational. Signed-off-by: Max Jonas Werner <mail@makk.es>
1 parent 28ab281 commit 0a96204

File tree

4 files changed

+107
-104
lines changed

4 files changed

+107
-104
lines changed

cmd/gitops/check/cmd.go

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,51 @@ import (
44
"fmt"
55

66
"github.com/weaveworks/weave-gitops/cmd/gitops/check/oidcconfig"
7+
"github.com/weaveworks/weave-gitops/cmd/gitops/cmderrors"
78
"github.com/weaveworks/weave-gitops/cmd/gitops/config"
9+
"github.com/weaveworks/weave-gitops/pkg/run"
810
"github.com/weaveworks/weave-gitops/pkg/services/check"
11+
"k8s.io/cli-runtime/pkg/genericclioptions"
12+
"k8s.io/client-go/discovery"
913

1014
"github.com/spf13/cobra"
1115
)
1216

1317
func GetCommand(opts *config.Options) *cobra.Command {
18+
var kubeConfigArgs *genericclioptions.ConfigFlags
19+
1420
cmd := &cobra.Command{
1521
Use: "check",
1622
Short: "Validates flux compatibility",
1723
Example: `
1824
# Validate flux and kubernetes compatibility
1925
gitops check
2026
`,
21-
RunE: runCmd,
27+
RunE: func(cmd *cobra.Command, args []string) error {
28+
kubeConfigArgs = run.GetKubeConfigArgs()
29+
kubeConfigArgs.AddFlags(cmd.Flags())
30+
31+
cfg, err := kubeConfigArgs.ToRESTConfig()
32+
if err != nil {
33+
return err
34+
}
35+
36+
c, err := discovery.NewDiscoveryClientForConfig(cfg)
37+
if err != nil {
38+
return cmderrors.ErrGetKubeClient
39+
}
40+
output, err := check.KubernetesVersion(c)
41+
if err != nil {
42+
return err
43+
}
44+
45+
fmt.Println(output)
46+
47+
return nil
48+
},
2249
}
2350

2451
cmd.AddCommand(oidcconfig.OIDCConfigCommand(opts))
2552

2653
return cmd
2754
}
28-
29-
func runCmd(_ *cobra.Command, _ []string) error {
30-
output, err := check.Pre()
31-
if err != nil {
32-
return err
33-
}
34-
35-
fmt.Println(output)
36-
37-
return nil
38-
}

pkg/services/check/check.go

Lines changed: 14 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,71 +2,33 @@ package check
22

33
import (
44
"fmt"
5-
"os/exec"
6-
"strings"
75

86
"github.com/Masterminds/semver/v3"
7+
"k8s.io/client-go/discovery"
98
)
109

1110
const (
12-
kubernetesConstraints = ">=1.20.6-0"
11+
kubernetesConstraints = ">=1.26"
1312
)
1413

15-
// Pre runs pre-install checks
16-
func Pre() (string, error) {
17-
k8sOutput, err := runKubernetesCheck()
14+
// KubernetesVersion checks if the Kubernetes version of the client is recent enough and
15+
// returns a proper string explaining the result of the check. An error is returned
16+
// if the check could not be performed (e.g. the cluster is not reachable).
17+
func KubernetesVersion(c discovery.DiscoveryInterface) (string, error) {
18+
v, err := c.ServerVersion()
1819
if err != nil {
19-
return "", err
20+
return "", fmt.Errorf("failed getting server version: %w", err)
2021
}
2122

22-
return k8sOutput, nil
23-
}
24-
25-
func runKubernetesCheck() (string, error) {
26-
versionOutput, err := exec.Command("kubectl", "version", "--short").CombinedOutput()
27-
if err != nil {
28-
return "", fmt.Errorf("unable to get kubernetes version: %w", err)
29-
}
30-
31-
v, err := parseVersion(string(versionOutput))
23+
sv, err := semver.NewVersion(v.GitVersion)
3224
if err != nil {
33-
return "", fmt.Errorf("kubernetes version can't be determined: %w", err)
34-
}
35-
36-
return checkKubernetesVersion(v)
37-
}
38-
39-
func checkKubernetesVersion(version *semver.Version) (string, error) {
40-
var valid bool
41-
42-
var vrange string
43-
44-
c, _ := semver.NewConstraint(kubernetesConstraints)
45-
if c.Check(version) {
46-
valid = true
47-
vrange = kubernetesConstraints
48-
}
49-
50-
if !valid {
51-
return "", fmt.Errorf("✗ kubernetes version %s does not match %s", version.Original(), kubernetesConstraints)
52-
}
53-
54-
return fmt.Sprintf("✔ Kubernetes %s %s", version.String(), vrange), nil
55-
}
56-
57-
func parseVersion(text string) (*semver.Version, error) {
58-
version := ""
59-
lines := strings.Split(text, "\n")
60-
61-
for _, line := range lines {
62-
if strings.Contains(line, "Server") {
63-
version = strings.Replace(line, "Server Version: v", "", 1)
64-
}
25+
return "", fmt.Errorf("failed parsing server version %q: %w", v.GitVersion, err)
6526
}
6627

67-
if _, err := semver.StrictNewVersion(version); err != nil {
68-
return nil, err
28+
cons, _ := semver.NewConstraint(kubernetesConstraints)
29+
if !cons.Check(sv) {
30+
return "", fmt.Errorf("✗ kubernetes version %s does not match %s", sv.Original(), kubernetesConstraints)
6931
}
7032

71-
return semver.NewVersion(version)
33+
return fmt.Sprintf("✔ Kubernetes %s %s", sv, kubernetesConstraints), nil
7234
}

pkg/services/check/check_suite_test.go

Lines changed: 0 additions & 13 deletions
This file was deleted.

pkg/services/check/check_test.go

Lines changed: 65 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,78 @@
1-
package check
1+
package check_test
22

33
import (
4-
"github.com/Masterminds/semver/v3"
4+
"errors"
5+
"testing"
6+
7+
"k8s.io/apimachinery/pkg/runtime"
8+
"k8s.io/apimachinery/pkg/version"
9+
fakediscovery "k8s.io/client-go/discovery/fake"
10+
fakeclientset "k8s.io/client-go/kubernetes/fake"
11+
kubetesting "k8s.io/client-go/testing"
512

6-
. "github.com/onsi/ginkgo/v2"
713
. "github.com/onsi/gomega"
14+
"github.com/weaveworks/weave-gitops/pkg/services/check"
815
)
916

10-
var _ = Describe("Check kubernetes version", func() {
11-
It("should show kuberetes is a valid version", func() {
12-
version, err := semver.NewVersion("1.21.1")
13-
Expect(err).ShouldNot(HaveOccurred())
14-
15-
output, err := checkKubernetesVersion(version)
16-
Expect(err).ShouldNot(HaveOccurred())
17+
func TestKubernetesVersionWithError(t *testing.T) {
18+
g := NewWithT(t)
1719

18-
Expect(output).To(Equal("✔ Kubernetes 1.21.1 >=1.20.6-0"))
20+
expectedError := errors.New("an error occurred")
21+
fakeClient := fakeclientset.NewSimpleClientset()
22+
fakeClient.Discovery().(*fakediscovery.FakeDiscovery).PrependReactor("*", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
23+
return true, nil, expectedError
1924
})
2025

21-
It("should fail with version does not match", func() {
22-
version, err := semver.NewVersion("1.19.1")
23-
Expect(err).ShouldNot(HaveOccurred())
26+
res, err := check.KubernetesVersion(fakeClient.Discovery())
2427

25-
_, err = checkKubernetesVersion(version)
26-
Expect(err.Error()).Should(Equal("✗ kubernetes version 1.19.1 does not match >=1.20.6-0"))
27-
})
28-
})
28+
g.Expect(err).To(MatchError(ContainSubstring(expectedError.Error())))
29+
g.Expect(res).To(BeEmpty())
30+
}
2931

30-
var _ = Describe("parse version", func() {
31-
It("should parse version", func() {
32-
expectedVersion, err := semver.NewVersion("1.21.1")
33-
Expect(err).ShouldNot(HaveOccurred())
32+
func TestKubernetesVersion(t *testing.T) {
33+
tests := []struct {
34+
name string
35+
serverVersion string
36+
expectedErr string
37+
expectedRes string
38+
}{
39+
{
40+
name: "server version satisfies constraint",
41+
serverVersion: "v1.28.4",
42+
expectedRes: `^✔ Kubernetes 1.28.4 >=1.`,
43+
},
44+
{
45+
name: "server version too low",
46+
serverVersion: "v1.20.5",
47+
expectedErr: `✗ kubernetes version v1\.20\.5 does not match >=1\.`,
48+
},
49+
{
50+
name: "server version not semver compliant",
51+
serverVersion: "1.x",
52+
expectedErr: `"1.x".*Invalid Semantic Version`,
53+
},
54+
}
3455

35-
output, err := parseVersion("Server Version: v1.21.1")
36-
Expect(err).ShouldNot(HaveOccurred())
56+
for _, tt := range tests {
57+
t.Run(tt.name, func(t *testing.T) {
58+
g := NewWithT(t)
59+
client := fakeclientset.NewSimpleClientset()
60+
fakeDiscovery, ok := client.Discovery().(*fakediscovery.FakeDiscovery)
61+
if !ok {
62+
t.Fatalf("couldn't convert Discovery() to *FakeDiscovery")
63+
}
3764

38-
Expect(output).To(Equal(expectedVersion))
39-
})
40-
})
65+
fakeDiscovery.FakedServerVersion = &version.Info{
66+
GitVersion: tt.serverVersion,
67+
}
68+
69+
res, err := check.KubernetesVersion(client.Discovery())
70+
71+
if tt.expectedErr != "" {
72+
g.Expect(err).To(MatchError(MatchRegexp(tt.expectedErr)))
73+
}
74+
75+
g.Expect(res).To(MatchRegexp(tt.expectedRes))
76+
})
77+
}
78+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy