Skip to content

chore(coderd): extract api version validation to util package #11407

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

Merged
merged 3 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
address PR comments
  • Loading branch information
johnstcn committed Jan 5, 2024
commit 3e427f059fbac265e447f604c3f35effc11b8092
46 changes: 31 additions & 15 deletions coderd/util/apiversion/apiversion.go
Original file line number Diff line number Diff line change
@@ -1,47 +1,63 @@
package apiversion

import (
"slices"
"strconv"
"strings"

"golang.org/x/xerrors"
)

func New(maj []int, min int) *APIVersion {
// New returns an *APIVersion with the given major.minor and
// additional supported major versions.
func New(maj, min int) *APIVersion {
v := &APIVersion{
supportedMajors: maj,
supportedMinor: min,
supportedMajor: maj,
supportedMinor: min,
additionalMajors: make([]int, 0),
}
return v
}

type APIVersion struct {
supportedMajors []int
supportedMinor int
supportedMajor int
supportedMinor int
additionalMajors []int
}

func (v *APIVersion) WithBackwardCompat(majs ...int) *APIVersion {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love this.

v.additionalMajors = append(v.additionalMajors, majs[:]...)
return v
}

// Validate validates the given version against the given constraints:
// A given major.minor version is valid iff:
// 1. The requested major version is contained within v.supportedMajors
// 2. If the requested major version is the 'current major', then
// the requested minor version must be less than or equal to the supported
// minor version.
//
// For example, given majors {1, 2} and minor 2, then:
// - 0.x is not supported,
// - 1.x is supported,
// - 2.0, 2.1, and 2.2 are supported,
// - 2.3+ is not supported.
func (v *APIVersion) Validate(version string) error {
if len(v.supportedMajors) == 0 {
return xerrors.Errorf("developer error: SupportedMajors is empty")
}
currentMajor := slices.Max(v.supportedMajors)
major, minor, err := Parse(version)
if err != nil {
return err
}
if major > currentMajor {
if major > v.supportedMajor {
return xerrors.Errorf("server is at version %d.%d, behind requested major version %s",
currentMajor, v.supportedMinor, version)
v.supportedMajor, v.supportedMinor, version)
}
if major == currentMajor {
if major == v.supportedMajor {
if minor > v.supportedMinor {
return xerrors.Errorf("server is at version %d.%d, behind requested minor version %s",
currentMajor, v.supportedMinor, version)
v.supportedMajor, v.supportedMinor, version)
}
return nil
}
for _, mjr := range v.supportedMajors {
for _, mjr := range v.additionalMajors {
if major == mjr {
return nil
}
Expand Down
27 changes: 16 additions & 11 deletions coderd/util/apiversion/apiversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,37 +9,38 @@ import (
)

func TestAPIVersionValidate(t *testing.T) {
t.Parallel()

// Given
v := apiversion.New([]int{2, 1}, 0)
v := apiversion.New(2, 1).WithBackwardCompat(1)

t.Parallel()
for _, tc := range []struct {
name string
version string
expectedError string
}{
{
name: "OK",
version: "2.1",
},
{
name: "MinorOK",
version: "2.0",
},
{
name: "MajorOK",
version: "1.0",
},
{
name: "TooNewMinor",
version: "2.1",
version: "2.2",
expectedError: "behind requested minor version",
},
{
name: "TooNewMajor",
version: "3.1",
expectedError: "behind requested major version",
},
{
name: "1.0",
version: "1.0",
},
{
name: "2.0",
version: "2.0",
},
{
name: "Malformed0",
version: "cats",
Expand Down Expand Up @@ -74,7 +75,11 @@ func TestAPIVersionValidate(t *testing.T) {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

// When
err := v.Validate(tc.version)

// Then
if tc.expectedError == "" {
require.NoError(t, err)
} else {
Expand Down
5 changes: 1 addition & 4 deletions tailnet/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ const (
CurrentMinor = 0
)

var (
SupportedMajors = []int{2, 1}
CurrentVersion = apiversion.New(SupportedMajors, CurrentMinor)
)
var CurrentVersion = apiversion.New(CurrentMajor, CurrentMinor).WithBackwardCompat(1)

type streamIDContextKey struct{}

Expand Down