Skip to content

Commit

Permalink
Add better semver parsing (#496)
Browse files Browse the repository at this point in the history
* add better semver parse

* go mod tidy

* fix test

* add more tests

* fix edge case with uint wrap

* lint
  • Loading branch information
alexmasi authored Feb 16, 2024
1 parent 08163c7 commit 1d152af
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 81 deletions.
133 changes: 56 additions & 77 deletions deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import (
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"time"

"github.com/blang/semver"
dtypes "github.com/docker/docker/api/types"
dclient "github.com/docker/docker/client"
"github.com/openconfig/gnmi/errlist"
Expand Down Expand Up @@ -203,33 +203,10 @@ func (d *Deployment) Deploy(ctx context.Context, kubecfg string) (rerr error) {
return fmt.Errorf("failed to create k8s client: %w", err)
}

log.Infof("Checking kubectl versions.")
output, err := run.OutCommand("kubectl", "version", "--output=yaml")
if err != nil {
return fmt.Errorf("failed get kubectl version: %w", err)
}
kubeYAML := kubeVersion{}
if err := yaml.Unmarshal(output, &kubeYAML); err != nil {
return fmt.Errorf("failed get kubectl version: %w", err)
}
kClientVersion, err := getVersion(kubeYAML.ClientVersion.GitVersion)
if err != nil {
return fmt.Errorf("failed to parse k8s client version: %w", err)
}
kServerVersion, err := getVersion(kubeYAML.ServerVersion.GitVersion)
if err != nil {
return fmt.Errorf("failed to parse k8s server version: %w", err)
}
origMajor := kClientVersion.Major
kClientVersion.Major -= 2
if kServerVersion.Less(kClientVersion) {
log.Warning("Kube client and server versions are not within expected range.")
}
kClientVersion.Major = origMajor + 2
if kClientVersion.Less(kServerVersion) {
log.Warning("Kube client and server versions are not within expected range.")
log.Infof("Validating kubectl version")
if err := validateKubectlVersion(); err != nil {
return fmt.Errorf("kubectl version outside of supported range: %v", err)
}
log.V(1).Info("Found k8s versions:\n", string(output))

ctx, cancel := context.WithCancel(ctx)

Expand Down Expand Up @@ -295,6 +272,55 @@ func (d *Deployment) Deploy(ctx context.Context, kubecfg string) (rerr error) {
return nil
}

func validateKubectlVersion() error {
output, err := run.OutCommand("kubectl", "version", "--output=yaml")
if err != nil {
return fmt.Errorf("failed get kubectl version: %w", err)
}
log.V(1).Info("Found k8s versions:\n", string(output))
kubeYAML := kubeVersion{}
if err := yaml.Unmarshal(output, &kubeYAML); err != nil {
return fmt.Errorf("failed get kubectl version: %w", err)
}
kClientVersion, err := parseVersion(kubeYAML.ClientVersion.GitVersion)
if err != nil {
return fmt.Errorf("failed to parse k8s client version: %w", err)
}
kServerVersion, err := parseVersion(kubeYAML.ServerVersion.GitVersion)
if err != nil {
return fmt.Errorf("failed to parse k8s server version: %w", err)
}
origMajor := kClientVersion.Major
if kClientVersion.Major < 2 {
kClientVersion.Major = 0
} else {
kClientVersion.Major -= 2
}
if kServerVersion.LT(kClientVersion) {
log.Warning("Kube client and server versions are not within expected range.")
}
kClientVersion.Major = origMajor + 2
if kClientVersion.LT(kServerVersion) {
log.Warning("Kube client and server versions are not within expected range.")
}
return nil
}

// parseVersion takes a github semver string and parses it into a comparable struct
// with prereleases and builds stripped.
func parseVersion(s string) (semver.Version, error) {
if !strings.HasPrefix(s, "v") {
return semver.Version{}, fmt.Errorf("missing prefix on major version")
}
v, err := semver.Parse(s[1:])
if err != nil {
return semver.Version{}, err
}
v.Pre = nil
v.Build = nil
return v, nil
}

func (d *Deployment) Delete() error {
log.Infof("Deleting cluster...")
if err := d.Cluster.Delete(); err != nil {
Expand Down Expand Up @@ -397,53 +423,6 @@ type KindSpec struct {
AdditionalManifests []string `yaml:"additionalManifests" kne:"yaml"`
}

type version struct {
Major int
Minor int
Patch int
}

func (v version) String() string {
return fmt.Sprintf("v%d.%d.%d", v.Major, v.Minor, v.Patch)
}

func (v version) Less(t *version) bool {
if v.Major == t.Major {
if v.Minor == t.Minor {
return v.Patch < t.Patch
}
return v.Minor < t.Minor
}
return v.Major < t.Major
}

// getVersion takes a git version tag string "v1.20.1" and returns a version
// comparable version struct.
func getVersion(s string) (*version, error) {
versions := strings.Split(s, ".")
if len(versions) != 3 {
return nil, fmt.Errorf("failed to get versions from: %s", s)
}
v := &version{}
var err error
if !strings.HasPrefix(versions[0], "v") {
return nil, fmt.Errorf("missing prefix on major version: %s", s)
}
v.Major, err = strconv.Atoi(versions[0][1:])
if err != nil {
return nil, fmt.Errorf("failed to convert major version: %s", s)
}
v.Minor, err = strconv.Atoi(versions[1])
if err != nil {
return nil, fmt.Errorf("failed to convert minor version: %s", s)
}
v.Patch, err = strconv.Atoi(versions[2])
if err != nil {
return nil, fmt.Errorf("failed to convert patch version: %s", s)
}
return v, nil
}

func (k *KindSpec) checkDependencies() error {
var errs errlist.List
bins := []string{"kind"}
Expand All @@ -456,7 +435,7 @@ func (k *KindSpec) checkDependencies() error {
return errs.Err()
}
if k.Version != "" {
wantV, err := getVersion(k.Version)
wantV, err := parseVersion(k.Version)
if err != nil {
return fmt.Errorf("failed to parse desired kind version: %w", err)
}
Expand All @@ -471,11 +450,11 @@ func (k *KindSpec) checkDependencies() error {
return fmt.Errorf("failed to parse kind version from: %s", stdout)
}

gotV, err := getVersion(vKindFields[1])
gotV, err := parseVersion(vKindFields[1])
if err != nil {
return fmt.Errorf("kind version check failed: %w", err)
}
if gotV.Less(wantV) {
if gotV.LT(wantV) {
return fmt.Errorf("kind version check failed: got %s, want %s. install with `go install sigs.k8s.io/kind@%s`", gotV, wantV, wantV)
}
log.Infof("kind version valid: got %s want %s", gotV, wantV)
Expand Down
18 changes: 14 additions & 4 deletions deploy/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,28 +349,28 @@ func TestKindSpec(t *testing.T) {
Name: "test",
Version: "versionfoo",
},
wantErr: "failed to get versions from",
wantErr: "No Major.Minor.Patch elements found",
}, {
desc: "failed kind version - invalid major",
k: &KindSpec{
Name: "test",
Version: "vr.1.1",
},
wantErr: "failed to convert major version",
wantErr: `Invalid character(s) found in major number "r"`,
}, {
desc: "failed kind version - invalid minor",
k: &KindSpec{
Name: "test",
Version: "v0.foo.15",
},
wantErr: "failed to convert minor version",
wantErr: `Invalid character(s) found in minor number "foo"`,
}, {
desc: "failed kind version - invalid patch",
k: &KindSpec{
Name: "test",
Version: "v0.1.foo",
},
wantErr: "failed to convert patch version",
wantErr: `Invalid character(s) found in patch number "foo"`,
}, {
desc: "failed kind version less check",
k: &KindSpec{
Expand Down Expand Up @@ -421,6 +421,16 @@ func TestKindSpec(t *testing.T) {
{Cmd: "kind", Args: []string{"version"}, Stdout: "kind v0.15.0 go1.18.2 linux/amd64"},
{Cmd: "kind", Args: []string{"create", "cluster", "--name", "test"}},
},
}, {
desc: "kind prerelease version pass",
k: &KindSpec{
Name: "test",
Version: "v0.15.0",
},
resp: []fexec.Response{
{Cmd: "kind", Args: []string{"version"}, Stdout: "kind v0.15.0-prelease go1.18.2 linux/amd64"},
{Cmd: "kind", Args: []string{"create", "cluster", "--name", "test"}},
},
}, {
desc: "kind version pass - major",
k: &KindSpec{
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.21
require (
cloud.google.com/go/pubsub v1.33.0
github.com/aristanetworks/arista-ceoslab-operator/v2 v2.0.2
github.com/blang/semver v3.5.1+incompatible
github.com/docker/docker v24.0.7+incompatible
github.com/ghodss/yaml v1.0.0
github.com/golang/glog v1.1.2
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
Expand Down

0 comments on commit 1d152af

Please sign in to comment.