Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add better semver parsing #496

Merged
merged 6 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading