From 5ff513e9bfd9cc3179d55ff1de24143220bc850a Mon Sep 17 00:00:00 2001 From: Fabian Fulga Date: Wed, 31 Jul 2024 15:29:56 +0300 Subject: [PATCH 01/12] Create a common Environment struct --- execution/execution.go | 27 + execution/v0.1.0/commands.go | 2 +- execution/v0.1.0/execution.go | 22 +- execution/v0.1.0/execution_test.go | 44 +- execution/v0.1.0/interface.go | 2 +- execution/v0.1.1/commands.go | 2 +- execution/v0.1.1/execution.go | 22 +- execution/v0.1.1/execution_test.go | 44 +- execution/v0.1.1/interface.go | 2 +- go.mod | 1 + go.sum | 2 + .../Masterminds/semver/v3/.gitignore | 1 + .../Masterminds/semver/v3/.golangci.yml | 27 + .../Masterminds/semver/v3/CHANGELOG.md | 214 ++++++ .../Masterminds/semver/v3/LICENSE.txt | 19 + .../github.com/Masterminds/semver/v3/Makefile | 30 + .../Masterminds/semver/v3/README.md | 258 +++++++ .../Masterminds/semver/v3/SECURITY.md | 19 + .../Masterminds/semver/v3/collection.go | 24 + .../Masterminds/semver/v3/constraints.go | 594 ++++++++++++++++ .../github.com/Masterminds/semver/v3/doc.go | 184 +++++ .../Masterminds/semver/v3/version.go | 639 ++++++++++++++++++ vendor/modules.txt | 3 + 23 files changed, 2112 insertions(+), 70 deletions(-) create mode 100644 execution/execution.go create mode 100644 vendor/github.com/Masterminds/semver/v3/.gitignore create mode 100644 vendor/github.com/Masterminds/semver/v3/.golangci.yml create mode 100644 vendor/github.com/Masterminds/semver/v3/CHANGELOG.md create mode 100644 vendor/github.com/Masterminds/semver/v3/LICENSE.txt create mode 100644 vendor/github.com/Masterminds/semver/v3/Makefile create mode 100644 vendor/github.com/Masterminds/semver/v3/README.md create mode 100644 vendor/github.com/Masterminds/semver/v3/SECURITY.md create mode 100644 vendor/github.com/Masterminds/semver/v3/collection.go create mode 100644 vendor/github.com/Masterminds/semver/v3/constraints.go create mode 100644 vendor/github.com/Masterminds/semver/v3/doc.go create mode 100644 vendor/github.com/Masterminds/semver/v3/version.go diff --git a/execution/execution.go b/execution/execution.go new file mode 100644 index 0000000..e123d87 --- /dev/null +++ b/execution/execution.go @@ -0,0 +1,27 @@ +// Copyright 2023 Cloudbase Solutions SRL +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package execution + +import ( + semver "github.com/Masterminds/semver/v3" + executionv010 "github.com/cloudbase/garm-provider-common/execution/v0.1.0" + executionv011 "github.com/cloudbase/garm-provider-common/execution/v0.1.1" +) + +type Environment struct { + EnvironmentV010 executionv010.EnvironmentV010 + EnvironmentV011 executionv011.EnvironmentV011 + InterfaceVersion semver.Version +} diff --git a/execution/v0.1.0/commands.go b/execution/v0.1.0/commands.go index b7976bf..1e91fb1 100644 --- a/execution/v0.1.0/commands.go +++ b/execution/v0.1.0/commands.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package execution +package executionv010 type ExecutionCommand string diff --git a/execution/v0.1.0/execution.go b/execution/v0.1.0/execution.go index 948fe3a..b28c7d2 100644 --- a/execution/v0.1.0/execution.go +++ b/execution/v0.1.0/execution.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package execution +package executionv010 import ( "bytes" @@ -48,8 +48,8 @@ func ResolveErrorToExitCode(err error) int { return 0 } -func GetEnvironment() (Environment, error) { - env := Environment{ +func GetEnvironment() (EnvironmentV010, error) { + env := EnvironmentV010{ Command: ExecutionCommand(os.Getenv("GARM_COMMAND")), ControllerID: os.Getenv("GARM_CONTROLLER_ID"), PoolID: os.Getenv("GARM_POOL_ID"), @@ -61,21 +61,21 @@ func GetEnvironment() (Environment, error) { // from stdin if env.Command == CreateInstanceCommand { if isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) { - return Environment{}, fmt.Errorf("%s requires data passed into stdin", CreateInstanceCommand) + return EnvironmentV010{}, fmt.Errorf("%s requires data passed into stdin", CreateInstanceCommand) } var data bytes.Buffer if _, err := io.Copy(&data, os.Stdin); err != nil { - return Environment{}, fmt.Errorf("failed to copy bootstrap params") + return EnvironmentV010{}, fmt.Errorf("failed to copy bootstrap params") } if data.Len() == 0 { - return Environment{}, fmt.Errorf("%s requires data passed into stdin", CreateInstanceCommand) + return EnvironmentV010{}, fmt.Errorf("%s requires data passed into stdin", CreateInstanceCommand) } var bootstrapParams params.BootstrapInstance if err := json.Unmarshal(data.Bytes(), &bootstrapParams); err != nil { - return Environment{}, fmt.Errorf("failed to decode instance params: %w", err) + return EnvironmentV010{}, fmt.Errorf("failed to decode instance params: %w", err) } if bootstrapParams.ExtraSpecs == nil { // Initialize ExtraSpecs as an empty JSON object @@ -85,13 +85,13 @@ func GetEnvironment() (Environment, error) { } if err := env.Validate(); err != nil { - return Environment{}, fmt.Errorf("failed to validate execution environment: %w", err) + return EnvironmentV010{}, fmt.Errorf("failed to validate execution environment: %w", err) } return env, nil } -type Environment struct { +type EnvironmentV010 struct { Command ExecutionCommand ControllerID string PoolID string @@ -100,7 +100,7 @@ type Environment struct { BootstrapParams params.BootstrapInstance } -func (e Environment) Validate() error { +func (e EnvironmentV010) Validate() error { if e.Command == "" { return fmt.Errorf("missing GARM_COMMAND") } @@ -147,7 +147,7 @@ func (e Environment) Validate() error { return nil } -func Run(ctx context.Context, provider ExternalProvider, env Environment) (string, error) { +func Run(ctx context.Context, provider ExternalProvider, env EnvironmentV010) (string, error) { var ret string switch env.Command { case CreateInstanceCommand: diff --git a/execution/v0.1.0/execution_test.go b/execution/v0.1.0/execution_test.go index 459f9d8..5c994fc 100644 --- a/execution/v0.1.0/execution_test.go +++ b/execution/v0.1.0/execution_test.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package execution +package executionv010 import ( "context" @@ -129,12 +129,12 @@ func TestValidateEnvironment(t *testing.T) { tests := []struct { name string - env Environment + env EnvironmentV010 errString string }{ { name: "valid environment", - env: Environment{ + env: EnvironmentV010{ Command: CreateInstanceCommand, ControllerID: "controller-id", PoolID: "pool-id", @@ -148,14 +148,14 @@ func TestValidateEnvironment(t *testing.T) { }, { name: "invalid command", - env: Environment{ + env: EnvironmentV010{ Command: "", }, errString: "missing GARM_COMMAND", }, { name: "invalid provider config file", - env: Environment{ + env: EnvironmentV010{ Command: CreateInstanceCommand, ProviderConfigFile: "", }, @@ -163,7 +163,7 @@ func TestValidateEnvironment(t *testing.T) { }, { name: "error accessing config file", - env: Environment{ + env: EnvironmentV010{ Command: CreateInstanceCommand, ProviderConfigFile: "invalid-file", }, @@ -171,7 +171,7 @@ func TestValidateEnvironment(t *testing.T) { }, { name: "invalid controller ID", - env: Environment{ + env: EnvironmentV010{ Command: CreateInstanceCommand, ProviderConfigFile: tmpfile.Name(), }, @@ -180,7 +180,7 @@ func TestValidateEnvironment(t *testing.T) { { name: "invalid instance ID", - env: Environment{ + env: EnvironmentV010{ Command: DeleteInstanceCommand, ProviderConfigFile: tmpfile.Name(), ControllerID: "controller-id", @@ -190,7 +190,7 @@ func TestValidateEnvironment(t *testing.T) { }, { name: "invalid pool ID", - env: Environment{ + env: EnvironmentV010{ Command: ListInstancesCommand, ProviderConfigFile: tmpfile.Name(), ControllerID: "controller-id", @@ -200,7 +200,7 @@ func TestValidateEnvironment(t *testing.T) { }, { name: "invalid bootstrap params", - env: Environment{ + env: EnvironmentV010{ Command: CreateInstanceCommand, ProviderConfigFile: tmpfile.Name(), ControllerID: "controller-id", @@ -211,7 +211,7 @@ func TestValidateEnvironment(t *testing.T) { }, { name: "missing pool ID", - env: Environment{ + env: EnvironmentV010{ Command: CreateInstanceCommand, ProviderConfigFile: tmpfile.Name(), ControllerID: "controller-id", @@ -224,7 +224,7 @@ func TestValidateEnvironment(t *testing.T) { }, { name: "unknown command", - env: Environment{ + env: EnvironmentV010{ Command: "unknown-command", ProviderConfigFile: tmpfile.Name(), ControllerID: "controller-id", @@ -253,14 +253,14 @@ func TestValidateEnvironment(t *testing.T) { func TestRun(t *testing.T) { tests := []struct { name string - providerEnv Environment + providerEnv EnvironmentV010 providerInstance params.ProviderInstance providerErr error expectedErrMsg string }{ { name: "Valid environment", - providerEnv: Environment{ + providerEnv: EnvironmentV010{ Command: CreateInstanceCommand, }, providerInstance: params.ProviderInstance{ @@ -272,7 +272,7 @@ func TestRun(t *testing.T) { }, { name: "Failed to create instance", - providerEnv: Environment{ + providerEnv: EnvironmentV010{ Command: CreateInstanceCommand, }, providerInstance: params.ProviderInstance{ @@ -284,7 +284,7 @@ func TestRun(t *testing.T) { }, { name: "Failed to get instance", - providerEnv: Environment{ + providerEnv: EnvironmentV010{ Command: GetInstanceCommand, }, providerInstance: params.ProviderInstance{ @@ -296,7 +296,7 @@ func TestRun(t *testing.T) { }, { name: "Failed to list instances", - providerEnv: Environment{ + providerEnv: EnvironmentV010{ Command: ListInstancesCommand, }, providerInstance: params.ProviderInstance{ @@ -308,7 +308,7 @@ func TestRun(t *testing.T) { }, { name: "Failed to delete instance", - providerEnv: Environment{ + providerEnv: EnvironmentV010{ Command: DeleteInstanceCommand, }, providerInstance: params.ProviderInstance{ @@ -320,7 +320,7 @@ func TestRun(t *testing.T) { }, { name: "Failed to remove all instances", - providerEnv: Environment{ + providerEnv: EnvironmentV010{ Command: RemoveAllInstancesCommand, }, providerInstance: params.ProviderInstance{ @@ -332,7 +332,7 @@ func TestRun(t *testing.T) { }, { name: "Failed to start instance", - providerEnv: Environment{ + providerEnv: EnvironmentV010{ Command: StartInstanceCommand, }, providerInstance: params.ProviderInstance{ @@ -344,7 +344,7 @@ func TestRun(t *testing.T) { }, { name: "Failed to stop instance", - providerEnv: Environment{ + providerEnv: EnvironmentV010{ Command: StopInstanceCommand, }, providerInstance: params.ProviderInstance{ @@ -356,7 +356,7 @@ func TestRun(t *testing.T) { }, { name: "Invalid command", - providerEnv: Environment{ + providerEnv: EnvironmentV010{ Command: "invalid-command", }, providerInstance: params.ProviderInstance{ diff --git a/execution/v0.1.0/interface.go b/execution/v0.1.0/interface.go index 24e39e0..d95df9f 100644 --- a/execution/v0.1.0/interface.go +++ b/execution/v0.1.0/interface.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package execution +package executionv010 import ( "context" diff --git a/execution/v0.1.1/commands.go b/execution/v0.1.1/commands.go index 2b42efc..ed2ca1f 100644 --- a/execution/v0.1.1/commands.go +++ b/execution/v0.1.1/commands.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package execution +package executionv011 type ExecutionCommand string diff --git a/execution/v0.1.1/execution.go b/execution/v0.1.1/execution.go index 4294757..80ef4a7 100644 --- a/execution/v0.1.1/execution.go +++ b/execution/v0.1.1/execution.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package execution +package executionv011 import ( "bytes" @@ -48,8 +48,8 @@ func ResolveErrorToExitCode(err error) int { return 0 } -func GetEnvironment() (Environment, error) { - env := Environment{ +func GetEnvironment() (EnvironmentV011, error) { + env := EnvironmentV011{ Command: ExecutionCommand(os.Getenv("GARM_COMMAND")), ControllerID: os.Getenv("GARM_CONTROLLER_ID"), PoolID: os.Getenv("GARM_POOL_ID"), @@ -63,21 +63,21 @@ func GetEnvironment() (Environment, error) { // from stdin if env.Command == CreateInstanceCommand { if isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) { - return Environment{}, fmt.Errorf("%s requires data passed into stdin", CreateInstanceCommand) + return EnvironmentV011{}, fmt.Errorf("%s requires data passed into stdin", CreateInstanceCommand) } var data bytes.Buffer if _, err := io.Copy(&data, os.Stdin); err != nil { - return Environment{}, fmt.Errorf("failed to copy bootstrap params") + return EnvironmentV011{}, fmt.Errorf("failed to copy bootstrap params") } if data.Len() == 0 { - return Environment{}, fmt.Errorf("%s requires data passed into stdin", CreateInstanceCommand) + return EnvironmentV011{}, fmt.Errorf("%s requires data passed into stdin", CreateInstanceCommand) } var bootstrapParams params.BootstrapInstance if err := json.Unmarshal(data.Bytes(), &bootstrapParams); err != nil { - return Environment{}, fmt.Errorf("failed to decode instance params: %w", err) + return EnvironmentV011{}, fmt.Errorf("failed to decode instance params: %w", err) } if bootstrapParams.ExtraSpecs == nil { // Initialize ExtraSpecs as an empty JSON object @@ -87,13 +87,13 @@ func GetEnvironment() (Environment, error) { } if err := env.Validate(); err != nil { - return Environment{}, fmt.Errorf("failed to validate execution environment: %w", err) + return EnvironmentV011{}, fmt.Errorf("failed to validate execution environment: %w", err) } return env, nil } -type Environment struct { +type EnvironmentV011 struct { Command ExecutionCommand ControllerID string PoolID string @@ -104,7 +104,7 @@ type Environment struct { BootstrapParams params.BootstrapInstance } -func (e Environment) Validate() error { +func (e EnvironmentV011) Validate() error { if e.Command == "" { return fmt.Errorf("missing GARM_COMMAND") } @@ -155,7 +155,7 @@ func (e Environment) Validate() error { return nil } -func Run(ctx context.Context, provider ExternalProvider, env Environment) (string, error) { +func Run(ctx context.Context, provider ExternalProvider, env EnvironmentV011) (string, error) { var ret string switch env.Command { case CreateInstanceCommand: diff --git a/execution/v0.1.1/execution_test.go b/execution/v0.1.1/execution_test.go index de8eb28..769ea96 100644 --- a/execution/v0.1.1/execution_test.go +++ b/execution/v0.1.1/execution_test.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package execution +package executionv011 import ( "context" @@ -129,12 +129,12 @@ func TestValidateEnvironment(t *testing.T) { tests := []struct { name string - env Environment + env EnvironmentV011 errString string }{ { name: "valid environment", - env: Environment{ + env: EnvironmentV011{ Command: CreateInstanceCommand, ControllerID: "controller-id", PoolID: "pool-id", @@ -149,14 +149,14 @@ func TestValidateEnvironment(t *testing.T) { }, { name: "invalid command", - env: Environment{ + env: EnvironmentV011{ Command: "", }, errString: "missing GARM_COMMAND", }, { name: "invalid provider config file", - env: Environment{ + env: EnvironmentV011{ Command: CreateInstanceCommand, ProviderConfigFile: "", }, @@ -164,7 +164,7 @@ func TestValidateEnvironment(t *testing.T) { }, { name: "error accessing config file", - env: Environment{ + env: EnvironmentV011{ Command: CreateInstanceCommand, ProviderConfigFile: "invalid-file", }, @@ -172,7 +172,7 @@ func TestValidateEnvironment(t *testing.T) { }, { name: "invalid controller ID", - env: Environment{ + env: EnvironmentV011{ Command: CreateInstanceCommand, ProviderConfigFile: tmpfile.Name(), }, @@ -181,7 +181,7 @@ func TestValidateEnvironment(t *testing.T) { { name: "invalid instance ID", - env: Environment{ + env: EnvironmentV011{ Command: DeleteInstanceCommand, ProviderConfigFile: tmpfile.Name(), ControllerID: "controller-id", @@ -191,7 +191,7 @@ func TestValidateEnvironment(t *testing.T) { }, { name: "invalid pool ID", - env: Environment{ + env: EnvironmentV011{ Command: ListInstancesCommand, ProviderConfigFile: tmpfile.Name(), ControllerID: "controller-id", @@ -201,7 +201,7 @@ func TestValidateEnvironment(t *testing.T) { }, { name: "invalid bootstrap params", - env: Environment{ + env: EnvironmentV011{ Command: CreateInstanceCommand, ProviderConfigFile: tmpfile.Name(), ControllerID: "controller-id", @@ -212,7 +212,7 @@ func TestValidateEnvironment(t *testing.T) { }, { name: "missing pool ID", - env: Environment{ + env: EnvironmentV011{ Command: CreateInstanceCommand, ProviderConfigFile: tmpfile.Name(), ControllerID: "controller-id", @@ -225,7 +225,7 @@ func TestValidateEnvironment(t *testing.T) { }, { name: "unknown command", - env: Environment{ + env: EnvironmentV011{ Command: "unknown-command", ProviderConfigFile: tmpfile.Name(), ControllerID: "controller-id", @@ -254,14 +254,14 @@ func TestValidateEnvironment(t *testing.T) { func TestRun(t *testing.T) { tests := []struct { name string - providerEnv Environment + providerEnv EnvironmentV011 providerInstance params.ProviderInstance providerErr error expectedErrMsg string }{ { name: "Valid environment", - providerEnv: Environment{ + providerEnv: EnvironmentV011{ Command: CreateInstanceCommand, }, providerInstance: params.ProviderInstance{ @@ -273,7 +273,7 @@ func TestRun(t *testing.T) { }, { name: "Failed to create instance", - providerEnv: Environment{ + providerEnv: EnvironmentV011{ Command: CreateInstanceCommand, }, providerInstance: params.ProviderInstance{ @@ -285,7 +285,7 @@ func TestRun(t *testing.T) { }, { name: "Failed to get instance", - providerEnv: Environment{ + providerEnv: EnvironmentV011{ Command: GetInstanceCommand, }, providerInstance: params.ProviderInstance{ @@ -297,7 +297,7 @@ func TestRun(t *testing.T) { }, { name: "Failed to list instances", - providerEnv: Environment{ + providerEnv: EnvironmentV011{ Command: ListInstancesCommand, }, providerInstance: params.ProviderInstance{ @@ -309,7 +309,7 @@ func TestRun(t *testing.T) { }, { name: "Failed to delete instance", - providerEnv: Environment{ + providerEnv: EnvironmentV011{ Command: DeleteInstanceCommand, }, providerInstance: params.ProviderInstance{ @@ -321,7 +321,7 @@ func TestRun(t *testing.T) { }, { name: "Failed to remove all instances", - providerEnv: Environment{ + providerEnv: EnvironmentV011{ Command: RemoveAllInstancesCommand, }, providerInstance: params.ProviderInstance{ @@ -333,7 +333,7 @@ func TestRun(t *testing.T) { }, { name: "Failed to start instance", - providerEnv: Environment{ + providerEnv: EnvironmentV011{ Command: StartInstanceCommand, }, providerInstance: params.ProviderInstance{ @@ -345,7 +345,7 @@ func TestRun(t *testing.T) { }, { name: "Failed to stop instance", - providerEnv: Environment{ + providerEnv: EnvironmentV011{ Command: StopInstanceCommand, }, providerInstance: params.ProviderInstance{ @@ -357,7 +357,7 @@ func TestRun(t *testing.T) { }, { name: "Invalid command", - providerEnv: Environment{ + providerEnv: EnvironmentV011{ Command: "invalid-command", }, providerInstance: params.ProviderInstance{ diff --git a/execution/v0.1.1/interface.go b/execution/v0.1.1/interface.go index 24e39e0..d85f28e 100644 --- a/execution/v0.1.1/interface.go +++ b/execution/v0.1.1/interface.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package execution +package executionv011 import ( "context" diff --git a/go.mod b/go.mod index 6be9c44..bc55a04 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( ) require ( + github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/felixge/httpsnoop v1.0.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/go.sum b/go.sum index 6a5b072..3b28739 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= diff --git a/vendor/github.com/Masterminds/semver/v3/.gitignore b/vendor/github.com/Masterminds/semver/v3/.gitignore new file mode 100644 index 0000000..6b061e6 --- /dev/null +++ b/vendor/github.com/Masterminds/semver/v3/.gitignore @@ -0,0 +1 @@ +_fuzz/ \ No newline at end of file diff --git a/vendor/github.com/Masterminds/semver/v3/.golangci.yml b/vendor/github.com/Masterminds/semver/v3/.golangci.yml new file mode 100644 index 0000000..fbc6332 --- /dev/null +++ b/vendor/github.com/Masterminds/semver/v3/.golangci.yml @@ -0,0 +1,27 @@ +run: + deadline: 2m + +linters: + disable-all: true + enable: + - misspell + - govet + - staticcheck + - errcheck + - unparam + - ineffassign + - nakedret + - gocyclo + - dupl + - goimports + - revive + - gosec + - gosimple + - typecheck + - unused + +linters-settings: + gofmt: + simplify: true + dupl: + threshold: 600 diff --git a/vendor/github.com/Masterminds/semver/v3/CHANGELOG.md b/vendor/github.com/Masterminds/semver/v3/CHANGELOG.md new file mode 100644 index 0000000..f126264 --- /dev/null +++ b/vendor/github.com/Masterminds/semver/v3/CHANGELOG.md @@ -0,0 +1,214 @@ +# Changelog + +## 3.2.0 (2022-11-28) + +### Added + +- #190: Added text marshaling and unmarshaling +- #167: Added JSON marshalling for constraints (thanks @SimonTheLeg) +- #173: Implement encoding.TextMarshaler and encoding.TextUnmarshaler on Version (thanks @MarkRosemaker) +- #179: Added New() version constructor (thanks @kazhuravlev) + +### Changed + +- #182/#183: Updated CI testing setup + +### Fixed + +- #186: Fixing issue where validation of constraint section gave false positives +- #176: Fix constraints check with *-0 (thanks @mtt0) +- #181: Fixed Caret operator (^) gives unexpected results when the minor version in constraint is 0 (thanks @arshchimni) +- #161: Fixed godoc (thanks @afirth) + +## 3.1.1 (2020-11-23) + +### Fixed + +- #158: Fixed issue with generated regex operation order that could cause problem + +## 3.1.0 (2020-04-15) + +### Added + +- #131: Add support for serializing/deserializing SQL (thanks @ryancurrah) + +### Changed + +- #148: More accurate validation messages on constraints + +## 3.0.3 (2019-12-13) + +### Fixed + +- #141: Fixed issue with <= comparison + +## 3.0.2 (2019-11-14) + +### Fixed + +- #134: Fixed broken constraint checking with ^0.0 (thanks @krmichelos) + +## 3.0.1 (2019-09-13) + +### Fixed + +- #125: Fixes issue with module path for v3 + +## 3.0.0 (2019-09-12) + +This is a major release of the semver package which includes API changes. The Go +API is compatible with ^1. The Go API was not changed because many people are using +`go get` without Go modules for their applications and API breaking changes cause +errors which we have or would need to support. + +The changes in this release are the handling based on the data passed into the +functions. These are described in the added and changed sections below. + +### Added + +- StrictNewVersion function. This is similar to NewVersion but will return an + error if the version passed in is not a strict semantic version. For example, + 1.2.3 would pass but v1.2.3 or 1.2 would fail because they are not strictly + speaking semantic versions. This function is faster, performs fewer operations, + and uses fewer allocations than NewVersion. +- Fuzzing has been performed on NewVersion, StrictNewVersion, and NewConstraint. + The Makefile contains the operations used. For more information on you can start + on Wikipedia at https://en.wikipedia.org/wiki/Fuzzing +- Now using Go modules + +### Changed + +- NewVersion has proper prerelease and metadata validation with error messages + to signal an issue with either of them +- ^ now operates using a similar set of rules to npm/js and Rust/Cargo. If the + version is >=1 the ^ ranges works the same as v1. For major versions of 0 the + rules have changed. The minor version is treated as the stable version unless + a patch is specified and then it is equivalent to =. One difference from npm/js + is that prereleases there are only to a specific version (e.g. 1.2.3). + Prereleases here look over multiple versions and follow semantic version + ordering rules. This pattern now follows along with the expected and requested + handling of this packaged by numerous users. + +## 1.5.0 (2019-09-11) + +### Added + +- #103: Add basic fuzzing for `NewVersion()` (thanks @jesse-c) + +### Changed + +- #82: Clarify wildcard meaning in range constraints and update tests for it (thanks @greysteil) +- #83: Clarify caret operator range for pre-1.0.0 dependencies (thanks @greysteil) +- #72: Adding docs comment pointing to vert for a cli +- #71: Update the docs on pre-release comparator handling +- #89: Test with new go versions (thanks @thedevsaddam) +- #87: Added $ to ValidPrerelease for better validation (thanks @jeremycarroll) + +### Fixed + +- #78: Fix unchecked error in example code (thanks @ravron) +- #70: Fix the handling of pre-releases and the 0.0.0 release edge case +- #97: Fixed copyright file for proper display on GitHub +- #107: Fix handling prerelease when sorting alphanum and num +- #109: Fixed where Validate sometimes returns wrong message on error + +## 1.4.2 (2018-04-10) + +### Changed + +- #72: Updated the docs to point to vert for a console appliaction +- #71: Update the docs on pre-release comparator handling + +### Fixed + +- #70: Fix the handling of pre-releases and the 0.0.0 release edge case + +## 1.4.1 (2018-04-02) + +### Fixed + +- Fixed #64: Fix pre-release precedence issue (thanks @uudashr) + +## 1.4.0 (2017-10-04) + +### Changed + +- #61: Update NewVersion to parse ints with a 64bit int size (thanks @zknill) + +## 1.3.1 (2017-07-10) + +### Fixed + +- Fixed #57: number comparisons in prerelease sometimes inaccurate + +## 1.3.0 (2017-05-02) + +### Added + +- #45: Added json (un)marshaling support (thanks @mh-cbon) +- Stability marker. See https://masterminds.github.io/stability/ + +### Fixed + +- #51: Fix handling of single digit tilde constraint (thanks @dgodd) + +### Changed + +- #55: The godoc icon moved from png to svg + +## 1.2.3 (2017-04-03) + +### Fixed + +- #46: Fixed 0.x.x and 0.0.x in constraints being treated as * + +## Release 1.2.2 (2016-12-13) + +### Fixed + +- #34: Fixed issue where hyphen range was not working with pre-release parsing. + +## Release 1.2.1 (2016-11-28) + +### Fixed + +- #24: Fixed edge case issue where constraint "> 0" does not handle "0.0.1-alpha" + properly. + +## Release 1.2.0 (2016-11-04) + +### Added + +- #20: Added MustParse function for versions (thanks @adamreese) +- #15: Added increment methods on versions (thanks @mh-cbon) + +### Fixed + +- Issue #21: Per the SemVer spec (section 9) a pre-release is unstable and + might not satisfy the intended compatibility. The change here ignores pre-releases + on constraint checks (e.g., ~ or ^) when a pre-release is not part of the + constraint. For example, `^1.2.3` will ignore pre-releases while + `^1.2.3-alpha` will include them. + +## Release 1.1.1 (2016-06-30) + +### Changed + +- Issue #9: Speed up version comparison performance (thanks @sdboyer) +- Issue #8: Added benchmarks (thanks @sdboyer) +- Updated Go Report Card URL to new location +- Updated Readme to add code snippet formatting (thanks @mh-cbon) +- Updating tagging to v[SemVer] structure for compatibility with other tools. + +## Release 1.1.0 (2016-03-11) + +- Issue #2: Implemented validation to provide reasons a versions failed a + constraint. + +## Release 1.0.1 (2015-12-31) + +- Fixed #1: * constraint failing on valid versions. + +## Release 1.0.0 (2015-10-20) + +- Initial release diff --git a/vendor/github.com/Masterminds/semver/v3/LICENSE.txt b/vendor/github.com/Masterminds/semver/v3/LICENSE.txt new file mode 100644 index 0000000..9ff7da9 --- /dev/null +++ b/vendor/github.com/Masterminds/semver/v3/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (C) 2014-2019, Matt Butcher and Matt Farina + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/Masterminds/semver/v3/Makefile b/vendor/github.com/Masterminds/semver/v3/Makefile new file mode 100644 index 0000000..0e7b5c7 --- /dev/null +++ b/vendor/github.com/Masterminds/semver/v3/Makefile @@ -0,0 +1,30 @@ +GOPATH=$(shell go env GOPATH) +GOLANGCI_LINT=$(GOPATH)/bin/golangci-lint + +.PHONY: lint +lint: $(GOLANGCI_LINT) + @echo "==> Linting codebase" + @$(GOLANGCI_LINT) run + +.PHONY: test +test: + @echo "==> Running tests" + GO111MODULE=on go test -v + +.PHONY: test-cover +test-cover: + @echo "==> Running Tests with coverage" + GO111MODULE=on go test -cover . + +.PHONY: fuzz +fuzz: + @echo "==> Running Fuzz Tests" + go test -fuzz=FuzzNewVersion -fuzztime=15s . + go test -fuzz=FuzzStrictNewVersion -fuzztime=15s . + go test -fuzz=FuzzNewConstraint -fuzztime=15s . + +$(GOLANGCI_LINT): + # Install golangci-lint. The configuration for it is in the .golangci.yml + # file in the root of the repository + echo ${GOPATH} + curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.17.1 diff --git a/vendor/github.com/Masterminds/semver/v3/README.md b/vendor/github.com/Masterminds/semver/v3/README.md new file mode 100644 index 0000000..eab8cac --- /dev/null +++ b/vendor/github.com/Masterminds/semver/v3/README.md @@ -0,0 +1,258 @@ +# SemVer + +The `semver` package provides the ability to work with [Semantic Versions](http://semver.org) in Go. Specifically it provides the ability to: + +* Parse semantic versions +* Sort semantic versions +* Check if a semantic version fits within a set of constraints +* Optionally work with a `v` prefix + +[![Stability: +Active](https://masterminds.github.io/stability/active.svg)](https://masterminds.github.io/stability/active.html) +[![](https://github.com/Masterminds/semver/workflows/Tests/badge.svg)](https://github.com/Masterminds/semver/actions) +[![GoDoc](https://img.shields.io/static/v1?label=godoc&message=reference&color=blue)](https://pkg.go.dev/github.com/Masterminds/semver/v3) +[![Go Report Card](https://goreportcard.com/badge/github.com/Masterminds/semver)](https://goreportcard.com/report/github.com/Masterminds/semver) + +If you are looking for a command line tool for version comparisons please see +[vert](https://github.com/Masterminds/vert) which uses this library. + +## Package Versions + +Note, import `github.com/github.com/Masterminds/semver/v3` to use the latest version. + +There are three major versions fo the `semver` package. + +* 3.x.x is the stable and active version. This version is focused on constraint + compatibility for range handling in other tools from other languages. It has + a similar API to the v1 releases. The development of this version is on the master + branch. The documentation for this version is below. +* 2.x was developed primarily for [dep](https://github.com/golang/dep). There are + no tagged releases and the development was performed by [@sdboyer](https://github.com/sdboyer). + There are API breaking changes from v1. This version lives on the [2.x branch](https://github.com/Masterminds/semver/tree/2.x). +* 1.x.x is the original release. It is no longer maintained. You should use the + v3 release instead. You can read the documentation for the 1.x.x release + [here](https://github.com/Masterminds/semver/blob/release-1/README.md). + +## Parsing Semantic Versions + +There are two functions that can parse semantic versions. The `StrictNewVersion` +function only parses valid version 2 semantic versions as outlined in the +specification. The `NewVersion` function attempts to coerce a version into a +semantic version and parse it. For example, if there is a leading v or a version +listed without all 3 parts (e.g. `v1.2`) it will attempt to coerce it into a valid +semantic version (e.g., 1.2.0). In both cases a `Version` object is returned +that can be sorted, compared, and used in constraints. + +When parsing a version an error is returned if there is an issue parsing the +version. For example, + + v, err := semver.NewVersion("1.2.3-beta.1+build345") + +The version object has methods to get the parts of the version, compare it to +other versions, convert the version back into a string, and get the original +string. Getting the original string is useful if the semantic version was coerced +into a valid form. + +## Sorting Semantic Versions + +A set of versions can be sorted using the `sort` package from the standard library. +For example, + +```go +raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",} +vs := make([]*semver.Version, len(raw)) +for i, r := range raw { + v, err := semver.NewVersion(r) + if err != nil { + t.Errorf("Error parsing version: %s", err) + } + + vs[i] = v +} + +sort.Sort(semver.Collection(vs)) +``` + +## Checking Version Constraints + +There are two methods for comparing versions. One uses comparison methods on +`Version` instances and the other uses `Constraints`. There are some important +differences to notes between these two methods of comparison. + +1. When two versions are compared using functions such as `Compare`, `LessThan`, + and others it will follow the specification and always include prereleases + within the comparison. It will provide an answer that is valid with the + comparison section of the spec at https://semver.org/#spec-item-11 +2. When constraint checking is used for checks or validation it will follow a + different set of rules that are common for ranges with tools like npm/js + and Rust/Cargo. This includes considering prereleases to be invalid if the + ranges does not include one. If you want to have it include pre-releases a + simple solution is to include `-0` in your range. +3. Constraint ranges can have some complex rules including the shorthand use of + ~ and ^. For more details on those see the options below. + +There are differences between the two methods or checking versions because the +comparison methods on `Version` follow the specification while comparison ranges +are not part of the specification. Different packages and tools have taken it +upon themselves to come up with range rules. This has resulted in differences. +For example, npm/js and Cargo/Rust follow similar patterns while PHP has a +different pattern for ^. The comparison features in this package follow the +npm/js and Cargo/Rust lead because applications using it have followed similar +patters with their versions. + +Checking a version against version constraints is one of the most featureful +parts of the package. + +```go +c, err := semver.NewConstraint(">= 1.2.3") +if err != nil { + // Handle constraint not being parsable. +} + +v, err := semver.NewVersion("1.3") +if err != nil { + // Handle version not being parsable. +} +// Check if the version meets the constraints. The a variable will be true. +a := c.Check(v) +``` + +### Basic Comparisons + +There are two elements to the comparisons. First, a comparison string is a list +of space or comma separated AND comparisons. These are then separated by || (OR) +comparisons. For example, `">= 1.2 < 3.0.0 || >= 4.2.3"` is looking for a +comparison that's greater than or equal to 1.2 and less than 3.0.0 or is +greater than or equal to 4.2.3. + +The basic comparisons are: + +* `=`: equal (aliased to no operator) +* `!=`: not equal +* `>`: greater than +* `<`: less than +* `>=`: greater than or equal to +* `<=`: less than or equal to + +### Working With Prerelease Versions + +Pre-releases, for those not familiar with them, are used for software releases +prior to stable or generally available releases. Examples of prereleases include +development, alpha, beta, and release candidate releases. A prerelease may be +a version such as `1.2.3-beta.1` while the stable release would be `1.2.3`. In the +order of precedence, prereleases come before their associated releases. In this +example `1.2.3-beta.1 < 1.2.3`. + +According to the Semantic Version specification prereleases may not be +API compliant with their release counterpart. It says, + +> A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version. + +SemVer comparisons using constraints without a prerelease comparator will skip +prerelease versions. For example, `>=1.2.3` will skip prereleases when looking +at a list of releases while `>=1.2.3-0` will evaluate and find prereleases. + +The reason for the `0` as a pre-release version in the example comparison is +because pre-releases can only contain ASCII alphanumerics and hyphens (along with +`.` separators), per the spec. Sorting happens in ASCII sort order, again per the +spec. The lowest character is a `0` in ASCII sort order +(see an [ASCII Table](http://www.asciitable.com/)) + +Understanding ASCII sort ordering is important because A-Z comes before a-z. That +means `>=1.2.3-BETA` will return `1.2.3-alpha`. What you might expect from case +sensitivity doesn't apply here. This is due to ASCII sort ordering which is what +the spec specifies. + +### Hyphen Range Comparisons + +There are multiple methods to handle ranges and the first is hyphens ranges. +These look like: + +* `1.2 - 1.4.5` which is equivalent to `>= 1.2 <= 1.4.5` +* `2.3.4 - 4.5` which is equivalent to `>= 2.3.4 <= 4.5` + +### Wildcards In Comparisons + +The `x`, `X`, and `*` characters can be used as a wildcard character. This works +for all comparison operators. When used on the `=` operator it falls +back to the patch level comparison (see tilde below). For example, + +* `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` +* `>= 1.2.x` is equivalent to `>= 1.2.0` +* `<= 2.x` is equivalent to `< 3` +* `*` is equivalent to `>= 0.0.0` + +### Tilde Range Comparisons (Patch) + +The tilde (`~`) comparison operator is for patch level ranges when a minor +version is specified and major level changes when the minor number is missing. +For example, + +* `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0` +* `~1` is equivalent to `>= 1, < 2` +* `~2.3` is equivalent to `>= 2.3, < 2.4` +* `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` +* `~1.x` is equivalent to `>= 1, < 2` + +### Caret Range Comparisons (Major) + +The caret (`^`) comparison operator is for major level changes once a stable +(1.0.0) release has occurred. Prior to a 1.0.0 release the minor versions acts +as the API stability level. This is useful when comparisons of API versions as a +major change is API breaking. For example, + +* `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0` +* `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0` +* `^2.3` is equivalent to `>= 2.3, < 3` +* `^2.x` is equivalent to `>= 2.0.0, < 3` +* `^0.2.3` is equivalent to `>=0.2.3 <0.3.0` +* `^0.2` is equivalent to `>=0.2.0 <0.3.0` +* `^0.0.3` is equivalent to `>=0.0.3 <0.0.4` +* `^0.0` is equivalent to `>=0.0.0 <0.1.0` +* `^0` is equivalent to `>=0.0.0 <1.0.0` + +## Validation + +In addition to testing a version against a constraint, a version can be validated +against a constraint. When validation fails a slice of errors containing why a +version didn't meet the constraint is returned. For example, + +```go +c, err := semver.NewConstraint("<= 1.2.3, >= 1.4") +if err != nil { + // Handle constraint not being parseable. +} + +v, err := semver.NewVersion("1.3") +if err != nil { + // Handle version not being parseable. +} + +// Validate a version against a constraint. +a, msgs := c.Validate(v) +// a is false +for _, m := range msgs { + fmt.Println(m) + + // Loops over the errors which would read + // "1.3 is greater than 1.2.3" + // "1.3 is less than 1.4" +} +``` + +## Contribute + +If you find an issue or want to contribute please file an [issue](https://github.com/Masterminds/semver/issues) +or [create a pull request](https://github.com/Masterminds/semver/pulls). + +## Security + +Security is an important consideration for this project. The project currently +uses the following tools to help discover security issues: + +* [CodeQL](https://github.com/Masterminds/semver) +* [gosec](https://github.com/securego/gosec) +* Daily Fuzz testing + +If you believe you have found a security vulnerability you can privately disclose +it through the [GitHub security page](https://github.com/Masterminds/semver/security). diff --git a/vendor/github.com/Masterminds/semver/v3/SECURITY.md b/vendor/github.com/Masterminds/semver/v3/SECURITY.md new file mode 100644 index 0000000..a30a66b --- /dev/null +++ b/vendor/github.com/Masterminds/semver/v3/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +## Supported Versions + +The following versions of semver are currently supported: + +| Version | Supported | +| ------- | ------------------ | +| 3.x | :white_check_mark: | +| 2.x | :x: | +| 1.x | :x: | + +Fixes are only released for the latest minor version in the form of a patch release. + +## Reporting a Vulnerability + +You can privately disclose a vulnerability through GitHubs +[private vulnerability reporting](https://github.com/Masterminds/semver/security/advisories) +mechanism. diff --git a/vendor/github.com/Masterminds/semver/v3/collection.go b/vendor/github.com/Masterminds/semver/v3/collection.go new file mode 100644 index 0000000..a782358 --- /dev/null +++ b/vendor/github.com/Masterminds/semver/v3/collection.go @@ -0,0 +1,24 @@ +package semver + +// Collection is a collection of Version instances and implements the sort +// interface. See the sort package for more details. +// https://golang.org/pkg/sort/ +type Collection []*Version + +// Len returns the length of a collection. The number of Version instances +// on the slice. +func (c Collection) Len() int { + return len(c) +} + +// Less is needed for the sort interface to compare two Version objects on the +// slice. If checks if one is less than the other. +func (c Collection) Less(i, j int) bool { + return c[i].LessThan(c[j]) +} + +// Swap is needed for the sort interface to replace the Version objects +// at two different positions in the slice. +func (c Collection) Swap(i, j int) { + c[i], c[j] = c[j], c[i] +} diff --git a/vendor/github.com/Masterminds/semver/v3/constraints.go b/vendor/github.com/Masterminds/semver/v3/constraints.go new file mode 100644 index 0000000..8461c7e --- /dev/null +++ b/vendor/github.com/Masterminds/semver/v3/constraints.go @@ -0,0 +1,594 @@ +package semver + +import ( + "bytes" + "errors" + "fmt" + "regexp" + "strings" +) + +// Constraints is one or more constraint that a semantic version can be +// checked against. +type Constraints struct { + constraints [][]*constraint +} + +// NewConstraint returns a Constraints instance that a Version instance can +// be checked against. If there is a parse error it will be returned. +func NewConstraint(c string) (*Constraints, error) { + + // Rewrite - ranges into a comparison operation. + c = rewriteRange(c) + + ors := strings.Split(c, "||") + or := make([][]*constraint, len(ors)) + for k, v := range ors { + + // TODO: Find a way to validate and fetch all the constraints in a simpler form + + // Validate the segment + if !validConstraintRegex.MatchString(v) { + return nil, fmt.Errorf("improper constraint: %s", v) + } + + cs := findConstraintRegex.FindAllString(v, -1) + if cs == nil { + cs = append(cs, v) + } + result := make([]*constraint, len(cs)) + for i, s := range cs { + pc, err := parseConstraint(s) + if err != nil { + return nil, err + } + + result[i] = pc + } + or[k] = result + } + + o := &Constraints{constraints: or} + return o, nil +} + +// Check tests if a version satisfies the constraints. +func (cs Constraints) Check(v *Version) bool { + // TODO(mattfarina): For v4 of this library consolidate the Check and Validate + // functions as the underlying functions make that possible now. + // loop over the ORs and check the inner ANDs + for _, o := range cs.constraints { + joy := true + for _, c := range o { + if check, _ := c.check(v); !check { + joy = false + break + } + } + + if joy { + return true + } + } + + return false +} + +// Validate checks if a version satisfies a constraint. If not a slice of +// reasons for the failure are returned in addition to a bool. +func (cs Constraints) Validate(v *Version) (bool, []error) { + // loop over the ORs and check the inner ANDs + var e []error + + // Capture the prerelease message only once. When it happens the first time + // this var is marked + var prerelesase bool + for _, o := range cs.constraints { + joy := true + for _, c := range o { + // Before running the check handle the case there the version is + // a prerelease and the check is not searching for prereleases. + if c.con.pre == "" && v.pre != "" { + if !prerelesase { + em := fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) + e = append(e, em) + prerelesase = true + } + joy = false + + } else { + + if _, err := c.check(v); err != nil { + e = append(e, err) + joy = false + } + } + } + + if joy { + return true, []error{} + } + } + + return false, e +} + +func (cs Constraints) String() string { + buf := make([]string, len(cs.constraints)) + var tmp bytes.Buffer + + for k, v := range cs.constraints { + tmp.Reset() + vlen := len(v) + for kk, c := range v { + tmp.WriteString(c.string()) + + // Space separate the AND conditions + if vlen > 1 && kk < vlen-1 { + tmp.WriteString(" ") + } + } + buf[k] = tmp.String() + } + + return strings.Join(buf, " || ") +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (cs *Constraints) UnmarshalText(text []byte) error { + temp, err := NewConstraint(string(text)) + if err != nil { + return err + } + + *cs = *temp + + return nil +} + +// MarshalText implements the encoding.TextMarshaler interface. +func (cs Constraints) MarshalText() ([]byte, error) { + return []byte(cs.String()), nil +} + +var constraintOps map[string]cfunc +var constraintRegex *regexp.Regexp +var constraintRangeRegex *regexp.Regexp + +// Used to find individual constraints within a multi-constraint string +var findConstraintRegex *regexp.Regexp + +// Used to validate an segment of ANDs is valid +var validConstraintRegex *regexp.Regexp + +const cvRegex string = `v?([0-9|x|X|\*]+)(\.[0-9|x|X|\*]+)?(\.[0-9|x|X|\*]+)?` + + `(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` + + `(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` + +func init() { + constraintOps = map[string]cfunc{ + "": constraintTildeOrEqual, + "=": constraintTildeOrEqual, + "!=": constraintNotEqual, + ">": constraintGreaterThan, + "<": constraintLessThan, + ">=": constraintGreaterThanEqual, + "=>": constraintGreaterThanEqual, + "<=": constraintLessThanEqual, + "=<": constraintLessThanEqual, + "~": constraintTilde, + "~>": constraintTilde, + "^": constraintCaret, + } + + ops := `=||!=|>|<|>=|=>|<=|=<|~|~>|\^` + + constraintRegex = regexp.MustCompile(fmt.Sprintf( + `^\s*(%s)\s*(%s)\s*$`, + ops, + cvRegex)) + + constraintRangeRegex = regexp.MustCompile(fmt.Sprintf( + `\s*(%s)\s+-\s+(%s)\s*`, + cvRegex, cvRegex)) + + findConstraintRegex = regexp.MustCompile(fmt.Sprintf( + `(%s)\s*(%s)`, + ops, + cvRegex)) + + // The first time a constraint shows up will look slightly different from + // future times it shows up due to a leading space or comma in a given + // string. + validConstraintRegex = regexp.MustCompile(fmt.Sprintf( + `^(\s*(%s)\s*(%s)\s*)((?:\s+|,\s*)(%s)\s*(%s)\s*)*$`, + ops, + cvRegex, + ops, + cvRegex)) +} + +// An individual constraint +type constraint struct { + // The version used in the constraint check. For example, if a constraint + // is '<= 2.0.0' the con a version instance representing 2.0.0. + con *Version + + // The original parsed version (e.g., 4.x from != 4.x) + orig string + + // The original operator for the constraint + origfunc string + + // When an x is used as part of the version (e.g., 1.x) + minorDirty bool + dirty bool + patchDirty bool +} + +// Check if a version meets the constraint +func (c *constraint) check(v *Version) (bool, error) { + return constraintOps[c.origfunc](v, c) +} + +// String prints an individual constraint into a string +func (c *constraint) string() string { + return c.origfunc + c.orig +} + +type cfunc func(v *Version, c *constraint) (bool, error) + +func parseConstraint(c string) (*constraint, error) { + if len(c) > 0 { + m := constraintRegex.FindStringSubmatch(c) + if m == nil { + return nil, fmt.Errorf("improper constraint: %s", c) + } + + cs := &constraint{ + orig: m[2], + origfunc: m[1], + } + + ver := m[2] + minorDirty := false + patchDirty := false + dirty := false + if isX(m[3]) || m[3] == "" { + ver = fmt.Sprintf("0.0.0%s", m[6]) + dirty = true + } else if isX(strings.TrimPrefix(m[4], ".")) || m[4] == "" { + minorDirty = true + dirty = true + ver = fmt.Sprintf("%s.0.0%s", m[3], m[6]) + } else if isX(strings.TrimPrefix(m[5], ".")) || m[5] == "" { + dirty = true + patchDirty = true + ver = fmt.Sprintf("%s%s.0%s", m[3], m[4], m[6]) + } + + con, err := NewVersion(ver) + if err != nil { + + // The constraintRegex should catch any regex parsing errors. So, + // we should never get here. + return nil, errors.New("constraint Parser Error") + } + + cs.con = con + cs.minorDirty = minorDirty + cs.patchDirty = patchDirty + cs.dirty = dirty + + return cs, nil + } + + // The rest is the special case where an empty string was passed in which + // is equivalent to * or >=0.0.0 + con, err := StrictNewVersion("0.0.0") + if err != nil { + + // The constraintRegex should catch any regex parsing errors. So, + // we should never get here. + return nil, errors.New("constraint Parser Error") + } + + cs := &constraint{ + con: con, + orig: c, + origfunc: "", + minorDirty: false, + patchDirty: false, + dirty: true, + } + return cs, nil +} + +// Constraint functions +func constraintNotEqual(v *Version, c *constraint) (bool, error) { + if c.dirty { + + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) + } + + if c.con.Major() != v.Major() { + return true, nil + } + if c.con.Minor() != v.Minor() && !c.minorDirty { + return true, nil + } else if c.minorDirty { + return false, fmt.Errorf("%s is equal to %s", v, c.orig) + } else if c.con.Patch() != v.Patch() && !c.patchDirty { + return true, nil + } else if c.patchDirty { + // Need to handle prereleases if present + if v.Prerelease() != "" || c.con.Prerelease() != "" { + eq := comparePrerelease(v.Prerelease(), c.con.Prerelease()) != 0 + if eq { + return true, nil + } + return false, fmt.Errorf("%s is equal to %s", v, c.orig) + } + return false, fmt.Errorf("%s is equal to %s", v, c.orig) + } + } + + eq := v.Equal(c.con) + if eq { + return false, fmt.Errorf("%s is equal to %s", v, c.orig) + } + + return true, nil +} + +func constraintGreaterThan(v *Version, c *constraint) (bool, error) { + + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) + } + + var eq bool + + if !c.dirty { + eq = v.Compare(c.con) == 1 + if eq { + return true, nil + } + return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig) + } + + if v.Major() > c.con.Major() { + return true, nil + } else if v.Major() < c.con.Major() { + return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig) + } else if c.minorDirty { + // This is a range case such as >11. When the version is something like + // 11.1.0 is it not > 11. For that we would need 12 or higher + return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig) + } else if c.patchDirty { + // This is for ranges such as >11.1. A version of 11.1.1 is not greater + // which one of 11.2.1 is greater + eq = v.Minor() > c.con.Minor() + if eq { + return true, nil + } + return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig) + } + + // If we have gotten here we are not comparing pre-preleases and can use the + // Compare function to accomplish that. + eq = v.Compare(c.con) == 1 + if eq { + return true, nil + } + return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig) +} + +func constraintLessThan(v *Version, c *constraint) (bool, error) { + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) + } + + eq := v.Compare(c.con) < 0 + if eq { + return true, nil + } + return false, fmt.Errorf("%s is greater than or equal to %s", v, c.orig) +} + +func constraintGreaterThanEqual(v *Version, c *constraint) (bool, error) { + + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) + } + + eq := v.Compare(c.con) >= 0 + if eq { + return true, nil + } + return false, fmt.Errorf("%s is less than %s", v, c.orig) +} + +func constraintLessThanEqual(v *Version, c *constraint) (bool, error) { + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) + } + + var eq bool + + if !c.dirty { + eq = v.Compare(c.con) <= 0 + if eq { + return true, nil + } + return false, fmt.Errorf("%s is greater than %s", v, c.orig) + } + + if v.Major() > c.con.Major() { + return false, fmt.Errorf("%s is greater than %s", v, c.orig) + } else if v.Major() == c.con.Major() && v.Minor() > c.con.Minor() && !c.minorDirty { + return false, fmt.Errorf("%s is greater than %s", v, c.orig) + } + + return true, nil +} + +// ~*, ~>* --> >= 0.0.0 (any) +// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0, <3.0.0 +// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0, <2.1.0 +// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0, <1.3.0 +// ~1.2.3, ~>1.2.3 --> >=1.2.3, <1.3.0 +// ~1.2.0, ~>1.2.0 --> >=1.2.0, <1.3.0 +func constraintTilde(v *Version, c *constraint) (bool, error) { + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) + } + + if v.LessThan(c.con) { + return false, fmt.Errorf("%s is less than %s", v, c.orig) + } + + // ~0.0.0 is a special case where all constraints are accepted. It's + // equivalent to >= 0.0.0. + if c.con.Major() == 0 && c.con.Minor() == 0 && c.con.Patch() == 0 && + !c.minorDirty && !c.patchDirty { + return true, nil + } + + if v.Major() != c.con.Major() { + return false, fmt.Errorf("%s does not have same major version as %s", v, c.orig) + } + + if v.Minor() != c.con.Minor() && !c.minorDirty { + return false, fmt.Errorf("%s does not have same major and minor version as %s", v, c.orig) + } + + return true, nil +} + +// When there is a .x (dirty) status it automatically opts in to ~. Otherwise +// it's a straight = +func constraintTildeOrEqual(v *Version, c *constraint) (bool, error) { + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) + } + + if c.dirty { + return constraintTilde(v, c) + } + + eq := v.Equal(c.con) + if eq { + return true, nil + } + + return false, fmt.Errorf("%s is not equal to %s", v, c.orig) +} + +// ^* --> (any) +// ^1.2.3 --> >=1.2.3 <2.0.0 +// ^1.2 --> >=1.2.0 <2.0.0 +// ^1 --> >=1.0.0 <2.0.0 +// ^0.2.3 --> >=0.2.3 <0.3.0 +// ^0.2 --> >=0.2.0 <0.3.0 +// ^0.0.3 --> >=0.0.3 <0.0.4 +// ^0.0 --> >=0.0.0 <0.1.0 +// ^0 --> >=0.0.0 <1.0.0 +func constraintCaret(v *Version, c *constraint) (bool, error) { + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) + } + + // This less than handles prereleases + if v.LessThan(c.con) { + return false, fmt.Errorf("%s is less than %s", v, c.orig) + } + + var eq bool + + // ^ when the major > 0 is >=x.y.z < x+1 + if c.con.Major() > 0 || c.minorDirty { + + // ^ has to be within a major range for > 0. Everything less than was + // filtered out with the LessThan call above. This filters out those + // that greater but not within the same major range. + eq = v.Major() == c.con.Major() + if eq { + return true, nil + } + return false, fmt.Errorf("%s does not have same major version as %s", v, c.orig) + } + + // ^ when the major is 0 and minor > 0 is >=0.y.z < 0.y+1 + if c.con.Major() == 0 && v.Major() > 0 { + return false, fmt.Errorf("%s does not have same major version as %s", v, c.orig) + } + // If the con Minor is > 0 it is not dirty + if c.con.Minor() > 0 || c.patchDirty { + eq = v.Minor() == c.con.Minor() + if eq { + return true, nil + } + return false, fmt.Errorf("%s does not have same minor version as %s. Expected minor versions to match when constraint major version is 0", v, c.orig) + } + // ^ when the minor is 0 and minor > 0 is =0.0.z + if c.con.Minor() == 0 && v.Minor() > 0 { + return false, fmt.Errorf("%s does not have same minor version as %s", v, c.orig) + } + + // At this point the major is 0 and the minor is 0 and not dirty. The patch + // is not dirty so we need to check if they are equal. If they are not equal + eq = c.con.Patch() == v.Patch() + if eq { + return true, nil + } + return false, fmt.Errorf("%s does not equal %s. Expect version and constraint to equal when major and minor versions are 0", v, c.orig) +} + +func isX(x string) bool { + switch x { + case "x", "*", "X": + return true + default: + return false + } +} + +func rewriteRange(i string) string { + m := constraintRangeRegex.FindAllStringSubmatch(i, -1) + if m == nil { + return i + } + o := i + for _, v := range m { + t := fmt.Sprintf(">= %s, <= %s ", v[1], v[11]) + o = strings.Replace(o, v[0], t, 1) + } + + return o +} diff --git a/vendor/github.com/Masterminds/semver/v3/doc.go b/vendor/github.com/Masterminds/semver/v3/doc.go new file mode 100644 index 0000000..74f97ca --- /dev/null +++ b/vendor/github.com/Masterminds/semver/v3/doc.go @@ -0,0 +1,184 @@ +/* +Package semver provides the ability to work with Semantic Versions (http://semver.org) in Go. + +Specifically it provides the ability to: + + - Parse semantic versions + - Sort semantic versions + - Check if a semantic version fits within a set of constraints + - Optionally work with a `v` prefix + +# Parsing Semantic Versions + +There are two functions that can parse semantic versions. The `StrictNewVersion` +function only parses valid version 2 semantic versions as outlined in the +specification. The `NewVersion` function attempts to coerce a version into a +semantic version and parse it. For example, if there is a leading v or a version +listed without all 3 parts (e.g. 1.2) it will attempt to coerce it into a valid +semantic version (e.g., 1.2.0). In both cases a `Version` object is returned +that can be sorted, compared, and used in constraints. + +When parsing a version an optional error can be returned if there is an issue +parsing the version. For example, + + v, err := semver.NewVersion("1.2.3-beta.1+b345") + +The version object has methods to get the parts of the version, compare it to +other versions, convert the version back into a string, and get the original +string. For more details please see the documentation +at https://godoc.org/github.com/Masterminds/semver. + +# Sorting Semantic Versions + +A set of versions can be sorted using the `sort` package from the standard library. +For example, + + raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",} + vs := make([]*semver.Version, len(raw)) + for i, r := range raw { + v, err := semver.NewVersion(r) + if err != nil { + t.Errorf("Error parsing version: %s", err) + } + + vs[i] = v + } + + sort.Sort(semver.Collection(vs)) + +# Checking Version Constraints and Comparing Versions + +There are two methods for comparing versions. One uses comparison methods on +`Version` instances and the other is using Constraints. There are some important +differences to notes between these two methods of comparison. + + 1. When two versions are compared using functions such as `Compare`, `LessThan`, + and others it will follow the specification and always include prereleases + within the comparison. It will provide an answer valid with the comparison + spec section at https://semver.org/#spec-item-11 + 2. When constraint checking is used for checks or validation it will follow a + different set of rules that are common for ranges with tools like npm/js + and Rust/Cargo. This includes considering prereleases to be invalid if the + ranges does not include on. If you want to have it include pre-releases a + simple solution is to include `-0` in your range. + 3. Constraint ranges can have some complex rules including the shorthard use of + ~ and ^. For more details on those see the options below. + +There are differences between the two methods or checking versions because the +comparison methods on `Version` follow the specification while comparison ranges +are not part of the specification. Different packages and tools have taken it +upon themselves to come up with range rules. This has resulted in differences. +For example, npm/js and Cargo/Rust follow similar patterns which PHP has a +different pattern for ^. The comparison features in this package follow the +npm/js and Cargo/Rust lead because applications using it have followed similar +patters with their versions. + +Checking a version against version constraints is one of the most featureful +parts of the package. + + c, err := semver.NewConstraint(">= 1.2.3") + if err != nil { + // Handle constraint not being parsable. + } + + v, err := semver.NewVersion("1.3") + if err != nil { + // Handle version not being parsable. + } + // Check if the version meets the constraints. The a variable will be true. + a := c.Check(v) + +# Basic Comparisons + +There are two elements to the comparisons. First, a comparison string is a list +of comma or space separated AND comparisons. These are then separated by || (OR) +comparisons. For example, `">= 1.2 < 3.0.0 || >= 4.2.3"` is looking for a +comparison that's greater than or equal to 1.2 and less than 3.0.0 or is +greater than or equal to 4.2.3. This can also be written as +`">= 1.2, < 3.0.0 || >= 4.2.3"` + +The basic comparisons are: + + - `=`: equal (aliased to no operator) + - `!=`: not equal + - `>`: greater than + - `<`: less than + - `>=`: greater than or equal to + - `<=`: less than or equal to + +# Hyphen Range Comparisons + +There are multiple methods to handle ranges and the first is hyphens ranges. +These look like: + + - `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5` + - `2.3.4 - 4.5` which is equivalent to `>= 2.3.4 <= 4.5` + +# Wildcards In Comparisons + +The `x`, `X`, and `*` characters can be used as a wildcard character. This works +for all comparison operators. When used on the `=` operator it falls +back to the tilde operation. For example, + + - `1.2.x` is equivalent to `>= 1.2.0 < 1.3.0` + - `>= 1.2.x` is equivalent to `>= 1.2.0` + - `<= 2.x` is equivalent to `<= 3` + - `*` is equivalent to `>= 0.0.0` + +Tilde Range Comparisons (Patch) + +The tilde (`~`) comparison operator is for patch level ranges when a minor +version is specified and major level changes when the minor number is missing. +For example, + + - `~1.2.3` is equivalent to `>= 1.2.3 < 1.3.0` + - `~1` is equivalent to `>= 1, < 2` + - `~2.3` is equivalent to `>= 2.3 < 2.4` + - `~1.2.x` is equivalent to `>= 1.2.0 < 1.3.0` + - `~1.x` is equivalent to `>= 1 < 2` + +Caret Range Comparisons (Major) + +The caret (`^`) comparison operator is for major level changes once a stable +(1.0.0) release has occurred. Prior to a 1.0.0 release the minor versions acts +as the API stability level. This is useful when comparisons of API versions as a +major change is API breaking. For example, + + - `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0` + - `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0` + - `^2.3` is equivalent to `>= 2.3, < 3` + - `^2.x` is equivalent to `>= 2.0.0, < 3` + - `^0.2.3` is equivalent to `>=0.2.3 <0.3.0` + - `^0.2` is equivalent to `>=0.2.0 <0.3.0` + - `^0.0.3` is equivalent to `>=0.0.3 <0.0.4` + - `^0.0` is equivalent to `>=0.0.0 <0.1.0` + - `^0` is equivalent to `>=0.0.0 <1.0.0` + +# Validation + +In addition to testing a version against a constraint, a version can be validated +against a constraint. When validation fails a slice of errors containing why a +version didn't meet the constraint is returned. For example, + + c, err := semver.NewConstraint("<= 1.2.3, >= 1.4") + if err != nil { + // Handle constraint not being parseable. + } + + v, _ := semver.NewVersion("1.3") + if err != nil { + // Handle version not being parseable. + } + + // Validate a version against a constraint. + a, msgs := c.Validate(v) + // a is false + for _, m := range msgs { + fmt.Println(m) + + // Loops over the errors which would read + // "1.3 is greater than 1.2.3" + // "1.3 is less than 1.4" + } +*/ +package semver diff --git a/vendor/github.com/Masterminds/semver/v3/version.go b/vendor/github.com/Masterminds/semver/v3/version.go new file mode 100644 index 0000000..7c4bed3 --- /dev/null +++ b/vendor/github.com/Masterminds/semver/v3/version.go @@ -0,0 +1,639 @@ +package semver + +import ( + "bytes" + "database/sql/driver" + "encoding/json" + "errors" + "fmt" + "regexp" + "strconv" + "strings" +) + +// The compiled version of the regex created at init() is cached here so it +// only needs to be created once. +var versionRegex *regexp.Regexp + +var ( + // ErrInvalidSemVer is returned a version is found to be invalid when + // being parsed. + ErrInvalidSemVer = errors.New("Invalid Semantic Version") + + // ErrEmptyString is returned when an empty string is passed in for parsing. + ErrEmptyString = errors.New("Version string empty") + + // ErrInvalidCharacters is returned when invalid characters are found as + // part of a version + ErrInvalidCharacters = errors.New("Invalid characters in version") + + // ErrSegmentStartsZero is returned when a version segment starts with 0. + // This is invalid in SemVer. + ErrSegmentStartsZero = errors.New("Version segment starts with 0") + + // ErrInvalidMetadata is returned when the metadata is an invalid format + ErrInvalidMetadata = errors.New("Invalid Metadata string") + + // ErrInvalidPrerelease is returned when the pre-release is an invalid format + ErrInvalidPrerelease = errors.New("Invalid Prerelease string") +) + +// semVerRegex is the regular expression used to parse a semantic version. +const semVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` + + `(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` + + `(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` + +// Version represents a single semantic version. +type Version struct { + major, minor, patch uint64 + pre string + metadata string + original string +} + +func init() { + versionRegex = regexp.MustCompile("^" + semVerRegex + "$") +} + +const ( + num string = "0123456789" + allowed string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-" + num +) + +// StrictNewVersion parses a given version and returns an instance of Version or +// an error if unable to parse the version. Only parses valid semantic versions. +// Performs checking that can find errors within the version. +// If you want to coerce a version such as 1 or 1.2 and parse it as the 1.x +// releases of semver did, use the NewVersion() function. +func StrictNewVersion(v string) (*Version, error) { + // Parsing here does not use RegEx in order to increase performance and reduce + // allocations. + + if len(v) == 0 { + return nil, ErrEmptyString + } + + // Split the parts into [0]major, [1]minor, and [2]patch,prerelease,build + parts := strings.SplitN(v, ".", 3) + if len(parts) != 3 { + return nil, ErrInvalidSemVer + } + + sv := &Version{ + original: v, + } + + // check for prerelease or build metadata + var extra []string + if strings.ContainsAny(parts[2], "-+") { + // Start with the build metadata first as it needs to be on the right + extra = strings.SplitN(parts[2], "+", 2) + if len(extra) > 1 { + // build metadata found + sv.metadata = extra[1] + parts[2] = extra[0] + } + + extra = strings.SplitN(parts[2], "-", 2) + if len(extra) > 1 { + // prerelease found + sv.pre = extra[1] + parts[2] = extra[0] + } + } + + // Validate the number segments are valid. This includes only having positive + // numbers and no leading 0's. + for _, p := range parts { + if !containsOnly(p, num) { + return nil, ErrInvalidCharacters + } + + if len(p) > 1 && p[0] == '0' { + return nil, ErrSegmentStartsZero + } + } + + // Extract the major, minor, and patch elements onto the returned Version + var err error + sv.major, err = strconv.ParseUint(parts[0], 10, 64) + if err != nil { + return nil, err + } + + sv.minor, err = strconv.ParseUint(parts[1], 10, 64) + if err != nil { + return nil, err + } + + sv.patch, err = strconv.ParseUint(parts[2], 10, 64) + if err != nil { + return nil, err + } + + // No prerelease or build metadata found so returning now as a fastpath. + if sv.pre == "" && sv.metadata == "" { + return sv, nil + } + + if sv.pre != "" { + if err = validatePrerelease(sv.pre); err != nil { + return nil, err + } + } + + if sv.metadata != "" { + if err = validateMetadata(sv.metadata); err != nil { + return nil, err + } + } + + return sv, nil +} + +// NewVersion parses a given version and returns an instance of Version or +// an error if unable to parse the version. If the version is SemVer-ish it +// attempts to convert it to SemVer. If you want to validate it was a strict +// semantic version at parse time see StrictNewVersion(). +func NewVersion(v string) (*Version, error) { + m := versionRegex.FindStringSubmatch(v) + if m == nil { + return nil, ErrInvalidSemVer + } + + sv := &Version{ + metadata: m[8], + pre: m[5], + original: v, + } + + var err error + sv.major, err = strconv.ParseUint(m[1], 10, 64) + if err != nil { + return nil, fmt.Errorf("Error parsing version segment: %s", err) + } + + if m[2] != "" { + sv.minor, err = strconv.ParseUint(strings.TrimPrefix(m[2], "."), 10, 64) + if err != nil { + return nil, fmt.Errorf("Error parsing version segment: %s", err) + } + } else { + sv.minor = 0 + } + + if m[3] != "" { + sv.patch, err = strconv.ParseUint(strings.TrimPrefix(m[3], "."), 10, 64) + if err != nil { + return nil, fmt.Errorf("Error parsing version segment: %s", err) + } + } else { + sv.patch = 0 + } + + // Perform some basic due diligence on the extra parts to ensure they are + // valid. + + if sv.pre != "" { + if err = validatePrerelease(sv.pre); err != nil { + return nil, err + } + } + + if sv.metadata != "" { + if err = validateMetadata(sv.metadata); err != nil { + return nil, err + } + } + + return sv, nil +} + +// New creates a new instance of Version with each of the parts passed in as +// arguments instead of parsing a version string. +func New(major, minor, patch uint64, pre, metadata string) *Version { + v := Version{ + major: major, + minor: minor, + patch: patch, + pre: pre, + metadata: metadata, + original: "", + } + + v.original = v.String() + + return &v +} + +// MustParse parses a given version and panics on error. +func MustParse(v string) *Version { + sv, err := NewVersion(v) + if err != nil { + panic(err) + } + return sv +} + +// String converts a Version object to a string. +// Note, if the original version contained a leading v this version will not. +// See the Original() method to retrieve the original value. Semantic Versions +// don't contain a leading v per the spec. Instead it's optional on +// implementation. +func (v Version) String() string { + var buf bytes.Buffer + + fmt.Fprintf(&buf, "%d.%d.%d", v.major, v.minor, v.patch) + if v.pre != "" { + fmt.Fprintf(&buf, "-%s", v.pre) + } + if v.metadata != "" { + fmt.Fprintf(&buf, "+%s", v.metadata) + } + + return buf.String() +} + +// Original returns the original value passed in to be parsed. +func (v *Version) Original() string { + return v.original +} + +// Major returns the major version. +func (v Version) Major() uint64 { + return v.major +} + +// Minor returns the minor version. +func (v Version) Minor() uint64 { + return v.minor +} + +// Patch returns the patch version. +func (v Version) Patch() uint64 { + return v.patch +} + +// Prerelease returns the pre-release version. +func (v Version) Prerelease() string { + return v.pre +} + +// Metadata returns the metadata on the version. +func (v Version) Metadata() string { + return v.metadata +} + +// originalVPrefix returns the original 'v' prefix if any. +func (v Version) originalVPrefix() string { + // Note, only lowercase v is supported as a prefix by the parser. + if v.original != "" && v.original[:1] == "v" { + return v.original[:1] + } + return "" +} + +// IncPatch produces the next patch version. +// If the current version does not have prerelease/metadata information, +// it unsets metadata and prerelease values, increments patch number. +// If the current version has any of prerelease or metadata information, +// it unsets both values and keeps current patch value +func (v Version) IncPatch() Version { + vNext := v + // according to http://semver.org/#spec-item-9 + // Pre-release versions have a lower precedence than the associated normal version. + // according to http://semver.org/#spec-item-10 + // Build metadata SHOULD be ignored when determining version precedence. + if v.pre != "" { + vNext.metadata = "" + vNext.pre = "" + } else { + vNext.metadata = "" + vNext.pre = "" + vNext.patch = v.patch + 1 + } + vNext.original = v.originalVPrefix() + "" + vNext.String() + return vNext +} + +// IncMinor produces the next minor version. +// Sets patch to 0. +// Increments minor number. +// Unsets metadata. +// Unsets prerelease status. +func (v Version) IncMinor() Version { + vNext := v + vNext.metadata = "" + vNext.pre = "" + vNext.patch = 0 + vNext.minor = v.minor + 1 + vNext.original = v.originalVPrefix() + "" + vNext.String() + return vNext +} + +// IncMajor produces the next major version. +// Sets patch to 0. +// Sets minor to 0. +// Increments major number. +// Unsets metadata. +// Unsets prerelease status. +func (v Version) IncMajor() Version { + vNext := v + vNext.metadata = "" + vNext.pre = "" + vNext.patch = 0 + vNext.minor = 0 + vNext.major = v.major + 1 + vNext.original = v.originalVPrefix() + "" + vNext.String() + return vNext +} + +// SetPrerelease defines the prerelease value. +// Value must not include the required 'hyphen' prefix. +func (v Version) SetPrerelease(prerelease string) (Version, error) { + vNext := v + if len(prerelease) > 0 { + if err := validatePrerelease(prerelease); err != nil { + return vNext, err + } + } + vNext.pre = prerelease + vNext.original = v.originalVPrefix() + "" + vNext.String() + return vNext, nil +} + +// SetMetadata defines metadata value. +// Value must not include the required 'plus' prefix. +func (v Version) SetMetadata(metadata string) (Version, error) { + vNext := v + if len(metadata) > 0 { + if err := validateMetadata(metadata); err != nil { + return vNext, err + } + } + vNext.metadata = metadata + vNext.original = v.originalVPrefix() + "" + vNext.String() + return vNext, nil +} + +// LessThan tests if one version is less than another one. +func (v *Version) LessThan(o *Version) bool { + return v.Compare(o) < 0 +} + +// GreaterThan tests if one version is greater than another one. +func (v *Version) GreaterThan(o *Version) bool { + return v.Compare(o) > 0 +} + +// Equal tests if two versions are equal to each other. +// Note, versions can be equal with different metadata since metadata +// is not considered part of the comparable version. +func (v *Version) Equal(o *Version) bool { + return v.Compare(o) == 0 +} + +// Compare compares this version to another one. It returns -1, 0, or 1 if +// the version smaller, equal, or larger than the other version. +// +// Versions are compared by X.Y.Z. Build metadata is ignored. Prerelease is +// lower than the version without a prerelease. Compare always takes into account +// prereleases. If you want to work with ranges using typical range syntaxes that +// skip prereleases if the range is not looking for them use constraints. +func (v *Version) Compare(o *Version) int { + // Compare the major, minor, and patch version for differences. If a + // difference is found return the comparison. + if d := compareSegment(v.Major(), o.Major()); d != 0 { + return d + } + if d := compareSegment(v.Minor(), o.Minor()); d != 0 { + return d + } + if d := compareSegment(v.Patch(), o.Patch()); d != 0 { + return d + } + + // At this point the major, minor, and patch versions are the same. + ps := v.pre + po := o.Prerelease() + + if ps == "" && po == "" { + return 0 + } + if ps == "" { + return 1 + } + if po == "" { + return -1 + } + + return comparePrerelease(ps, po) +} + +// UnmarshalJSON implements JSON.Unmarshaler interface. +func (v *Version) UnmarshalJSON(b []byte) error { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + temp, err := NewVersion(s) + if err != nil { + return err + } + v.major = temp.major + v.minor = temp.minor + v.patch = temp.patch + v.pre = temp.pre + v.metadata = temp.metadata + v.original = temp.original + return nil +} + +// MarshalJSON implements JSON.Marshaler interface. +func (v Version) MarshalJSON() ([]byte, error) { + return json.Marshal(v.String()) +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (v *Version) UnmarshalText(text []byte) error { + temp, err := NewVersion(string(text)) + if err != nil { + return err + } + + *v = *temp + + return nil +} + +// MarshalText implements the encoding.TextMarshaler interface. +func (v Version) MarshalText() ([]byte, error) { + return []byte(v.String()), nil +} + +// Scan implements the SQL.Scanner interface. +func (v *Version) Scan(value interface{}) error { + var s string + s, _ = value.(string) + temp, err := NewVersion(s) + if err != nil { + return err + } + v.major = temp.major + v.minor = temp.minor + v.patch = temp.patch + v.pre = temp.pre + v.metadata = temp.metadata + v.original = temp.original + return nil +} + +// Value implements the Driver.Valuer interface. +func (v Version) Value() (driver.Value, error) { + return v.String(), nil +} + +func compareSegment(v, o uint64) int { + if v < o { + return -1 + } + if v > o { + return 1 + } + + return 0 +} + +func comparePrerelease(v, o string) int { + // split the prelease versions by their part. The separator, per the spec, + // is a . + sparts := strings.Split(v, ".") + oparts := strings.Split(o, ".") + + // Find the longer length of the parts to know how many loop iterations to + // go through. + slen := len(sparts) + olen := len(oparts) + + l := slen + if olen > slen { + l = olen + } + + // Iterate over each part of the prereleases to compare the differences. + for i := 0; i < l; i++ { + // Since the lentgh of the parts can be different we need to create + // a placeholder. This is to avoid out of bounds issues. + stemp := "" + if i < slen { + stemp = sparts[i] + } + + otemp := "" + if i < olen { + otemp = oparts[i] + } + + d := comparePrePart(stemp, otemp) + if d != 0 { + return d + } + } + + // Reaching here means two versions are of equal value but have different + // metadata (the part following a +). They are not identical in string form + // but the version comparison finds them to be equal. + return 0 +} + +func comparePrePart(s, o string) int { + // Fastpath if they are equal + if s == o { + return 0 + } + + // When s or o are empty we can use the other in an attempt to determine + // the response. + if s == "" { + if o != "" { + return -1 + } + return 1 + } + + if o == "" { + if s != "" { + return 1 + } + return -1 + } + + // When comparing strings "99" is greater than "103". To handle + // cases like this we need to detect numbers and compare them. According + // to the semver spec, numbers are always positive. If there is a - at the + // start like -99 this is to be evaluated as an alphanum. numbers always + // have precedence over alphanum. Parsing as Uints because negative numbers + // are ignored. + + oi, n1 := strconv.ParseUint(o, 10, 64) + si, n2 := strconv.ParseUint(s, 10, 64) + + // The case where both are strings compare the strings + if n1 != nil && n2 != nil { + if s > o { + return 1 + } + return -1 + } else if n1 != nil { + // o is a string and s is a number + return -1 + } else if n2 != nil { + // s is a string and o is a number + return 1 + } + // Both are numbers + if si > oi { + return 1 + } + return -1 +} + +// Like strings.ContainsAny but does an only instead of any. +func containsOnly(s string, comp string) bool { + return strings.IndexFunc(s, func(r rune) bool { + return !strings.ContainsRune(comp, r) + }) == -1 +} + +// From the spec, "Identifiers MUST comprise only +// ASCII alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty. +// Numeric identifiers MUST NOT include leading zeroes.". These segments can +// be dot separated. +func validatePrerelease(p string) error { + eparts := strings.Split(p, ".") + for _, p := range eparts { + if containsOnly(p, num) { + if len(p) > 1 && p[0] == '0' { + return ErrSegmentStartsZero + } + } else if !containsOnly(p, allowed) { + return ErrInvalidPrerelease + } + } + + return nil +} + +// From the spec, "Build metadata MAY be denoted by +// appending a plus sign and a series of dot separated identifiers immediately +// following the patch or pre-release version. Identifiers MUST comprise only +// ASCII alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty." +func validateMetadata(m string) error { + eparts := strings.Split(m, ".") + for _, p := range eparts { + if !containsOnly(p, allowed) { + return ErrInvalidMetadata + } + } + return nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 43063f2..f4d14da 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,3 +1,6 @@ +# github.com/Masterminds/semver/v3 v3.2.1 +## explicit; go 1.18 +github.com/Masterminds/semver/v3 # github.com/davecgh/go-spew v1.1.1 ## explicit github.com/davecgh/go-spew/spew From b321a4618e7c528cedf07903318becb4d333eafa Mon Sep 17 00:00:00 2001 From: Fabian Fulga Date: Wed, 31 Jul 2024 15:41:14 +0300 Subject: [PATCH 02/12] Move execution/commands to execution/common/commands in a common package --- execution/{v0.1.1 => common}/commands.go | 2 +- execution/v0.1.0/commands.go | 27 ------------------ execution/v0.1.0/execution.go | 35 ++++++++++++------------ execution/v0.1.0/execution_test.go | 35 ++++++++++++------------ execution/v0.1.1/execution.go | 35 ++++++++++++------------ execution/v0.1.1/execution_test.go | 35 ++++++++++++------------ 6 files changed, 73 insertions(+), 96 deletions(-) rename execution/{v0.1.1 => common}/commands.go (98%) delete mode 100644 execution/v0.1.0/commands.go diff --git a/execution/v0.1.1/commands.go b/execution/common/commands.go similarity index 98% rename from execution/v0.1.1/commands.go rename to execution/common/commands.go index ed2ca1f..ffc3051 100644 --- a/execution/v0.1.1/commands.go +++ b/execution/common/commands.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package executionv011 +package common type ExecutionCommand string diff --git a/execution/v0.1.0/commands.go b/execution/v0.1.0/commands.go deleted file mode 100644 index 1e91fb1..0000000 --- a/execution/v0.1.0/commands.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2023 Cloudbase Solutions SRL -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -package executionv010 - -type ExecutionCommand string - -const ( - CreateInstanceCommand ExecutionCommand = "CreateInstance" - DeleteInstanceCommand ExecutionCommand = "DeleteInstance" - GetInstanceCommand ExecutionCommand = "GetInstance" - ListInstancesCommand ExecutionCommand = "ListInstances" - StartInstanceCommand ExecutionCommand = "StartInstance" - StopInstanceCommand ExecutionCommand = "StopInstance" - RemoveAllInstancesCommand ExecutionCommand = "RemoveAllInstances" -) diff --git a/execution/v0.1.0/execution.go b/execution/v0.1.0/execution.go index b28c7d2..4e810da 100644 --- a/execution/v0.1.0/execution.go +++ b/execution/v0.1.0/execution.go @@ -24,6 +24,7 @@ import ( "os" gErrors "github.com/cloudbase/garm-provider-common/errors" + common "github.com/cloudbase/garm-provider-common/execution/common" "github.com/cloudbase/garm-provider-common/params" "github.com/mattn/go-isatty" @@ -50,7 +51,7 @@ func ResolveErrorToExitCode(err error) int { func GetEnvironment() (EnvironmentV010, error) { env := EnvironmentV010{ - Command: ExecutionCommand(os.Getenv("GARM_COMMAND")), + Command: common.ExecutionCommand(os.Getenv("GARM_COMMAND")), ControllerID: os.Getenv("GARM_CONTROLLER_ID"), PoolID: os.Getenv("GARM_POOL_ID"), ProviderConfigFile: os.Getenv("GARM_PROVIDER_CONFIG_FILE"), @@ -59,9 +60,9 @@ func GetEnvironment() (EnvironmentV010, error) { // If this is a CreateInstance command, we need to get the bootstrap params // from stdin - if env.Command == CreateInstanceCommand { + if env.Command == common.CreateInstanceCommand { if isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) { - return EnvironmentV010{}, fmt.Errorf("%s requires data passed into stdin", CreateInstanceCommand) + return EnvironmentV010{}, fmt.Errorf("%s requires data passed into stdin", common.CreateInstanceCommand) } var data bytes.Buffer @@ -70,7 +71,7 @@ func GetEnvironment() (EnvironmentV010, error) { } if data.Len() == 0 { - return EnvironmentV010{}, fmt.Errorf("%s requires data passed into stdin", CreateInstanceCommand) + return EnvironmentV010{}, fmt.Errorf("%s requires data passed into stdin", common.CreateInstanceCommand) } var bootstrapParams params.BootstrapInstance @@ -92,7 +93,7 @@ func GetEnvironment() (EnvironmentV010, error) { } type EnvironmentV010 struct { - Command ExecutionCommand + Command common.ExecutionCommand ControllerID string PoolID string ProviderConfigFile string @@ -118,7 +119,7 @@ func (e EnvironmentV010) Validate() error { } switch e.Command { - case CreateInstanceCommand: + case common.CreateInstanceCommand: if e.BootstrapParams.Name == "" { return fmt.Errorf("missing bootstrap params") } @@ -128,16 +129,16 @@ func (e EnvironmentV010) Validate() error { if e.PoolID == "" { return fmt.Errorf("missing pool ID") } - case DeleteInstanceCommand, GetInstanceCommand, - StartInstanceCommand, StopInstanceCommand: + case common.DeleteInstanceCommand, common.GetInstanceCommand, + common.StartInstanceCommand, common.StopInstanceCommand: if e.InstanceID == "" { return fmt.Errorf("missing instance ID") } - case ListInstancesCommand: + case common.ListInstancesCommand: if e.PoolID == "" { return fmt.Errorf("missing pool ID") } - case RemoveAllInstancesCommand: + case common.RemoveAllInstancesCommand: if e.ControllerID == "" { return fmt.Errorf("missing controller ID") } @@ -150,7 +151,7 @@ func (e EnvironmentV010) Validate() error { func Run(ctx context.Context, provider ExternalProvider, env EnvironmentV010) (string, error) { var ret string switch env.Command { - case CreateInstanceCommand: + case common.CreateInstanceCommand: instance, err := provider.CreateInstance(ctx, env.BootstrapParams) if err != nil { return "", fmt.Errorf("failed to create instance in provider: %w", err) @@ -161,7 +162,7 @@ func Run(ctx context.Context, provider ExternalProvider, env EnvironmentV010) (s return "", fmt.Errorf("failed to marshal response: %w", err) } ret = string(asJs) - case GetInstanceCommand: + case common.GetInstanceCommand: instance, err := provider.GetInstance(ctx, env.InstanceID) if err != nil { return "", fmt.Errorf("failed to get instance from provider: %w", err) @@ -171,7 +172,7 @@ func Run(ctx context.Context, provider ExternalProvider, env EnvironmentV010) (s return "", fmt.Errorf("failed to marshal response: %w", err) } ret = string(asJs) - case ListInstancesCommand: + case common.ListInstancesCommand: instances, err := provider.ListInstances(ctx, env.PoolID) if err != nil { return "", fmt.Errorf("failed to list instances from provider: %w", err) @@ -181,19 +182,19 @@ func Run(ctx context.Context, provider ExternalProvider, env EnvironmentV010) (s return "", fmt.Errorf("failed to marshal response: %w", err) } ret = string(asJs) - case DeleteInstanceCommand: + case common.DeleteInstanceCommand: if err := provider.DeleteInstance(ctx, env.InstanceID); err != nil { return "", fmt.Errorf("failed to delete instance from provider: %w", err) } - case RemoveAllInstancesCommand: + case common.RemoveAllInstancesCommand: if err := provider.RemoveAllInstances(ctx); err != nil { return "", fmt.Errorf("failed to destroy environment: %w", err) } - case StartInstanceCommand: + case common.StartInstanceCommand: if err := provider.Start(ctx, env.InstanceID); err != nil { return "", fmt.Errorf("failed to start instance: %w", err) } - case StopInstanceCommand: + case common.StopInstanceCommand: if err := provider.Stop(ctx, env.InstanceID, true); err != nil { return "", fmt.Errorf("failed to stop instance: %w", err) } diff --git a/execution/v0.1.0/execution_test.go b/execution/v0.1.0/execution_test.go index 5c994fc..35b8e07 100644 --- a/execution/v0.1.0/execution_test.go +++ b/execution/v0.1.0/execution_test.go @@ -24,6 +24,7 @@ import ( "testing" gErrors "github.com/cloudbase/garm-provider-common/errors" + common "github.com/cloudbase/garm-provider-common/execution/common" "github.com/cloudbase/garm-provider-common/params" "github.com/stretchr/testify/require" ) @@ -135,7 +136,7 @@ func TestValidateEnvironment(t *testing.T) { { name: "valid environment", env: EnvironmentV010{ - Command: CreateInstanceCommand, + Command: common.CreateInstanceCommand, ControllerID: "controller-id", PoolID: "pool-id", ProviderConfigFile: tmpfile.Name(), @@ -156,7 +157,7 @@ func TestValidateEnvironment(t *testing.T) { { name: "invalid provider config file", env: EnvironmentV010{ - Command: CreateInstanceCommand, + Command: common.CreateInstanceCommand, ProviderConfigFile: "", }, errString: "missing GARM_PROVIDER_CONFIG_FILE", @@ -164,7 +165,7 @@ func TestValidateEnvironment(t *testing.T) { { name: "error accessing config file", env: EnvironmentV010{ - Command: CreateInstanceCommand, + Command: common.CreateInstanceCommand, ProviderConfigFile: "invalid-file", }, errString: "error accessing config file", @@ -172,7 +173,7 @@ func TestValidateEnvironment(t *testing.T) { { name: "invalid controller ID", env: EnvironmentV010{ - Command: CreateInstanceCommand, + Command: common.CreateInstanceCommand, ProviderConfigFile: tmpfile.Name(), }, errString: "missing GARM_CONTROLLER_ID", @@ -181,7 +182,7 @@ func TestValidateEnvironment(t *testing.T) { { name: "invalid instance ID", env: EnvironmentV010{ - Command: DeleteInstanceCommand, + Command: common.DeleteInstanceCommand, ProviderConfigFile: tmpfile.Name(), ControllerID: "controller-id", InstanceID: "", @@ -191,7 +192,7 @@ func TestValidateEnvironment(t *testing.T) { { name: "invalid pool ID", env: EnvironmentV010{ - Command: ListInstancesCommand, + Command: common.ListInstancesCommand, ProviderConfigFile: tmpfile.Name(), ControllerID: "controller-id", PoolID: "", @@ -201,7 +202,7 @@ func TestValidateEnvironment(t *testing.T) { { name: "invalid bootstrap params", env: EnvironmentV010{ - Command: CreateInstanceCommand, + Command: common.CreateInstanceCommand, ProviderConfigFile: tmpfile.Name(), ControllerID: "controller-id", PoolID: "pool-id", @@ -212,7 +213,7 @@ func TestValidateEnvironment(t *testing.T) { { name: "missing pool ID", env: EnvironmentV010{ - Command: CreateInstanceCommand, + Command: common.CreateInstanceCommand, ProviderConfigFile: tmpfile.Name(), ControllerID: "controller-id", PoolID: "", @@ -261,7 +262,7 @@ func TestRun(t *testing.T) { { name: "Valid environment", providerEnv: EnvironmentV010{ - Command: CreateInstanceCommand, + Command: common.CreateInstanceCommand, }, providerInstance: params.ProviderInstance{ Name: "test-instance", @@ -273,7 +274,7 @@ func TestRun(t *testing.T) { { name: "Failed to create instance", providerEnv: EnvironmentV010{ - Command: CreateInstanceCommand, + Command: common.CreateInstanceCommand, }, providerInstance: params.ProviderInstance{ Name: "test-instance", @@ -285,7 +286,7 @@ func TestRun(t *testing.T) { { name: "Failed to get instance", providerEnv: EnvironmentV010{ - Command: GetInstanceCommand, + Command: common.GetInstanceCommand, }, providerInstance: params.ProviderInstance{ Name: "test-instance", @@ -297,7 +298,7 @@ func TestRun(t *testing.T) { { name: "Failed to list instances", providerEnv: EnvironmentV010{ - Command: ListInstancesCommand, + Command: common.ListInstancesCommand, }, providerInstance: params.ProviderInstance{ Name: "test-instance", @@ -309,7 +310,7 @@ func TestRun(t *testing.T) { { name: "Failed to delete instance", providerEnv: EnvironmentV010{ - Command: DeleteInstanceCommand, + Command: common.DeleteInstanceCommand, }, providerInstance: params.ProviderInstance{ Name: "test-instance", @@ -321,7 +322,7 @@ func TestRun(t *testing.T) { { name: "Failed to remove all instances", providerEnv: EnvironmentV010{ - Command: RemoveAllInstancesCommand, + Command: common.RemoveAllInstancesCommand, }, providerInstance: params.ProviderInstance{ Name: "test-instance", @@ -333,7 +334,7 @@ func TestRun(t *testing.T) { { name: "Failed to start instance", providerEnv: EnvironmentV010{ - Command: StartInstanceCommand, + Command: common.StartInstanceCommand, }, providerInstance: params.ProviderInstance{ Name: "test-instance", @@ -345,7 +346,7 @@ func TestRun(t *testing.T) { { name: "Failed to stop instance", providerEnv: EnvironmentV010{ - Command: StopInstanceCommand, + Command: common.StopInstanceCommand, }, providerInstance: params.ProviderInstance{ Name: "test-instance", @@ -459,7 +460,7 @@ func TestGetEnvironment(t *testing.T) { env, err := GetEnvironment() if tc.errString == "" { require.NoError(t, err) - require.Equal(t, CreateInstanceCommand, env.Command) + require.Equal(t, common.CreateInstanceCommand, env.Command) } else { require.Equal(t, tc.errString, err.Error()) } diff --git a/execution/v0.1.1/execution.go b/execution/v0.1.1/execution.go index 80ef4a7..1261648 100644 --- a/execution/v0.1.1/execution.go +++ b/execution/v0.1.1/execution.go @@ -24,6 +24,7 @@ import ( "os" gErrors "github.com/cloudbase/garm-provider-common/errors" + common "github.com/cloudbase/garm-provider-common/execution/common" "github.com/cloudbase/garm-provider-common/params" "github.com/mattn/go-isatty" @@ -50,7 +51,7 @@ func ResolveErrorToExitCode(err error) int { func GetEnvironment() (EnvironmentV011, error) { env := EnvironmentV011{ - Command: ExecutionCommand(os.Getenv("GARM_COMMAND")), + Command: common.ExecutionCommand(os.Getenv("GARM_COMMAND")), ControllerID: os.Getenv("GARM_CONTROLLER_ID"), PoolID: os.Getenv("GARM_POOL_ID"), ProviderConfigFile: os.Getenv("GARM_PROVIDER_CONFIG_FILE"), @@ -61,9 +62,9 @@ func GetEnvironment() (EnvironmentV011, error) { // If this is a CreateInstance command, we need to get the bootstrap params // from stdin - if env.Command == CreateInstanceCommand { + if env.Command == common.CreateInstanceCommand { if isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) { - return EnvironmentV011{}, fmt.Errorf("%s requires data passed into stdin", CreateInstanceCommand) + return EnvironmentV011{}, fmt.Errorf("%s requires data passed into stdin", common.CreateInstanceCommand) } var data bytes.Buffer @@ -72,7 +73,7 @@ func GetEnvironment() (EnvironmentV011, error) { } if data.Len() == 0 { - return EnvironmentV011{}, fmt.Errorf("%s requires data passed into stdin", CreateInstanceCommand) + return EnvironmentV011{}, fmt.Errorf("%s requires data passed into stdin", common.CreateInstanceCommand) } var bootstrapParams params.BootstrapInstance @@ -94,7 +95,7 @@ func GetEnvironment() (EnvironmentV011, error) { } type EnvironmentV011 struct { - Command ExecutionCommand + Command common.ExecutionCommand ControllerID string PoolID string ProviderConfigFile string @@ -122,7 +123,7 @@ func (e EnvironmentV011) Validate() error { } switch e.Command { - case CreateInstanceCommand: + case common.CreateInstanceCommand: if e.BootstrapParams.Name == "" { return fmt.Errorf("missing bootstrap params") } @@ -132,20 +133,20 @@ func (e EnvironmentV011) Validate() error { if e.PoolID == "" { return fmt.Errorf("missing pool ID") } - case DeleteInstanceCommand, GetInstanceCommand, - StartInstanceCommand, StopInstanceCommand: + case common.DeleteInstanceCommand, common.GetInstanceCommand, + common.StartInstanceCommand, common.StopInstanceCommand: if e.InstanceID == "" { return fmt.Errorf("missing instance ID") } if e.PoolID == "" { return fmt.Errorf("missing pool ID") } - case ListInstancesCommand: + case common.ListInstancesCommand: if e.PoolID == "" { return fmt.Errorf("missing pool ID") } - case RemoveAllInstancesCommand: + case common.RemoveAllInstancesCommand: if e.ControllerID == "" { return fmt.Errorf("missing controller ID") } @@ -158,7 +159,7 @@ func (e EnvironmentV011) Validate() error { func Run(ctx context.Context, provider ExternalProvider, env EnvironmentV011) (string, error) { var ret string switch env.Command { - case CreateInstanceCommand: + case common.CreateInstanceCommand: instance, err := provider.CreateInstance(ctx, env.BootstrapParams) if err != nil { return "", fmt.Errorf("failed to create instance in provider: %w", err) @@ -169,7 +170,7 @@ func Run(ctx context.Context, provider ExternalProvider, env EnvironmentV011) (s return "", fmt.Errorf("failed to marshal response: %w", err) } ret = string(asJs) - case GetInstanceCommand: + case common.GetInstanceCommand: instance, err := provider.GetInstance(ctx, env.InstanceID) if err != nil { return "", fmt.Errorf("failed to get instance from provider: %w", err) @@ -179,7 +180,7 @@ func Run(ctx context.Context, provider ExternalProvider, env EnvironmentV011) (s return "", fmt.Errorf("failed to marshal response: %w", err) } ret = string(asJs) - case ListInstancesCommand: + case common.ListInstancesCommand: instances, err := provider.ListInstances(ctx, env.PoolID) if err != nil { return "", fmt.Errorf("failed to list instances from provider: %w", err) @@ -189,19 +190,19 @@ func Run(ctx context.Context, provider ExternalProvider, env EnvironmentV011) (s return "", fmt.Errorf("failed to marshal response: %w", err) } ret = string(asJs) - case DeleteInstanceCommand: + case common.DeleteInstanceCommand: if err := provider.DeleteInstance(ctx, env.InstanceID); err != nil { return "", fmt.Errorf("failed to delete instance from provider: %w", err) } - case RemoveAllInstancesCommand: + case common.RemoveAllInstancesCommand: if err := provider.RemoveAllInstances(ctx); err != nil { return "", fmt.Errorf("failed to destroy environment: %w", err) } - case StartInstanceCommand: + case common.StartInstanceCommand: if err := provider.Start(ctx, env.InstanceID); err != nil { return "", fmt.Errorf("failed to start instance: %w", err) } - case StopInstanceCommand: + case common.StopInstanceCommand: if err := provider.Stop(ctx, env.InstanceID, true); err != nil { return "", fmt.Errorf("failed to stop instance: %w", err) } diff --git a/execution/v0.1.1/execution_test.go b/execution/v0.1.1/execution_test.go index 769ea96..46f7420 100644 --- a/execution/v0.1.1/execution_test.go +++ b/execution/v0.1.1/execution_test.go @@ -24,6 +24,7 @@ import ( "testing" gErrors "github.com/cloudbase/garm-provider-common/errors" + common "github.com/cloudbase/garm-provider-common/execution/common" "github.com/cloudbase/garm-provider-common/params" "github.com/stretchr/testify/require" ) @@ -135,7 +136,7 @@ func TestValidateEnvironment(t *testing.T) { { name: "valid environment", env: EnvironmentV011{ - Command: CreateInstanceCommand, + Command: common.CreateInstanceCommand, ControllerID: "controller-id", PoolID: "pool-id", ProviderConfigFile: tmpfile.Name(), @@ -157,7 +158,7 @@ func TestValidateEnvironment(t *testing.T) { { name: "invalid provider config file", env: EnvironmentV011{ - Command: CreateInstanceCommand, + Command: common.CreateInstanceCommand, ProviderConfigFile: "", }, errString: "missing GARM_PROVIDER_CONFIG_FILE", @@ -165,7 +166,7 @@ func TestValidateEnvironment(t *testing.T) { { name: "error accessing config file", env: EnvironmentV011{ - Command: CreateInstanceCommand, + Command: common.CreateInstanceCommand, ProviderConfigFile: "invalid-file", }, errString: "error accessing config file", @@ -173,7 +174,7 @@ func TestValidateEnvironment(t *testing.T) { { name: "invalid controller ID", env: EnvironmentV011{ - Command: CreateInstanceCommand, + Command: common.CreateInstanceCommand, ProviderConfigFile: tmpfile.Name(), }, errString: "missing GARM_CONTROLLER_ID", @@ -182,7 +183,7 @@ func TestValidateEnvironment(t *testing.T) { { name: "invalid instance ID", env: EnvironmentV011{ - Command: DeleteInstanceCommand, + Command: common.DeleteInstanceCommand, ProviderConfigFile: tmpfile.Name(), ControllerID: "controller-id", InstanceID: "", @@ -192,7 +193,7 @@ func TestValidateEnvironment(t *testing.T) { { name: "invalid pool ID", env: EnvironmentV011{ - Command: ListInstancesCommand, + Command: common.ListInstancesCommand, ProviderConfigFile: tmpfile.Name(), ControllerID: "controller-id", PoolID: "", @@ -202,7 +203,7 @@ func TestValidateEnvironment(t *testing.T) { { name: "invalid bootstrap params", env: EnvironmentV011{ - Command: CreateInstanceCommand, + Command: common.CreateInstanceCommand, ProviderConfigFile: tmpfile.Name(), ControllerID: "controller-id", PoolID: "pool-id", @@ -213,7 +214,7 @@ func TestValidateEnvironment(t *testing.T) { { name: "missing pool ID", env: EnvironmentV011{ - Command: CreateInstanceCommand, + Command: common.CreateInstanceCommand, ProviderConfigFile: tmpfile.Name(), ControllerID: "controller-id", PoolID: "", @@ -262,7 +263,7 @@ func TestRun(t *testing.T) { { name: "Valid environment", providerEnv: EnvironmentV011{ - Command: CreateInstanceCommand, + Command: common.CreateInstanceCommand, }, providerInstance: params.ProviderInstance{ Name: "test-instance", @@ -274,7 +275,7 @@ func TestRun(t *testing.T) { { name: "Failed to create instance", providerEnv: EnvironmentV011{ - Command: CreateInstanceCommand, + Command: common.CreateInstanceCommand, }, providerInstance: params.ProviderInstance{ Name: "test-instance", @@ -286,7 +287,7 @@ func TestRun(t *testing.T) { { name: "Failed to get instance", providerEnv: EnvironmentV011{ - Command: GetInstanceCommand, + Command: common.GetInstanceCommand, }, providerInstance: params.ProviderInstance{ Name: "test-instance", @@ -298,7 +299,7 @@ func TestRun(t *testing.T) { { name: "Failed to list instances", providerEnv: EnvironmentV011{ - Command: ListInstancesCommand, + Command: common.ListInstancesCommand, }, providerInstance: params.ProviderInstance{ Name: "test-instance", @@ -310,7 +311,7 @@ func TestRun(t *testing.T) { { name: "Failed to delete instance", providerEnv: EnvironmentV011{ - Command: DeleteInstanceCommand, + Command: common.DeleteInstanceCommand, }, providerInstance: params.ProviderInstance{ Name: "test-instance", @@ -322,7 +323,7 @@ func TestRun(t *testing.T) { { name: "Failed to remove all instances", providerEnv: EnvironmentV011{ - Command: RemoveAllInstancesCommand, + Command: common.RemoveAllInstancesCommand, }, providerInstance: params.ProviderInstance{ Name: "test-instance", @@ -334,7 +335,7 @@ func TestRun(t *testing.T) { { name: "Failed to start instance", providerEnv: EnvironmentV011{ - Command: StartInstanceCommand, + Command: common.StartInstanceCommand, }, providerInstance: params.ProviderInstance{ Name: "test-instance", @@ -346,7 +347,7 @@ func TestRun(t *testing.T) { { name: "Failed to stop instance", providerEnv: EnvironmentV011{ - Command: StopInstanceCommand, + Command: common.StopInstanceCommand, }, providerInstance: params.ProviderInstance{ Name: "test-instance", @@ -460,7 +461,7 @@ func TestGetEnvironment(t *testing.T) { env, err := GetEnvironment() if tc.errString == "" { require.NoError(t, err) - require.Equal(t, CreateInstanceCommand, env.Command) + require.Equal(t, common.CreateInstanceCommand, env.Command) } else { require.Equal(t, tc.errString, err.Error()) } From eac8f753e250b867257e6d47d93eb934b0185d90 Mon Sep 17 00:00:00 2001 From: Fabian Fulga Date: Fri, 9 Aug 2024 01:01:02 +0300 Subject: [PATCH 03/12] Move to common getting boostrap params from stdin --- execution/common/commands.go | 42 +++++++++++++++++++++++++++++++++++ execution/v0.1.0/execution.go | 31 ++++---------------------- execution/v0.1.1/execution.go | 31 ++++---------------------- 3 files changed, 50 insertions(+), 54 deletions(-) diff --git a/execution/common/commands.go b/execution/common/commands.go index ffc3051..bf785bc 100644 --- a/execution/common/commands.go +++ b/execution/common/commands.go @@ -14,6 +14,17 @@ package common +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "os" + + "github.com/cloudbase/garm-provider-common/params" + "github.com/mattn/go-isatty" +) + type ExecutionCommand string const ( @@ -26,3 +37,34 @@ const ( RemoveAllInstancesCommand ExecutionCommand = "RemoveAllInstances" GetVersionInfoCommand ExecutionCommand = "GetVersionInfo" ) + +func GetBoostrapParamsFromStdin(c ExecutionCommand) (params.BootstrapInstance, error) { + var bootstrapParams params.BootstrapInstance + if c == CreateInstanceCommand { + if isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) { + return params.BootstrapInstance{}, fmt.Errorf("%s requires data passed into stdin", CreateInstanceCommand) + } + + var data bytes.Buffer + if _, err := io.Copy(&data, os.Stdin); err != nil { + return params.BootstrapInstance{}, fmt.Errorf("failed to copy bootstrap params") + } + + if data.Len() == 0 { + return params.BootstrapInstance{}, fmt.Errorf("%s requires data passed into stdin", CreateInstanceCommand) + } + + if err := json.Unmarshal(data.Bytes(), &bootstrapParams); err != nil { + return params.BootstrapInstance{}, fmt.Errorf("failed to decode instance params: %w", err) + } + if bootstrapParams.ExtraSpecs == nil { + // Initialize ExtraSpecs as an empty JSON object + bootstrapParams.ExtraSpecs = json.RawMessage([]byte("{}")) + } + + return bootstrapParams, nil + } + + // If the command is not CreateInstance, we don't need to read from stdin + return params.BootstrapInstance{}, nil +} diff --git a/execution/v0.1.0/execution.go b/execution/v0.1.0/execution.go index 4e810da..f987b7b 100644 --- a/execution/v0.1.0/execution.go +++ b/execution/v0.1.0/execution.go @@ -15,19 +15,15 @@ package executionv010 import ( - "bytes" "context" "encoding/json" "errors" "fmt" - "io" "os" gErrors "github.com/cloudbase/garm-provider-common/errors" common "github.com/cloudbase/garm-provider-common/execution/common" "github.com/cloudbase/garm-provider-common/params" - - "github.com/mattn/go-isatty" ) const ( @@ -60,30 +56,11 @@ func GetEnvironment() (EnvironmentV010, error) { // If this is a CreateInstance command, we need to get the bootstrap params // from stdin - if env.Command == common.CreateInstanceCommand { - if isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) { - return EnvironmentV010{}, fmt.Errorf("%s requires data passed into stdin", common.CreateInstanceCommand) - } - - var data bytes.Buffer - if _, err := io.Copy(&data, os.Stdin); err != nil { - return EnvironmentV010{}, fmt.Errorf("failed to copy bootstrap params") - } - - if data.Len() == 0 { - return EnvironmentV010{}, fmt.Errorf("%s requires data passed into stdin", common.CreateInstanceCommand) - } - - var bootstrapParams params.BootstrapInstance - if err := json.Unmarshal(data.Bytes(), &bootstrapParams); err != nil { - return EnvironmentV010{}, fmt.Errorf("failed to decode instance params: %w", err) - } - if bootstrapParams.ExtraSpecs == nil { - // Initialize ExtraSpecs as an empty JSON object - bootstrapParams.ExtraSpecs = json.RawMessage([]byte("{}")) - } - env.BootstrapParams = bootstrapParams + boostrapParams, err := common.GetBoostrapParamsFromStdin(env.Command) + if err != nil { + return EnvironmentV010{}, fmt.Errorf("failed to get bootstrap params: %w", err) } + env.BootstrapParams = boostrapParams if err := env.Validate(); err != nil { return EnvironmentV010{}, fmt.Errorf("failed to validate execution environment: %w", err) diff --git a/execution/v0.1.1/execution.go b/execution/v0.1.1/execution.go index 1261648..463b2f8 100644 --- a/execution/v0.1.1/execution.go +++ b/execution/v0.1.1/execution.go @@ -15,19 +15,15 @@ package executionv011 import ( - "bytes" "context" "encoding/json" "errors" "fmt" - "io" "os" gErrors "github.com/cloudbase/garm-provider-common/errors" common "github.com/cloudbase/garm-provider-common/execution/common" "github.com/cloudbase/garm-provider-common/params" - - "github.com/mattn/go-isatty" ) const ( @@ -62,30 +58,11 @@ func GetEnvironment() (EnvironmentV011, error) { // If this is a CreateInstance command, we need to get the bootstrap params // from stdin - if env.Command == common.CreateInstanceCommand { - if isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) { - return EnvironmentV011{}, fmt.Errorf("%s requires data passed into stdin", common.CreateInstanceCommand) - } - - var data bytes.Buffer - if _, err := io.Copy(&data, os.Stdin); err != nil { - return EnvironmentV011{}, fmt.Errorf("failed to copy bootstrap params") - } - - if data.Len() == 0 { - return EnvironmentV011{}, fmt.Errorf("%s requires data passed into stdin", common.CreateInstanceCommand) - } - - var bootstrapParams params.BootstrapInstance - if err := json.Unmarshal(data.Bytes(), &bootstrapParams); err != nil { - return EnvironmentV011{}, fmt.Errorf("failed to decode instance params: %w", err) - } - if bootstrapParams.ExtraSpecs == nil { - // Initialize ExtraSpecs as an empty JSON object - bootstrapParams.ExtraSpecs = json.RawMessage([]byte("{}")) - } - env.BootstrapParams = bootstrapParams + boostrapParams, err := common.GetBoostrapParamsFromStdin(env.Command) + if err != nil { + return EnvironmentV011{}, fmt.Errorf("failed to get bootstrap params: %w", err) } + env.BootstrapParams = boostrapParams if err := env.Validate(); err != nil { return EnvironmentV011{}, fmt.Errorf("failed to validate execution environment: %w", err) From df029be4471bcbc42c54ecd11e09f8211e92aefa Mon Sep 17 00:00:00 2001 From: Fabian Fulga Date: Fri, 9 Aug 2024 01:20:41 +0300 Subject: [PATCH 04/12] Add GetVersion command for v0.1.1 --- execution/common/commands.go | 2 +- execution/v0.1.0/execution.go | 1 + execution/v0.1.1/execution.go | 6 ++++++ execution/v0.1.1/execution_test.go | 4 ++++ execution/v0.1.1/interface.go | 2 ++ 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/execution/common/commands.go b/execution/common/commands.go index bf785bc..3e18b44 100644 --- a/execution/common/commands.go +++ b/execution/common/commands.go @@ -35,7 +35,7 @@ const ( StartInstanceCommand ExecutionCommand = "StartInstance" StopInstanceCommand ExecutionCommand = "StopInstance" RemoveAllInstancesCommand ExecutionCommand = "RemoveAllInstances" - GetVersionInfoCommand ExecutionCommand = "GetVersionInfo" + GetVersionCommand ExecutionCommand = "GetVersion" ) func GetBoostrapParamsFromStdin(c ExecutionCommand) (params.BootstrapInstance, error) { diff --git a/execution/v0.1.0/execution.go b/execution/v0.1.0/execution.go index f987b7b..fe4b348 100644 --- a/execution/v0.1.0/execution.go +++ b/execution/v0.1.0/execution.go @@ -75,6 +75,7 @@ type EnvironmentV010 struct { PoolID string ProviderConfigFile string InstanceID string + InterfaceVersion string BootstrapParams params.BootstrapInstance } diff --git a/execution/v0.1.1/execution.go b/execution/v0.1.1/execution.go index 463b2f8..aed8c22 100644 --- a/execution/v0.1.1/execution.go +++ b/execution/v0.1.1/execution.go @@ -183,6 +183,12 @@ func Run(ctx context.Context, provider ExternalProvider, env EnvironmentV011) (s if err := provider.Stop(ctx, env.InstanceID, true); err != nil { return "", fmt.Errorf("failed to stop instance: %w", err) } + case common.GetVersionCommand: + version := env.InterfaceVersion + if version == "" { + version = "v0.1.0" + } + ret = string(version) default: return "", fmt.Errorf("invalid command: %s", env.Command) } diff --git a/execution/v0.1.1/execution_test.go b/execution/v0.1.1/execution_test.go index 46f7420..e48fcc1 100644 --- a/execution/v0.1.1/execution_test.go +++ b/execution/v0.1.1/execution_test.go @@ -83,6 +83,10 @@ func (p *testExternalProvider) Start(context.Context, string) error { return nil } +func (p *testExternalProvider) GetVersion(context.Context) string { + return "v0.1.1" +} + func TestResolveErrorToExitCode(t *testing.T) { tests := []struct { name string diff --git a/execution/v0.1.1/interface.go b/execution/v0.1.1/interface.go index d85f28e..2fa70f5 100644 --- a/execution/v0.1.1/interface.go +++ b/execution/v0.1.1/interface.go @@ -38,4 +38,6 @@ type ExternalProvider interface { Stop(ctx context.Context, instance string, force bool) error // Start boots up an instance. Start(ctx context.Context, instance string) error + // GetVersion returns the version of the provider. + GetVersion(ctx context.Context) string } From 64de35659c18c430ad825c7b61ce1e2eec1d90ae Mon Sep 17 00:00:00 2001 From: Fabian Fulga Date: Fri, 9 Aug 2024 01:28:57 +0300 Subject: [PATCH 05/12] Add ValidatePoolInfo command for v0.1.1 --- execution/v0.1.1/commands.go | 23 +++++++++++++++++++++++ execution/v0.1.1/execution.go | 4 ++++ execution/v0.1.1/execution_test.go | 6 ++++++ execution/v0.1.1/interface.go | 2 ++ 4 files changed, 35 insertions(+) create mode 100644 execution/v0.1.1/commands.go diff --git a/execution/v0.1.1/commands.go b/execution/v0.1.1/commands.go new file mode 100644 index 0000000..d5be33a --- /dev/null +++ b/execution/v0.1.1/commands.go @@ -0,0 +1,23 @@ +// Copyright 2023 Cloudbase Solutions SRL +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package executionv011 + +import ( + common "github.com/cloudbase/garm-provider-common/execution/common" +) + +const ( + ValidatePoolInfoCommand common.ExecutionCommand = "ValidatePoolInfo" +) diff --git a/execution/v0.1.1/execution.go b/execution/v0.1.1/execution.go index aed8c22..52d614e 100644 --- a/execution/v0.1.1/execution.go +++ b/execution/v0.1.1/execution.go @@ -189,6 +189,10 @@ func Run(ctx context.Context, provider ExternalProvider, env EnvironmentV011) (s version = "v0.1.0" } ret = string(version) + case ValidatePoolInfoCommand: + if err := provider.ValidatePoolInfo(ctx, env.BootstrapParams.Image, env.BootstrapParams.Flavor, env.ProviderConfigFile, env.BootstrapParams); err != nil { + return "", fmt.Errorf("failed to validate pool info: %w", err) + } default: return "", fmt.Errorf("invalid command: %s", env.Command) } diff --git a/execution/v0.1.1/execution_test.go b/execution/v0.1.1/execution_test.go index e48fcc1..4912395 100644 --- a/execution/v0.1.1/execution_test.go +++ b/execution/v0.1.1/execution_test.go @@ -84,9 +84,15 @@ func (p *testExternalProvider) Start(context.Context, string) error { } func (p *testExternalProvider) GetVersion(context.Context) string { + //TODO: implement return "v0.1.1" } +func (p *testExternalProvider) ValidatePoolInfo(context.Context, string, string, string, params.BootstrapInstance) error { + //TODO: implement + return nil +} + func TestResolveErrorToExitCode(t *testing.T) { tests := []struct { name string diff --git a/execution/v0.1.1/interface.go b/execution/v0.1.1/interface.go index 2fa70f5..6391ecb 100644 --- a/execution/v0.1.1/interface.go +++ b/execution/v0.1.1/interface.go @@ -40,4 +40,6 @@ type ExternalProvider interface { Start(ctx context.Context, instance string) error // GetVersion returns the version of the provider. GetVersion(ctx context.Context) string + //ValidatePoolInfo will validate the pool info and return an error if it's not valid. + ValidatePoolInfo(ctx context.Context, image string, flavor string, providerConfig string, extraspecs params.BootstrapInstance) error } From 3b767146c88d37f6d774172e032a97203770b176 Mon Sep 17 00:00:00 2001 From: Fabian Fulga Date: Fri, 9 Aug 2024 01:53:09 +0300 Subject: [PATCH 06/12] Add GetVersion for v0.1.0 --- execution/v0.1.0/execution.go | 13 + execution/v0.1.0/execution_test.go | 5 + execution/v0.1.0/interface.go | 2 + go.mod | 3 +- go.sum | 2 + vendor/golang.org/x/mod/LICENSE | 27 ++ vendor/golang.org/x/mod/PATENTS | 22 ++ vendor/golang.org/x/mod/semver/semver.go | 401 +++++++++++++++++++++++ vendor/modules.txt | 3 + 9 files changed, 477 insertions(+), 1 deletion(-) create mode 100644 vendor/golang.org/x/mod/LICENSE create mode 100644 vendor/golang.org/x/mod/PATENTS create mode 100644 vendor/golang.org/x/mod/semver/semver.go diff --git a/execution/v0.1.0/execution.go b/execution/v0.1.0/execution.go index fe4b348..b8ad18f 100644 --- a/execution/v0.1.0/execution.go +++ b/execution/v0.1.0/execution.go @@ -21,6 +21,8 @@ import ( "fmt" "os" + "golang.org/x/mod/semver" + gErrors "github.com/cloudbase/garm-provider-common/errors" common "github.com/cloudbase/garm-provider-common/execution/common" "github.com/cloudbase/garm-provider-common/params" @@ -62,6 +64,10 @@ func GetEnvironment() (EnvironmentV010, error) { } env.BootstrapParams = boostrapParams + if env.InterfaceVersion == "" { + env.InterfaceVersion = "v0.1.0" + } + if err := env.Validate(); err != nil { return EnvironmentV010{}, fmt.Errorf("failed to validate execution environment: %w", err) } @@ -120,6 +126,10 @@ func (e EnvironmentV010) Validate() error { if e.ControllerID == "" { return fmt.Errorf("missing controller ID") } + case common.GetVersionCommand: + if semver.IsValid(e.InterfaceVersion) { + return fmt.Errorf("invalid interface version: %s", e.InterfaceVersion) + } default: return fmt.Errorf("unknown GARM_COMMAND: %s", e.Command) } @@ -176,6 +186,9 @@ func Run(ctx context.Context, provider ExternalProvider, env EnvironmentV010) (s if err := provider.Stop(ctx, env.InstanceID, true); err != nil { return "", fmt.Errorf("failed to stop instance: %w", err) } + case common.GetVersionCommand: + version := env.InterfaceVersion + ret = string(version) default: return "", fmt.Errorf("invalid command: %s", env.Command) } diff --git a/execution/v0.1.0/execution_test.go b/execution/v0.1.0/execution_test.go index 35b8e07..e32d66a 100644 --- a/execution/v0.1.0/execution_test.go +++ b/execution/v0.1.0/execution_test.go @@ -83,6 +83,11 @@ func (p *testExternalProvider) Start(context.Context, string) error { return nil } +func (p *testExternalProvider) GetVersion(context.Context) string { + //TODO: Implement this + return "0.1.0" +} + func TestResolveErrorToExitCode(t *testing.T) { tests := []struct { name string diff --git a/execution/v0.1.0/interface.go b/execution/v0.1.0/interface.go index d95df9f..670a2fa 100644 --- a/execution/v0.1.0/interface.go +++ b/execution/v0.1.0/interface.go @@ -38,4 +38,6 @@ type ExternalProvider interface { Stop(ctx context.Context, instance string, force bool) error // Start boots up an instance. Start(ctx context.Context, instance string) error + // GetVersion returns the version of the provider. + GetVersion(ctx context.Context) string } diff --git a/go.mod b/go.mod index bc55a04..57b02ad 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/cloudbase/garm-provider-common go 1.20 require ( + github.com/Masterminds/semver/v3 v3.2.1 github.com/google/uuid v1.3.0 github.com/gorilla/handlers v1.5.1 github.com/mattn/go-isatty v0.0.19 @@ -11,13 +12,13 @@ require ( github.com/stretchr/testify v1.8.4 github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569 golang.org/x/crypto v0.12.0 + golang.org/x/mod v0.20.0 golang.org/x/sys v0.11.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/felixge/httpsnoop v1.0.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/go.sum b/go.sum index 3b28739..eb54a74 100644 --- a/go.sum +++ b/go.sum @@ -22,6 +22,8 @@ github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569 h1:xzABM9let0HLLq github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569/go.mod h1:2Ly+NIftZN4de9zRmENdYbvPQeaVIYKWpLFStLFEBgI= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/vendor/golang.org/x/mod/LICENSE b/vendor/golang.org/x/mod/LICENSE new file mode 100644 index 0000000..2a7cf70 --- /dev/null +++ b/vendor/golang.org/x/mod/LICENSE @@ -0,0 +1,27 @@ +Copyright 2009 The Go Authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google LLC nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/mod/PATENTS b/vendor/golang.org/x/mod/PATENTS new file mode 100644 index 0000000..7330990 --- /dev/null +++ b/vendor/golang.org/x/mod/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/mod/semver/semver.go b/vendor/golang.org/x/mod/semver/semver.go new file mode 100644 index 0000000..9a2dfd3 --- /dev/null +++ b/vendor/golang.org/x/mod/semver/semver.go @@ -0,0 +1,401 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package semver implements comparison of semantic version strings. +// In this package, semantic version strings must begin with a leading "v", +// as in "v1.0.0". +// +// The general form of a semantic version string accepted by this package is +// +// vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]] +// +// where square brackets indicate optional parts of the syntax; +// MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros; +// PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers +// using only alphanumeric characters and hyphens; and +// all-numeric PRERELEASE identifiers must not have leading zeros. +// +// This package follows Semantic Versioning 2.0.0 (see semver.org) +// with two exceptions. First, it requires the "v" prefix. Second, it recognizes +// vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes) +// as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0. +package semver + +import "sort" + +// parsed returns the parsed form of a semantic version string. +type parsed struct { + major string + minor string + patch string + short string + prerelease string + build string +} + +// IsValid reports whether v is a valid semantic version string. +func IsValid(v string) bool { + _, ok := parse(v) + return ok +} + +// Canonical returns the canonical formatting of the semantic version v. +// It fills in any missing .MINOR or .PATCH and discards build metadata. +// Two semantic versions compare equal only if their canonical formattings +// are identical strings. +// The canonical invalid semantic version is the empty string. +func Canonical(v string) string { + p, ok := parse(v) + if !ok { + return "" + } + if p.build != "" { + return v[:len(v)-len(p.build)] + } + if p.short != "" { + return v + p.short + } + return v +} + +// Major returns the major version prefix of the semantic version v. +// For example, Major("v2.1.0") == "v2". +// If v is an invalid semantic version string, Major returns the empty string. +func Major(v string) string { + pv, ok := parse(v) + if !ok { + return "" + } + return v[:1+len(pv.major)] +} + +// MajorMinor returns the major.minor version prefix of the semantic version v. +// For example, MajorMinor("v2.1.0") == "v2.1". +// If v is an invalid semantic version string, MajorMinor returns the empty string. +func MajorMinor(v string) string { + pv, ok := parse(v) + if !ok { + return "" + } + i := 1 + len(pv.major) + if j := i + 1 + len(pv.minor); j <= len(v) && v[i] == '.' && v[i+1:j] == pv.minor { + return v[:j] + } + return v[:i] + "." + pv.minor +} + +// Prerelease returns the prerelease suffix of the semantic version v. +// For example, Prerelease("v2.1.0-pre+meta") == "-pre". +// If v is an invalid semantic version string, Prerelease returns the empty string. +func Prerelease(v string) string { + pv, ok := parse(v) + if !ok { + return "" + } + return pv.prerelease +} + +// Build returns the build suffix of the semantic version v. +// For example, Build("v2.1.0+meta") == "+meta". +// If v is an invalid semantic version string, Build returns the empty string. +func Build(v string) string { + pv, ok := parse(v) + if !ok { + return "" + } + return pv.build +} + +// Compare returns an integer comparing two versions according to +// semantic version precedence. +// The result will be 0 if v == w, -1 if v < w, or +1 if v > w. +// +// An invalid semantic version string is considered less than a valid one. +// All invalid semantic version strings compare equal to each other. +func Compare(v, w string) int { + pv, ok1 := parse(v) + pw, ok2 := parse(w) + if !ok1 && !ok2 { + return 0 + } + if !ok1 { + return -1 + } + if !ok2 { + return +1 + } + if c := compareInt(pv.major, pw.major); c != 0 { + return c + } + if c := compareInt(pv.minor, pw.minor); c != 0 { + return c + } + if c := compareInt(pv.patch, pw.patch); c != 0 { + return c + } + return comparePrerelease(pv.prerelease, pw.prerelease) +} + +// Max canonicalizes its arguments and then returns the version string +// that compares greater. +// +// Deprecated: use [Compare] instead. In most cases, returning a canonicalized +// version is not expected or desired. +func Max(v, w string) string { + v = Canonical(v) + w = Canonical(w) + if Compare(v, w) > 0 { + return v + } + return w +} + +// ByVersion implements [sort.Interface] for sorting semantic version strings. +type ByVersion []string + +func (vs ByVersion) Len() int { return len(vs) } +func (vs ByVersion) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] } +func (vs ByVersion) Less(i, j int) bool { + cmp := Compare(vs[i], vs[j]) + if cmp != 0 { + return cmp < 0 + } + return vs[i] < vs[j] +} + +// Sort sorts a list of semantic version strings using [ByVersion]. +func Sort(list []string) { + sort.Sort(ByVersion(list)) +} + +func parse(v string) (p parsed, ok bool) { + if v == "" || v[0] != 'v' { + return + } + p.major, v, ok = parseInt(v[1:]) + if !ok { + return + } + if v == "" { + p.minor = "0" + p.patch = "0" + p.short = ".0.0" + return + } + if v[0] != '.' { + ok = false + return + } + p.minor, v, ok = parseInt(v[1:]) + if !ok { + return + } + if v == "" { + p.patch = "0" + p.short = ".0" + return + } + if v[0] != '.' { + ok = false + return + } + p.patch, v, ok = parseInt(v[1:]) + if !ok { + return + } + if len(v) > 0 && v[0] == '-' { + p.prerelease, v, ok = parsePrerelease(v) + if !ok { + return + } + } + if len(v) > 0 && v[0] == '+' { + p.build, v, ok = parseBuild(v) + if !ok { + return + } + } + if v != "" { + ok = false + return + } + ok = true + return +} + +func parseInt(v string) (t, rest string, ok bool) { + if v == "" { + return + } + if v[0] < '0' || '9' < v[0] { + return + } + i := 1 + for i < len(v) && '0' <= v[i] && v[i] <= '9' { + i++ + } + if v[0] == '0' && i != 1 { + return + } + return v[:i], v[i:], true +} + +func parsePrerelease(v string) (t, rest string, ok bool) { + // "A pre-release version MAY be denoted by appending a hyphen and + // a series of dot separated identifiers immediately following the patch version. + // Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-]. + // Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes." + if v == "" || v[0] != '-' { + return + } + i := 1 + start := 1 + for i < len(v) && v[i] != '+' { + if !isIdentChar(v[i]) && v[i] != '.' { + return + } + if v[i] == '.' { + if start == i || isBadNum(v[start:i]) { + return + } + start = i + 1 + } + i++ + } + if start == i || isBadNum(v[start:i]) { + return + } + return v[:i], v[i:], true +} + +func parseBuild(v string) (t, rest string, ok bool) { + if v == "" || v[0] != '+' { + return + } + i := 1 + start := 1 + for i < len(v) { + if !isIdentChar(v[i]) && v[i] != '.' { + return + } + if v[i] == '.' { + if start == i { + return + } + start = i + 1 + } + i++ + } + if start == i { + return + } + return v[:i], v[i:], true +} + +func isIdentChar(c byte) bool { + return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-' +} + +func isBadNum(v string) bool { + i := 0 + for i < len(v) && '0' <= v[i] && v[i] <= '9' { + i++ + } + return i == len(v) && i > 1 && v[0] == '0' +} + +func isNum(v string) bool { + i := 0 + for i < len(v) && '0' <= v[i] && v[i] <= '9' { + i++ + } + return i == len(v) +} + +func compareInt(x, y string) int { + if x == y { + return 0 + } + if len(x) < len(y) { + return -1 + } + if len(x) > len(y) { + return +1 + } + if x < y { + return -1 + } else { + return +1 + } +} + +func comparePrerelease(x, y string) int { + // "When major, minor, and patch are equal, a pre-release version has + // lower precedence than a normal version. + // Example: 1.0.0-alpha < 1.0.0. + // Precedence for two pre-release versions with the same major, minor, + // and patch version MUST be determined by comparing each dot separated + // identifier from left to right until a difference is found as follows: + // identifiers consisting of only digits are compared numerically and + // identifiers with letters or hyphens are compared lexically in ASCII + // sort order. Numeric identifiers always have lower precedence than + // non-numeric identifiers. A larger set of pre-release fields has a + // higher precedence than a smaller set, if all of the preceding + // identifiers are equal. + // Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < + // 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0." + if x == y { + return 0 + } + if x == "" { + return +1 + } + if y == "" { + return -1 + } + for x != "" && y != "" { + x = x[1:] // skip - or . + y = y[1:] // skip - or . + var dx, dy string + dx, x = nextIdent(x) + dy, y = nextIdent(y) + if dx != dy { + ix := isNum(dx) + iy := isNum(dy) + if ix != iy { + if ix { + return -1 + } else { + return +1 + } + } + if ix { + if len(dx) < len(dy) { + return -1 + } + if len(dx) > len(dy) { + return +1 + } + } + if dx < dy { + return -1 + } else { + return +1 + } + } + } + if x == "" { + return -1 + } else { + return +1 + } +} + +func nextIdent(x string) (dx, rest string) { + i := 0 + for i < len(x) && x[i] != '.' { + i++ + } + return x[:i], x[i:] +} diff --git a/vendor/modules.txt b/vendor/modules.txt index f4d14da..9c5f4cf 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -41,6 +41,9 @@ golang.org/x/crypto/chacha20poly1305 golang.org/x/crypto/hkdf golang.org/x/crypto/internal/alias golang.org/x/crypto/internal/poly1305 +# golang.org/x/mod v0.20.0 +## explicit; go 1.18 +golang.org/x/mod/semver # golang.org/x/sys v0.11.0 ## explicit; go 1.17 golang.org/x/sys/cpu From dab9e8e35c54986cfca22be007d43f0d149edd02 Mon Sep 17 00:00:00 2001 From: Fabian Fulga Date: Fri, 9 Aug 2024 01:53:36 +0300 Subject: [PATCH 07/12] Update GetVersion for v0.1.1 --- execution/v0.1.1/execution.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/execution/v0.1.1/execution.go b/execution/v0.1.1/execution.go index 52d614e..208e851 100644 --- a/execution/v0.1.1/execution.go +++ b/execution/v0.1.1/execution.go @@ -21,6 +21,8 @@ import ( "fmt" "os" + "golang.org/x/mod/semver" + gErrors "github.com/cloudbase/garm-provider-common/errors" common "github.com/cloudbase/garm-provider-common/execution/common" "github.com/cloudbase/garm-provider-common/params" @@ -64,6 +66,10 @@ func GetEnvironment() (EnvironmentV011, error) { } env.BootstrapParams = boostrapParams + if env.InterfaceVersion == "" { + env.InterfaceVersion = "v0.1.0" + } + if err := env.Validate(); err != nil { return EnvironmentV011{}, fmt.Errorf("failed to validate execution environment: %w", err) } @@ -122,11 +128,14 @@ func (e EnvironmentV011) Validate() error { if e.PoolID == "" { return fmt.Errorf("missing pool ID") } - case common.RemoveAllInstancesCommand: if e.ControllerID == "" { return fmt.Errorf("missing controller ID") } + case common.GetVersionCommand: + if semver.IsValid(e.InterfaceVersion) { + return fmt.Errorf("invalid interface version: %s", e.InterfaceVersion) + } default: return fmt.Errorf("unknown GARM_COMMAND: %s", e.Command) } @@ -185,9 +194,6 @@ func Run(ctx context.Context, provider ExternalProvider, env EnvironmentV011) (s } case common.GetVersionCommand: version := env.InterfaceVersion - if version == "" { - version = "v0.1.0" - } ret = string(version) case ValidatePoolInfoCommand: if err := provider.ValidatePoolInfo(ctx, env.BootstrapParams.Image, env.BootstrapParams.Flavor, env.ProviderConfigFile, env.BootstrapParams); err != nil { From 5bb948cab79e5df01368b8963f5f04f2e971423c Mon Sep 17 00:00:00 2001 From: Fabian Fulga Date: Fri, 9 Aug 2024 02:01:01 +0300 Subject: [PATCH 08/12] Add GetConfigJSONSchema and GetExtraSpecsJSONSchema commands to v0.1.1 --- execution/v0.1.1/commands.go | 4 +++- execution/v0.1.1/execution.go | 12 ++++++++++++ execution/v0.1.1/execution_test.go | 10 ++++++++++ execution/v0.1.1/interface.go | 6 +++++- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/execution/v0.1.1/commands.go b/execution/v0.1.1/commands.go index d5be33a..c9facc0 100644 --- a/execution/v0.1.1/commands.go +++ b/execution/v0.1.1/commands.go @@ -19,5 +19,7 @@ import ( ) const ( - ValidatePoolInfoCommand common.ExecutionCommand = "ValidatePoolInfo" + ValidatePoolInfoCommand common.ExecutionCommand = "ValidatePoolInfo" + GetConfigJSONSchemaCommand common.ExecutionCommand = "GetConfigJSONSchema" + GetExtraSpecsJSONSchemaCommand common.ExecutionCommand = "GetExtraSpecsJSONSchema" ) diff --git a/execution/v0.1.1/execution.go b/execution/v0.1.1/execution.go index 208e851..da15d17 100644 --- a/execution/v0.1.1/execution.go +++ b/execution/v0.1.1/execution.go @@ -199,6 +199,18 @@ func Run(ctx context.Context, provider ExternalProvider, env EnvironmentV011) (s if err := provider.ValidatePoolInfo(ctx, env.BootstrapParams.Image, env.BootstrapParams.Flavor, env.ProviderConfigFile, env.BootstrapParams); err != nil { return "", fmt.Errorf("failed to validate pool info: %w", err) } + case GetConfigJSONSchemaCommand: + schema, err := provider.GetConfigJSONSchema(ctx) + if err != nil { + return "", fmt.Errorf("failed to get config JSON schema: %w", err) + } + ret = schema + case GetExtraSpecsJSONSchemaCommand: + schema, err := provider.GetExtraSpecsJSONSchema(ctx) + if err != nil { + return "", fmt.Errorf("failed to get extra specs JSON schema: %w", err) + } + ret = schema default: return "", fmt.Errorf("invalid command: %s", env.Command) } diff --git a/execution/v0.1.1/execution_test.go b/execution/v0.1.1/execution_test.go index 4912395..eaed412 100644 --- a/execution/v0.1.1/execution_test.go +++ b/execution/v0.1.1/execution_test.go @@ -93,6 +93,16 @@ func (p *testExternalProvider) ValidatePoolInfo(context.Context, string, string, return nil } +func (p *testExternalProvider) GetConfigJSONSchema(context.Context) (string, error) { + //TODO: implement + return "", nil +} + +func (p *testExternalProvider) GetExtraSpecsJSONSchema(context.Context) (string, error) { + //TODO: implement + return "", nil +} + func TestResolveErrorToExitCode(t *testing.T) { tests := []struct { name string diff --git a/execution/v0.1.1/interface.go b/execution/v0.1.1/interface.go index 6391ecb..4c1ac47 100644 --- a/execution/v0.1.1/interface.go +++ b/execution/v0.1.1/interface.go @@ -40,6 +40,10 @@ type ExternalProvider interface { Start(ctx context.Context, instance string) error // GetVersion returns the version of the provider. GetVersion(ctx context.Context) string - //ValidatePoolInfo will validate the pool info and return an error if it's not valid. + // ValidatePoolInfo will validate the pool info and return an error if it's not valid. ValidatePoolInfo(ctx context.Context, image string, flavor string, providerConfig string, extraspecs params.BootstrapInstance) error + // GetConfigJSONSchema will return the JSON schema for the provider's configuration. + GetConfigJSONSchema(ctx context.Context) (string, error) + // GetExtraSpecsJSONSchema will return the JSON schema for the provider's extra specs. + GetExtraSpecsJSONSchema(ctx context.Context) (string, error) } From de2902972020020b31856347cc5418bc6af1e3af Mon Sep 17 00:00:00 2001 From: Fabian Fulga Date: Fri, 9 Aug 2024 02:27:20 +0300 Subject: [PATCH 09/12] Update ValidatePoolInfo command signature --- execution/v0.1.1/execution.go | 2 +- execution/v0.1.1/execution_test.go | 2 +- execution/v0.1.1/interface.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/execution/v0.1.1/execution.go b/execution/v0.1.1/execution.go index da15d17..52272bc 100644 --- a/execution/v0.1.1/execution.go +++ b/execution/v0.1.1/execution.go @@ -196,7 +196,7 @@ func Run(ctx context.Context, provider ExternalProvider, env EnvironmentV011) (s version := env.InterfaceVersion ret = string(version) case ValidatePoolInfoCommand: - if err := provider.ValidatePoolInfo(ctx, env.BootstrapParams.Image, env.BootstrapParams.Flavor, env.ProviderConfigFile, env.BootstrapParams); err != nil { + if err := provider.ValidatePoolInfo(ctx, env.BootstrapParams.Image, env.BootstrapParams.Flavor, env.ProviderConfigFile, env.ExtraSpecs); err != nil { return "", fmt.Errorf("failed to validate pool info: %w", err) } case GetConfigJSONSchemaCommand: diff --git a/execution/v0.1.1/execution_test.go b/execution/v0.1.1/execution_test.go index eaed412..6f6f026 100644 --- a/execution/v0.1.1/execution_test.go +++ b/execution/v0.1.1/execution_test.go @@ -88,7 +88,7 @@ func (p *testExternalProvider) GetVersion(context.Context) string { return "v0.1.1" } -func (p *testExternalProvider) ValidatePoolInfo(context.Context, string, string, string, params.BootstrapInstance) error { +func (p *testExternalProvider) ValidatePoolInfo(context.Context, string, string, string, string) error { //TODO: implement return nil } diff --git a/execution/v0.1.1/interface.go b/execution/v0.1.1/interface.go index 4c1ac47..6d1154a 100644 --- a/execution/v0.1.1/interface.go +++ b/execution/v0.1.1/interface.go @@ -41,7 +41,7 @@ type ExternalProvider interface { // GetVersion returns the version of the provider. GetVersion(ctx context.Context) string // ValidatePoolInfo will validate the pool info and return an error if it's not valid. - ValidatePoolInfo(ctx context.Context, image string, flavor string, providerConfig string, extraspecs params.BootstrapInstance) error + ValidatePoolInfo(ctx context.Context, image string, flavor string, providerConfig string, extraspecs string) error // GetConfigJSONSchema will return the JSON schema for the provider's configuration. GetConfigJSONSchema(ctx context.Context) (string, error) // GetExtraSpecsJSONSchema will return the JSON schema for the provider's extra specs. From 284d518049768f9b7ca8d9a18ad5f41efa7969b6 Mon Sep 17 00:00:00 2001 From: Fabian Fulga Date: Fri, 9 Aug 2024 02:27:33 +0300 Subject: [PATCH 10/12] Add common execution GetEnvironment --- execution/common/versions.go | 22 + execution/execution.go | 34 +- execution/v0.1.0/execution.go | 2 +- execution/v0.1.1/execution.go | 2 +- go.mod | 1 - go.sum | 2 - .../Masterminds/semver/v3/.gitignore | 1 - .../Masterminds/semver/v3/.golangci.yml | 27 - .../Masterminds/semver/v3/CHANGELOG.md | 214 ------ .../Masterminds/semver/v3/LICENSE.txt | 19 - .../github.com/Masterminds/semver/v3/Makefile | 30 - .../Masterminds/semver/v3/README.md | 258 ------- .../Masterminds/semver/v3/SECURITY.md | 19 - .../Masterminds/semver/v3/collection.go | 24 - .../Masterminds/semver/v3/constraints.go | 594 ---------------- .../github.com/Masterminds/semver/v3/doc.go | 184 ----- .../Masterminds/semver/v3/version.go | 639 ------------------ vendor/modules.txt | 3 - 18 files changed, 56 insertions(+), 2019 deletions(-) create mode 100644 execution/common/versions.go delete mode 100644 vendor/github.com/Masterminds/semver/v3/.gitignore delete mode 100644 vendor/github.com/Masterminds/semver/v3/.golangci.yml delete mode 100644 vendor/github.com/Masterminds/semver/v3/CHANGELOG.md delete mode 100644 vendor/github.com/Masterminds/semver/v3/LICENSE.txt delete mode 100644 vendor/github.com/Masterminds/semver/v3/Makefile delete mode 100644 vendor/github.com/Masterminds/semver/v3/README.md delete mode 100644 vendor/github.com/Masterminds/semver/v3/SECURITY.md delete mode 100644 vendor/github.com/Masterminds/semver/v3/collection.go delete mode 100644 vendor/github.com/Masterminds/semver/v3/constraints.go delete mode 100644 vendor/github.com/Masterminds/semver/v3/doc.go delete mode 100644 vendor/github.com/Masterminds/semver/v3/version.go diff --git a/execution/common/versions.go b/execution/common/versions.go new file mode 100644 index 0000000..ebdbbb8 --- /dev/null +++ b/execution/common/versions.go @@ -0,0 +1,22 @@ +// Copyright 2023 Cloudbase Solutions SRL +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package common + +const ( + // Version v0.1.0 + Version010 = "v0.1.0" + // Version v0.1.1 + Version011 = "v0.1.1" +) diff --git a/execution/execution.go b/execution/execution.go index e123d87..db9141a 100644 --- a/execution/execution.go +++ b/execution/execution.go @@ -15,7 +15,10 @@ package execution import ( - semver "github.com/Masterminds/semver/v3" + "fmt" + "os" + + "github.com/cloudbase/garm-provider-common/execution/common" executionv010 "github.com/cloudbase/garm-provider-common/execution/v0.1.0" executionv011 "github.com/cloudbase/garm-provider-common/execution/v0.1.1" ) @@ -23,5 +26,32 @@ import ( type Environment struct { EnvironmentV010 executionv010.EnvironmentV010 EnvironmentV011 executionv011.EnvironmentV011 - InterfaceVersion semver.Version + InterfaceVersion string +} + +func GetEnvironment() (Environment, error) { + interfaceVersion := os.Getenv("GARM_INTERFACE_VERSION") + + switch interfaceVersion { + case common.Version010: + env, err := executionv010.GetEnvironment() + if err != nil { + return Environment{}, err + } + return Environment{ + EnvironmentV010: env, + InterfaceVersion: interfaceVersion, + }, nil + case common.Version011: + env, err := executionv011.GetEnvironment() + if err != nil { + return Environment{}, err + } + return Environment{ + EnvironmentV011: env, + InterfaceVersion: interfaceVersion, + }, nil + default: + return Environment{}, fmt.Errorf("unsupported interface version: %s", interfaceVersion) + } } diff --git a/execution/v0.1.0/execution.go b/execution/v0.1.0/execution.go index b8ad18f..c209458 100644 --- a/execution/v0.1.0/execution.go +++ b/execution/v0.1.0/execution.go @@ -65,7 +65,7 @@ func GetEnvironment() (EnvironmentV010, error) { env.BootstrapParams = boostrapParams if env.InterfaceVersion == "" { - env.InterfaceVersion = "v0.1.0" + env.InterfaceVersion = common.Version010 } if err := env.Validate(); err != nil { diff --git a/execution/v0.1.1/execution.go b/execution/v0.1.1/execution.go index 52272bc..95132b0 100644 --- a/execution/v0.1.1/execution.go +++ b/execution/v0.1.1/execution.go @@ -67,7 +67,7 @@ func GetEnvironment() (EnvironmentV011, error) { env.BootstrapParams = boostrapParams if env.InterfaceVersion == "" { - env.InterfaceVersion = "v0.1.0" + env.InterfaceVersion = common.Version010 } if err := env.Validate(); err != nil { diff --git a/go.mod b/go.mod index 57b02ad..9101310 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/cloudbase/garm-provider-common go 1.20 require ( - github.com/Masterminds/semver/v3 v3.2.1 github.com/google/uuid v1.3.0 github.com/gorilla/handlers v1.5.1 github.com/mattn/go-isatty v0.0.19 diff --git a/go.sum b/go.sum index eb54a74..7cb5ad6 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= diff --git a/vendor/github.com/Masterminds/semver/v3/.gitignore b/vendor/github.com/Masterminds/semver/v3/.gitignore deleted file mode 100644 index 6b061e6..0000000 --- a/vendor/github.com/Masterminds/semver/v3/.gitignore +++ /dev/null @@ -1 +0,0 @@ -_fuzz/ \ No newline at end of file diff --git a/vendor/github.com/Masterminds/semver/v3/.golangci.yml b/vendor/github.com/Masterminds/semver/v3/.golangci.yml deleted file mode 100644 index fbc6332..0000000 --- a/vendor/github.com/Masterminds/semver/v3/.golangci.yml +++ /dev/null @@ -1,27 +0,0 @@ -run: - deadline: 2m - -linters: - disable-all: true - enable: - - misspell - - govet - - staticcheck - - errcheck - - unparam - - ineffassign - - nakedret - - gocyclo - - dupl - - goimports - - revive - - gosec - - gosimple - - typecheck - - unused - -linters-settings: - gofmt: - simplify: true - dupl: - threshold: 600 diff --git a/vendor/github.com/Masterminds/semver/v3/CHANGELOG.md b/vendor/github.com/Masterminds/semver/v3/CHANGELOG.md deleted file mode 100644 index f126264..0000000 --- a/vendor/github.com/Masterminds/semver/v3/CHANGELOG.md +++ /dev/null @@ -1,214 +0,0 @@ -# Changelog - -## 3.2.0 (2022-11-28) - -### Added - -- #190: Added text marshaling and unmarshaling -- #167: Added JSON marshalling for constraints (thanks @SimonTheLeg) -- #173: Implement encoding.TextMarshaler and encoding.TextUnmarshaler on Version (thanks @MarkRosemaker) -- #179: Added New() version constructor (thanks @kazhuravlev) - -### Changed - -- #182/#183: Updated CI testing setup - -### Fixed - -- #186: Fixing issue where validation of constraint section gave false positives -- #176: Fix constraints check with *-0 (thanks @mtt0) -- #181: Fixed Caret operator (^) gives unexpected results when the minor version in constraint is 0 (thanks @arshchimni) -- #161: Fixed godoc (thanks @afirth) - -## 3.1.1 (2020-11-23) - -### Fixed - -- #158: Fixed issue with generated regex operation order that could cause problem - -## 3.1.0 (2020-04-15) - -### Added - -- #131: Add support for serializing/deserializing SQL (thanks @ryancurrah) - -### Changed - -- #148: More accurate validation messages on constraints - -## 3.0.3 (2019-12-13) - -### Fixed - -- #141: Fixed issue with <= comparison - -## 3.0.2 (2019-11-14) - -### Fixed - -- #134: Fixed broken constraint checking with ^0.0 (thanks @krmichelos) - -## 3.0.1 (2019-09-13) - -### Fixed - -- #125: Fixes issue with module path for v3 - -## 3.0.0 (2019-09-12) - -This is a major release of the semver package which includes API changes. The Go -API is compatible with ^1. The Go API was not changed because many people are using -`go get` without Go modules for their applications and API breaking changes cause -errors which we have or would need to support. - -The changes in this release are the handling based on the data passed into the -functions. These are described in the added and changed sections below. - -### Added - -- StrictNewVersion function. This is similar to NewVersion but will return an - error if the version passed in is not a strict semantic version. For example, - 1.2.3 would pass but v1.2.3 or 1.2 would fail because they are not strictly - speaking semantic versions. This function is faster, performs fewer operations, - and uses fewer allocations than NewVersion. -- Fuzzing has been performed on NewVersion, StrictNewVersion, and NewConstraint. - The Makefile contains the operations used. For more information on you can start - on Wikipedia at https://en.wikipedia.org/wiki/Fuzzing -- Now using Go modules - -### Changed - -- NewVersion has proper prerelease and metadata validation with error messages - to signal an issue with either of them -- ^ now operates using a similar set of rules to npm/js and Rust/Cargo. If the - version is >=1 the ^ ranges works the same as v1. For major versions of 0 the - rules have changed. The minor version is treated as the stable version unless - a patch is specified and then it is equivalent to =. One difference from npm/js - is that prereleases there are only to a specific version (e.g. 1.2.3). - Prereleases here look over multiple versions and follow semantic version - ordering rules. This pattern now follows along with the expected and requested - handling of this packaged by numerous users. - -## 1.5.0 (2019-09-11) - -### Added - -- #103: Add basic fuzzing for `NewVersion()` (thanks @jesse-c) - -### Changed - -- #82: Clarify wildcard meaning in range constraints and update tests for it (thanks @greysteil) -- #83: Clarify caret operator range for pre-1.0.0 dependencies (thanks @greysteil) -- #72: Adding docs comment pointing to vert for a cli -- #71: Update the docs on pre-release comparator handling -- #89: Test with new go versions (thanks @thedevsaddam) -- #87: Added $ to ValidPrerelease for better validation (thanks @jeremycarroll) - -### Fixed - -- #78: Fix unchecked error in example code (thanks @ravron) -- #70: Fix the handling of pre-releases and the 0.0.0 release edge case -- #97: Fixed copyright file for proper display on GitHub -- #107: Fix handling prerelease when sorting alphanum and num -- #109: Fixed where Validate sometimes returns wrong message on error - -## 1.4.2 (2018-04-10) - -### Changed - -- #72: Updated the docs to point to vert for a console appliaction -- #71: Update the docs on pre-release comparator handling - -### Fixed - -- #70: Fix the handling of pre-releases and the 0.0.0 release edge case - -## 1.4.1 (2018-04-02) - -### Fixed - -- Fixed #64: Fix pre-release precedence issue (thanks @uudashr) - -## 1.4.0 (2017-10-04) - -### Changed - -- #61: Update NewVersion to parse ints with a 64bit int size (thanks @zknill) - -## 1.3.1 (2017-07-10) - -### Fixed - -- Fixed #57: number comparisons in prerelease sometimes inaccurate - -## 1.3.0 (2017-05-02) - -### Added - -- #45: Added json (un)marshaling support (thanks @mh-cbon) -- Stability marker. See https://masterminds.github.io/stability/ - -### Fixed - -- #51: Fix handling of single digit tilde constraint (thanks @dgodd) - -### Changed - -- #55: The godoc icon moved from png to svg - -## 1.2.3 (2017-04-03) - -### Fixed - -- #46: Fixed 0.x.x and 0.0.x in constraints being treated as * - -## Release 1.2.2 (2016-12-13) - -### Fixed - -- #34: Fixed issue where hyphen range was not working with pre-release parsing. - -## Release 1.2.1 (2016-11-28) - -### Fixed - -- #24: Fixed edge case issue where constraint "> 0" does not handle "0.0.1-alpha" - properly. - -## Release 1.2.0 (2016-11-04) - -### Added - -- #20: Added MustParse function for versions (thanks @adamreese) -- #15: Added increment methods on versions (thanks @mh-cbon) - -### Fixed - -- Issue #21: Per the SemVer spec (section 9) a pre-release is unstable and - might not satisfy the intended compatibility. The change here ignores pre-releases - on constraint checks (e.g., ~ or ^) when a pre-release is not part of the - constraint. For example, `^1.2.3` will ignore pre-releases while - `^1.2.3-alpha` will include them. - -## Release 1.1.1 (2016-06-30) - -### Changed - -- Issue #9: Speed up version comparison performance (thanks @sdboyer) -- Issue #8: Added benchmarks (thanks @sdboyer) -- Updated Go Report Card URL to new location -- Updated Readme to add code snippet formatting (thanks @mh-cbon) -- Updating tagging to v[SemVer] structure for compatibility with other tools. - -## Release 1.1.0 (2016-03-11) - -- Issue #2: Implemented validation to provide reasons a versions failed a - constraint. - -## Release 1.0.1 (2015-12-31) - -- Fixed #1: * constraint failing on valid versions. - -## Release 1.0.0 (2015-10-20) - -- Initial release diff --git a/vendor/github.com/Masterminds/semver/v3/LICENSE.txt b/vendor/github.com/Masterminds/semver/v3/LICENSE.txt deleted file mode 100644 index 9ff7da9..0000000 --- a/vendor/github.com/Masterminds/semver/v3/LICENSE.txt +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (C) 2014-2019, Matt Butcher and Matt Farina - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/github.com/Masterminds/semver/v3/Makefile b/vendor/github.com/Masterminds/semver/v3/Makefile deleted file mode 100644 index 0e7b5c7..0000000 --- a/vendor/github.com/Masterminds/semver/v3/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -GOPATH=$(shell go env GOPATH) -GOLANGCI_LINT=$(GOPATH)/bin/golangci-lint - -.PHONY: lint -lint: $(GOLANGCI_LINT) - @echo "==> Linting codebase" - @$(GOLANGCI_LINT) run - -.PHONY: test -test: - @echo "==> Running tests" - GO111MODULE=on go test -v - -.PHONY: test-cover -test-cover: - @echo "==> Running Tests with coverage" - GO111MODULE=on go test -cover . - -.PHONY: fuzz -fuzz: - @echo "==> Running Fuzz Tests" - go test -fuzz=FuzzNewVersion -fuzztime=15s . - go test -fuzz=FuzzStrictNewVersion -fuzztime=15s . - go test -fuzz=FuzzNewConstraint -fuzztime=15s . - -$(GOLANGCI_LINT): - # Install golangci-lint. The configuration for it is in the .golangci.yml - # file in the root of the repository - echo ${GOPATH} - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.17.1 diff --git a/vendor/github.com/Masterminds/semver/v3/README.md b/vendor/github.com/Masterminds/semver/v3/README.md deleted file mode 100644 index eab8cac..0000000 --- a/vendor/github.com/Masterminds/semver/v3/README.md +++ /dev/null @@ -1,258 +0,0 @@ -# SemVer - -The `semver` package provides the ability to work with [Semantic Versions](http://semver.org) in Go. Specifically it provides the ability to: - -* Parse semantic versions -* Sort semantic versions -* Check if a semantic version fits within a set of constraints -* Optionally work with a `v` prefix - -[![Stability: -Active](https://masterminds.github.io/stability/active.svg)](https://masterminds.github.io/stability/active.html) -[![](https://github.com/Masterminds/semver/workflows/Tests/badge.svg)](https://github.com/Masterminds/semver/actions) -[![GoDoc](https://img.shields.io/static/v1?label=godoc&message=reference&color=blue)](https://pkg.go.dev/github.com/Masterminds/semver/v3) -[![Go Report Card](https://goreportcard.com/badge/github.com/Masterminds/semver)](https://goreportcard.com/report/github.com/Masterminds/semver) - -If you are looking for a command line tool for version comparisons please see -[vert](https://github.com/Masterminds/vert) which uses this library. - -## Package Versions - -Note, import `github.com/github.com/Masterminds/semver/v3` to use the latest version. - -There are three major versions fo the `semver` package. - -* 3.x.x is the stable and active version. This version is focused on constraint - compatibility for range handling in other tools from other languages. It has - a similar API to the v1 releases. The development of this version is on the master - branch. The documentation for this version is below. -* 2.x was developed primarily for [dep](https://github.com/golang/dep). There are - no tagged releases and the development was performed by [@sdboyer](https://github.com/sdboyer). - There are API breaking changes from v1. This version lives on the [2.x branch](https://github.com/Masterminds/semver/tree/2.x). -* 1.x.x is the original release. It is no longer maintained. You should use the - v3 release instead. You can read the documentation for the 1.x.x release - [here](https://github.com/Masterminds/semver/blob/release-1/README.md). - -## Parsing Semantic Versions - -There are two functions that can parse semantic versions. The `StrictNewVersion` -function only parses valid version 2 semantic versions as outlined in the -specification. The `NewVersion` function attempts to coerce a version into a -semantic version and parse it. For example, if there is a leading v or a version -listed without all 3 parts (e.g. `v1.2`) it will attempt to coerce it into a valid -semantic version (e.g., 1.2.0). In both cases a `Version` object is returned -that can be sorted, compared, and used in constraints. - -When parsing a version an error is returned if there is an issue parsing the -version. For example, - - v, err := semver.NewVersion("1.2.3-beta.1+build345") - -The version object has methods to get the parts of the version, compare it to -other versions, convert the version back into a string, and get the original -string. Getting the original string is useful if the semantic version was coerced -into a valid form. - -## Sorting Semantic Versions - -A set of versions can be sorted using the `sort` package from the standard library. -For example, - -```go -raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",} -vs := make([]*semver.Version, len(raw)) -for i, r := range raw { - v, err := semver.NewVersion(r) - if err != nil { - t.Errorf("Error parsing version: %s", err) - } - - vs[i] = v -} - -sort.Sort(semver.Collection(vs)) -``` - -## Checking Version Constraints - -There are two methods for comparing versions. One uses comparison methods on -`Version` instances and the other uses `Constraints`. There are some important -differences to notes between these two methods of comparison. - -1. When two versions are compared using functions such as `Compare`, `LessThan`, - and others it will follow the specification and always include prereleases - within the comparison. It will provide an answer that is valid with the - comparison section of the spec at https://semver.org/#spec-item-11 -2. When constraint checking is used for checks or validation it will follow a - different set of rules that are common for ranges with tools like npm/js - and Rust/Cargo. This includes considering prereleases to be invalid if the - ranges does not include one. If you want to have it include pre-releases a - simple solution is to include `-0` in your range. -3. Constraint ranges can have some complex rules including the shorthand use of - ~ and ^. For more details on those see the options below. - -There are differences between the two methods or checking versions because the -comparison methods on `Version` follow the specification while comparison ranges -are not part of the specification. Different packages and tools have taken it -upon themselves to come up with range rules. This has resulted in differences. -For example, npm/js and Cargo/Rust follow similar patterns while PHP has a -different pattern for ^. The comparison features in this package follow the -npm/js and Cargo/Rust lead because applications using it have followed similar -patters with their versions. - -Checking a version against version constraints is one of the most featureful -parts of the package. - -```go -c, err := semver.NewConstraint(">= 1.2.3") -if err != nil { - // Handle constraint not being parsable. -} - -v, err := semver.NewVersion("1.3") -if err != nil { - // Handle version not being parsable. -} -// Check if the version meets the constraints. The a variable will be true. -a := c.Check(v) -``` - -### Basic Comparisons - -There are two elements to the comparisons. First, a comparison string is a list -of space or comma separated AND comparisons. These are then separated by || (OR) -comparisons. For example, `">= 1.2 < 3.0.0 || >= 4.2.3"` is looking for a -comparison that's greater than or equal to 1.2 and less than 3.0.0 or is -greater than or equal to 4.2.3. - -The basic comparisons are: - -* `=`: equal (aliased to no operator) -* `!=`: not equal -* `>`: greater than -* `<`: less than -* `>=`: greater than or equal to -* `<=`: less than or equal to - -### Working With Prerelease Versions - -Pre-releases, for those not familiar with them, are used for software releases -prior to stable or generally available releases. Examples of prereleases include -development, alpha, beta, and release candidate releases. A prerelease may be -a version such as `1.2.3-beta.1` while the stable release would be `1.2.3`. In the -order of precedence, prereleases come before their associated releases. In this -example `1.2.3-beta.1 < 1.2.3`. - -According to the Semantic Version specification prereleases may not be -API compliant with their release counterpart. It says, - -> A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version. - -SemVer comparisons using constraints without a prerelease comparator will skip -prerelease versions. For example, `>=1.2.3` will skip prereleases when looking -at a list of releases while `>=1.2.3-0` will evaluate and find prereleases. - -The reason for the `0` as a pre-release version in the example comparison is -because pre-releases can only contain ASCII alphanumerics and hyphens (along with -`.` separators), per the spec. Sorting happens in ASCII sort order, again per the -spec. The lowest character is a `0` in ASCII sort order -(see an [ASCII Table](http://www.asciitable.com/)) - -Understanding ASCII sort ordering is important because A-Z comes before a-z. That -means `>=1.2.3-BETA` will return `1.2.3-alpha`. What you might expect from case -sensitivity doesn't apply here. This is due to ASCII sort ordering which is what -the spec specifies. - -### Hyphen Range Comparisons - -There are multiple methods to handle ranges and the first is hyphens ranges. -These look like: - -* `1.2 - 1.4.5` which is equivalent to `>= 1.2 <= 1.4.5` -* `2.3.4 - 4.5` which is equivalent to `>= 2.3.4 <= 4.5` - -### Wildcards In Comparisons - -The `x`, `X`, and `*` characters can be used as a wildcard character. This works -for all comparison operators. When used on the `=` operator it falls -back to the patch level comparison (see tilde below). For example, - -* `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` -* `>= 1.2.x` is equivalent to `>= 1.2.0` -* `<= 2.x` is equivalent to `< 3` -* `*` is equivalent to `>= 0.0.0` - -### Tilde Range Comparisons (Patch) - -The tilde (`~`) comparison operator is for patch level ranges when a minor -version is specified and major level changes when the minor number is missing. -For example, - -* `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0` -* `~1` is equivalent to `>= 1, < 2` -* `~2.3` is equivalent to `>= 2.3, < 2.4` -* `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` -* `~1.x` is equivalent to `>= 1, < 2` - -### Caret Range Comparisons (Major) - -The caret (`^`) comparison operator is for major level changes once a stable -(1.0.0) release has occurred. Prior to a 1.0.0 release the minor versions acts -as the API stability level. This is useful when comparisons of API versions as a -major change is API breaking. For example, - -* `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0` -* `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0` -* `^2.3` is equivalent to `>= 2.3, < 3` -* `^2.x` is equivalent to `>= 2.0.0, < 3` -* `^0.2.3` is equivalent to `>=0.2.3 <0.3.0` -* `^0.2` is equivalent to `>=0.2.0 <0.3.0` -* `^0.0.3` is equivalent to `>=0.0.3 <0.0.4` -* `^0.0` is equivalent to `>=0.0.0 <0.1.0` -* `^0` is equivalent to `>=0.0.0 <1.0.0` - -## Validation - -In addition to testing a version against a constraint, a version can be validated -against a constraint. When validation fails a slice of errors containing why a -version didn't meet the constraint is returned. For example, - -```go -c, err := semver.NewConstraint("<= 1.2.3, >= 1.4") -if err != nil { - // Handle constraint not being parseable. -} - -v, err := semver.NewVersion("1.3") -if err != nil { - // Handle version not being parseable. -} - -// Validate a version against a constraint. -a, msgs := c.Validate(v) -// a is false -for _, m := range msgs { - fmt.Println(m) - - // Loops over the errors which would read - // "1.3 is greater than 1.2.3" - // "1.3 is less than 1.4" -} -``` - -## Contribute - -If you find an issue or want to contribute please file an [issue](https://github.com/Masterminds/semver/issues) -or [create a pull request](https://github.com/Masterminds/semver/pulls). - -## Security - -Security is an important consideration for this project. The project currently -uses the following tools to help discover security issues: - -* [CodeQL](https://github.com/Masterminds/semver) -* [gosec](https://github.com/securego/gosec) -* Daily Fuzz testing - -If you believe you have found a security vulnerability you can privately disclose -it through the [GitHub security page](https://github.com/Masterminds/semver/security). diff --git a/vendor/github.com/Masterminds/semver/v3/SECURITY.md b/vendor/github.com/Masterminds/semver/v3/SECURITY.md deleted file mode 100644 index a30a66b..0000000 --- a/vendor/github.com/Masterminds/semver/v3/SECURITY.md +++ /dev/null @@ -1,19 +0,0 @@ -# Security Policy - -## Supported Versions - -The following versions of semver are currently supported: - -| Version | Supported | -| ------- | ------------------ | -| 3.x | :white_check_mark: | -| 2.x | :x: | -| 1.x | :x: | - -Fixes are only released for the latest minor version in the form of a patch release. - -## Reporting a Vulnerability - -You can privately disclose a vulnerability through GitHubs -[private vulnerability reporting](https://github.com/Masterminds/semver/security/advisories) -mechanism. diff --git a/vendor/github.com/Masterminds/semver/v3/collection.go b/vendor/github.com/Masterminds/semver/v3/collection.go deleted file mode 100644 index a782358..0000000 --- a/vendor/github.com/Masterminds/semver/v3/collection.go +++ /dev/null @@ -1,24 +0,0 @@ -package semver - -// Collection is a collection of Version instances and implements the sort -// interface. See the sort package for more details. -// https://golang.org/pkg/sort/ -type Collection []*Version - -// Len returns the length of a collection. The number of Version instances -// on the slice. -func (c Collection) Len() int { - return len(c) -} - -// Less is needed for the sort interface to compare two Version objects on the -// slice. If checks if one is less than the other. -func (c Collection) Less(i, j int) bool { - return c[i].LessThan(c[j]) -} - -// Swap is needed for the sort interface to replace the Version objects -// at two different positions in the slice. -func (c Collection) Swap(i, j int) { - c[i], c[j] = c[j], c[i] -} diff --git a/vendor/github.com/Masterminds/semver/v3/constraints.go b/vendor/github.com/Masterminds/semver/v3/constraints.go deleted file mode 100644 index 8461c7e..0000000 --- a/vendor/github.com/Masterminds/semver/v3/constraints.go +++ /dev/null @@ -1,594 +0,0 @@ -package semver - -import ( - "bytes" - "errors" - "fmt" - "regexp" - "strings" -) - -// Constraints is one or more constraint that a semantic version can be -// checked against. -type Constraints struct { - constraints [][]*constraint -} - -// NewConstraint returns a Constraints instance that a Version instance can -// be checked against. If there is a parse error it will be returned. -func NewConstraint(c string) (*Constraints, error) { - - // Rewrite - ranges into a comparison operation. - c = rewriteRange(c) - - ors := strings.Split(c, "||") - or := make([][]*constraint, len(ors)) - for k, v := range ors { - - // TODO: Find a way to validate and fetch all the constraints in a simpler form - - // Validate the segment - if !validConstraintRegex.MatchString(v) { - return nil, fmt.Errorf("improper constraint: %s", v) - } - - cs := findConstraintRegex.FindAllString(v, -1) - if cs == nil { - cs = append(cs, v) - } - result := make([]*constraint, len(cs)) - for i, s := range cs { - pc, err := parseConstraint(s) - if err != nil { - return nil, err - } - - result[i] = pc - } - or[k] = result - } - - o := &Constraints{constraints: or} - return o, nil -} - -// Check tests if a version satisfies the constraints. -func (cs Constraints) Check(v *Version) bool { - // TODO(mattfarina): For v4 of this library consolidate the Check and Validate - // functions as the underlying functions make that possible now. - // loop over the ORs and check the inner ANDs - for _, o := range cs.constraints { - joy := true - for _, c := range o { - if check, _ := c.check(v); !check { - joy = false - break - } - } - - if joy { - return true - } - } - - return false -} - -// Validate checks if a version satisfies a constraint. If not a slice of -// reasons for the failure are returned in addition to a bool. -func (cs Constraints) Validate(v *Version) (bool, []error) { - // loop over the ORs and check the inner ANDs - var e []error - - // Capture the prerelease message only once. When it happens the first time - // this var is marked - var prerelesase bool - for _, o := range cs.constraints { - joy := true - for _, c := range o { - // Before running the check handle the case there the version is - // a prerelease and the check is not searching for prereleases. - if c.con.pre == "" && v.pre != "" { - if !prerelesase { - em := fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) - e = append(e, em) - prerelesase = true - } - joy = false - - } else { - - if _, err := c.check(v); err != nil { - e = append(e, err) - joy = false - } - } - } - - if joy { - return true, []error{} - } - } - - return false, e -} - -func (cs Constraints) String() string { - buf := make([]string, len(cs.constraints)) - var tmp bytes.Buffer - - for k, v := range cs.constraints { - tmp.Reset() - vlen := len(v) - for kk, c := range v { - tmp.WriteString(c.string()) - - // Space separate the AND conditions - if vlen > 1 && kk < vlen-1 { - tmp.WriteString(" ") - } - } - buf[k] = tmp.String() - } - - return strings.Join(buf, " || ") -} - -// UnmarshalText implements the encoding.TextUnmarshaler interface. -func (cs *Constraints) UnmarshalText(text []byte) error { - temp, err := NewConstraint(string(text)) - if err != nil { - return err - } - - *cs = *temp - - return nil -} - -// MarshalText implements the encoding.TextMarshaler interface. -func (cs Constraints) MarshalText() ([]byte, error) { - return []byte(cs.String()), nil -} - -var constraintOps map[string]cfunc -var constraintRegex *regexp.Regexp -var constraintRangeRegex *regexp.Regexp - -// Used to find individual constraints within a multi-constraint string -var findConstraintRegex *regexp.Regexp - -// Used to validate an segment of ANDs is valid -var validConstraintRegex *regexp.Regexp - -const cvRegex string = `v?([0-9|x|X|\*]+)(\.[0-9|x|X|\*]+)?(\.[0-9|x|X|\*]+)?` + - `(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` + - `(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` - -func init() { - constraintOps = map[string]cfunc{ - "": constraintTildeOrEqual, - "=": constraintTildeOrEqual, - "!=": constraintNotEqual, - ">": constraintGreaterThan, - "<": constraintLessThan, - ">=": constraintGreaterThanEqual, - "=>": constraintGreaterThanEqual, - "<=": constraintLessThanEqual, - "=<": constraintLessThanEqual, - "~": constraintTilde, - "~>": constraintTilde, - "^": constraintCaret, - } - - ops := `=||!=|>|<|>=|=>|<=|=<|~|~>|\^` - - constraintRegex = regexp.MustCompile(fmt.Sprintf( - `^\s*(%s)\s*(%s)\s*$`, - ops, - cvRegex)) - - constraintRangeRegex = regexp.MustCompile(fmt.Sprintf( - `\s*(%s)\s+-\s+(%s)\s*`, - cvRegex, cvRegex)) - - findConstraintRegex = regexp.MustCompile(fmt.Sprintf( - `(%s)\s*(%s)`, - ops, - cvRegex)) - - // The first time a constraint shows up will look slightly different from - // future times it shows up due to a leading space or comma in a given - // string. - validConstraintRegex = regexp.MustCompile(fmt.Sprintf( - `^(\s*(%s)\s*(%s)\s*)((?:\s+|,\s*)(%s)\s*(%s)\s*)*$`, - ops, - cvRegex, - ops, - cvRegex)) -} - -// An individual constraint -type constraint struct { - // The version used in the constraint check. For example, if a constraint - // is '<= 2.0.0' the con a version instance representing 2.0.0. - con *Version - - // The original parsed version (e.g., 4.x from != 4.x) - orig string - - // The original operator for the constraint - origfunc string - - // When an x is used as part of the version (e.g., 1.x) - minorDirty bool - dirty bool - patchDirty bool -} - -// Check if a version meets the constraint -func (c *constraint) check(v *Version) (bool, error) { - return constraintOps[c.origfunc](v, c) -} - -// String prints an individual constraint into a string -func (c *constraint) string() string { - return c.origfunc + c.orig -} - -type cfunc func(v *Version, c *constraint) (bool, error) - -func parseConstraint(c string) (*constraint, error) { - if len(c) > 0 { - m := constraintRegex.FindStringSubmatch(c) - if m == nil { - return nil, fmt.Errorf("improper constraint: %s", c) - } - - cs := &constraint{ - orig: m[2], - origfunc: m[1], - } - - ver := m[2] - minorDirty := false - patchDirty := false - dirty := false - if isX(m[3]) || m[3] == "" { - ver = fmt.Sprintf("0.0.0%s", m[6]) - dirty = true - } else if isX(strings.TrimPrefix(m[4], ".")) || m[4] == "" { - minorDirty = true - dirty = true - ver = fmt.Sprintf("%s.0.0%s", m[3], m[6]) - } else if isX(strings.TrimPrefix(m[5], ".")) || m[5] == "" { - dirty = true - patchDirty = true - ver = fmt.Sprintf("%s%s.0%s", m[3], m[4], m[6]) - } - - con, err := NewVersion(ver) - if err != nil { - - // The constraintRegex should catch any regex parsing errors. So, - // we should never get here. - return nil, errors.New("constraint Parser Error") - } - - cs.con = con - cs.minorDirty = minorDirty - cs.patchDirty = patchDirty - cs.dirty = dirty - - return cs, nil - } - - // The rest is the special case where an empty string was passed in which - // is equivalent to * or >=0.0.0 - con, err := StrictNewVersion("0.0.0") - if err != nil { - - // The constraintRegex should catch any regex parsing errors. So, - // we should never get here. - return nil, errors.New("constraint Parser Error") - } - - cs := &constraint{ - con: con, - orig: c, - origfunc: "", - minorDirty: false, - patchDirty: false, - dirty: true, - } - return cs, nil -} - -// Constraint functions -func constraintNotEqual(v *Version, c *constraint) (bool, error) { - if c.dirty { - - // If there is a pre-release on the version but the constraint isn't looking - // for them assume that pre-releases are not compatible. See issue 21 for - // more details. - if v.Prerelease() != "" && c.con.Prerelease() == "" { - return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) - } - - if c.con.Major() != v.Major() { - return true, nil - } - if c.con.Minor() != v.Minor() && !c.minorDirty { - return true, nil - } else if c.minorDirty { - return false, fmt.Errorf("%s is equal to %s", v, c.orig) - } else if c.con.Patch() != v.Patch() && !c.patchDirty { - return true, nil - } else if c.patchDirty { - // Need to handle prereleases if present - if v.Prerelease() != "" || c.con.Prerelease() != "" { - eq := comparePrerelease(v.Prerelease(), c.con.Prerelease()) != 0 - if eq { - return true, nil - } - return false, fmt.Errorf("%s is equal to %s", v, c.orig) - } - return false, fmt.Errorf("%s is equal to %s", v, c.orig) - } - } - - eq := v.Equal(c.con) - if eq { - return false, fmt.Errorf("%s is equal to %s", v, c.orig) - } - - return true, nil -} - -func constraintGreaterThan(v *Version, c *constraint) (bool, error) { - - // If there is a pre-release on the version but the constraint isn't looking - // for them assume that pre-releases are not compatible. See issue 21 for - // more details. - if v.Prerelease() != "" && c.con.Prerelease() == "" { - return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) - } - - var eq bool - - if !c.dirty { - eq = v.Compare(c.con) == 1 - if eq { - return true, nil - } - return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig) - } - - if v.Major() > c.con.Major() { - return true, nil - } else if v.Major() < c.con.Major() { - return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig) - } else if c.minorDirty { - // This is a range case such as >11. When the version is something like - // 11.1.0 is it not > 11. For that we would need 12 or higher - return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig) - } else if c.patchDirty { - // This is for ranges such as >11.1. A version of 11.1.1 is not greater - // which one of 11.2.1 is greater - eq = v.Minor() > c.con.Minor() - if eq { - return true, nil - } - return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig) - } - - // If we have gotten here we are not comparing pre-preleases and can use the - // Compare function to accomplish that. - eq = v.Compare(c.con) == 1 - if eq { - return true, nil - } - return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig) -} - -func constraintLessThan(v *Version, c *constraint) (bool, error) { - // If there is a pre-release on the version but the constraint isn't looking - // for them assume that pre-releases are not compatible. See issue 21 for - // more details. - if v.Prerelease() != "" && c.con.Prerelease() == "" { - return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) - } - - eq := v.Compare(c.con) < 0 - if eq { - return true, nil - } - return false, fmt.Errorf("%s is greater than or equal to %s", v, c.orig) -} - -func constraintGreaterThanEqual(v *Version, c *constraint) (bool, error) { - - // If there is a pre-release on the version but the constraint isn't looking - // for them assume that pre-releases are not compatible. See issue 21 for - // more details. - if v.Prerelease() != "" && c.con.Prerelease() == "" { - return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) - } - - eq := v.Compare(c.con) >= 0 - if eq { - return true, nil - } - return false, fmt.Errorf("%s is less than %s", v, c.orig) -} - -func constraintLessThanEqual(v *Version, c *constraint) (bool, error) { - // If there is a pre-release on the version but the constraint isn't looking - // for them assume that pre-releases are not compatible. See issue 21 for - // more details. - if v.Prerelease() != "" && c.con.Prerelease() == "" { - return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) - } - - var eq bool - - if !c.dirty { - eq = v.Compare(c.con) <= 0 - if eq { - return true, nil - } - return false, fmt.Errorf("%s is greater than %s", v, c.orig) - } - - if v.Major() > c.con.Major() { - return false, fmt.Errorf("%s is greater than %s", v, c.orig) - } else if v.Major() == c.con.Major() && v.Minor() > c.con.Minor() && !c.minorDirty { - return false, fmt.Errorf("%s is greater than %s", v, c.orig) - } - - return true, nil -} - -// ~*, ~>* --> >= 0.0.0 (any) -// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0, <3.0.0 -// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0, <2.1.0 -// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0, <1.3.0 -// ~1.2.3, ~>1.2.3 --> >=1.2.3, <1.3.0 -// ~1.2.0, ~>1.2.0 --> >=1.2.0, <1.3.0 -func constraintTilde(v *Version, c *constraint) (bool, error) { - // If there is a pre-release on the version but the constraint isn't looking - // for them assume that pre-releases are not compatible. See issue 21 for - // more details. - if v.Prerelease() != "" && c.con.Prerelease() == "" { - return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) - } - - if v.LessThan(c.con) { - return false, fmt.Errorf("%s is less than %s", v, c.orig) - } - - // ~0.0.0 is a special case where all constraints are accepted. It's - // equivalent to >= 0.0.0. - if c.con.Major() == 0 && c.con.Minor() == 0 && c.con.Patch() == 0 && - !c.minorDirty && !c.patchDirty { - return true, nil - } - - if v.Major() != c.con.Major() { - return false, fmt.Errorf("%s does not have same major version as %s", v, c.orig) - } - - if v.Minor() != c.con.Minor() && !c.minorDirty { - return false, fmt.Errorf("%s does not have same major and minor version as %s", v, c.orig) - } - - return true, nil -} - -// When there is a .x (dirty) status it automatically opts in to ~. Otherwise -// it's a straight = -func constraintTildeOrEqual(v *Version, c *constraint) (bool, error) { - // If there is a pre-release on the version but the constraint isn't looking - // for them assume that pre-releases are not compatible. See issue 21 for - // more details. - if v.Prerelease() != "" && c.con.Prerelease() == "" { - return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) - } - - if c.dirty { - return constraintTilde(v, c) - } - - eq := v.Equal(c.con) - if eq { - return true, nil - } - - return false, fmt.Errorf("%s is not equal to %s", v, c.orig) -} - -// ^* --> (any) -// ^1.2.3 --> >=1.2.3 <2.0.0 -// ^1.2 --> >=1.2.0 <2.0.0 -// ^1 --> >=1.0.0 <2.0.0 -// ^0.2.3 --> >=0.2.3 <0.3.0 -// ^0.2 --> >=0.2.0 <0.3.0 -// ^0.0.3 --> >=0.0.3 <0.0.4 -// ^0.0 --> >=0.0.0 <0.1.0 -// ^0 --> >=0.0.0 <1.0.0 -func constraintCaret(v *Version, c *constraint) (bool, error) { - // If there is a pre-release on the version but the constraint isn't looking - // for them assume that pre-releases are not compatible. See issue 21 for - // more details. - if v.Prerelease() != "" && c.con.Prerelease() == "" { - return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) - } - - // This less than handles prereleases - if v.LessThan(c.con) { - return false, fmt.Errorf("%s is less than %s", v, c.orig) - } - - var eq bool - - // ^ when the major > 0 is >=x.y.z < x+1 - if c.con.Major() > 0 || c.minorDirty { - - // ^ has to be within a major range for > 0. Everything less than was - // filtered out with the LessThan call above. This filters out those - // that greater but not within the same major range. - eq = v.Major() == c.con.Major() - if eq { - return true, nil - } - return false, fmt.Errorf("%s does not have same major version as %s", v, c.orig) - } - - // ^ when the major is 0 and minor > 0 is >=0.y.z < 0.y+1 - if c.con.Major() == 0 && v.Major() > 0 { - return false, fmt.Errorf("%s does not have same major version as %s", v, c.orig) - } - // If the con Minor is > 0 it is not dirty - if c.con.Minor() > 0 || c.patchDirty { - eq = v.Minor() == c.con.Minor() - if eq { - return true, nil - } - return false, fmt.Errorf("%s does not have same minor version as %s. Expected minor versions to match when constraint major version is 0", v, c.orig) - } - // ^ when the minor is 0 and minor > 0 is =0.0.z - if c.con.Minor() == 0 && v.Minor() > 0 { - return false, fmt.Errorf("%s does not have same minor version as %s", v, c.orig) - } - - // At this point the major is 0 and the minor is 0 and not dirty. The patch - // is not dirty so we need to check if they are equal. If they are not equal - eq = c.con.Patch() == v.Patch() - if eq { - return true, nil - } - return false, fmt.Errorf("%s does not equal %s. Expect version and constraint to equal when major and minor versions are 0", v, c.orig) -} - -func isX(x string) bool { - switch x { - case "x", "*", "X": - return true - default: - return false - } -} - -func rewriteRange(i string) string { - m := constraintRangeRegex.FindAllStringSubmatch(i, -1) - if m == nil { - return i - } - o := i - for _, v := range m { - t := fmt.Sprintf(">= %s, <= %s ", v[1], v[11]) - o = strings.Replace(o, v[0], t, 1) - } - - return o -} diff --git a/vendor/github.com/Masterminds/semver/v3/doc.go b/vendor/github.com/Masterminds/semver/v3/doc.go deleted file mode 100644 index 74f97ca..0000000 --- a/vendor/github.com/Masterminds/semver/v3/doc.go +++ /dev/null @@ -1,184 +0,0 @@ -/* -Package semver provides the ability to work with Semantic Versions (http://semver.org) in Go. - -Specifically it provides the ability to: - - - Parse semantic versions - - Sort semantic versions - - Check if a semantic version fits within a set of constraints - - Optionally work with a `v` prefix - -# Parsing Semantic Versions - -There are two functions that can parse semantic versions. The `StrictNewVersion` -function only parses valid version 2 semantic versions as outlined in the -specification. The `NewVersion` function attempts to coerce a version into a -semantic version and parse it. For example, if there is a leading v or a version -listed without all 3 parts (e.g. 1.2) it will attempt to coerce it into a valid -semantic version (e.g., 1.2.0). In both cases a `Version` object is returned -that can be sorted, compared, and used in constraints. - -When parsing a version an optional error can be returned if there is an issue -parsing the version. For example, - - v, err := semver.NewVersion("1.2.3-beta.1+b345") - -The version object has methods to get the parts of the version, compare it to -other versions, convert the version back into a string, and get the original -string. For more details please see the documentation -at https://godoc.org/github.com/Masterminds/semver. - -# Sorting Semantic Versions - -A set of versions can be sorted using the `sort` package from the standard library. -For example, - - raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",} - vs := make([]*semver.Version, len(raw)) - for i, r := range raw { - v, err := semver.NewVersion(r) - if err != nil { - t.Errorf("Error parsing version: %s", err) - } - - vs[i] = v - } - - sort.Sort(semver.Collection(vs)) - -# Checking Version Constraints and Comparing Versions - -There are two methods for comparing versions. One uses comparison methods on -`Version` instances and the other is using Constraints. There are some important -differences to notes between these two methods of comparison. - - 1. When two versions are compared using functions such as `Compare`, `LessThan`, - and others it will follow the specification and always include prereleases - within the comparison. It will provide an answer valid with the comparison - spec section at https://semver.org/#spec-item-11 - 2. When constraint checking is used for checks or validation it will follow a - different set of rules that are common for ranges with tools like npm/js - and Rust/Cargo. This includes considering prereleases to be invalid if the - ranges does not include on. If you want to have it include pre-releases a - simple solution is to include `-0` in your range. - 3. Constraint ranges can have some complex rules including the shorthard use of - ~ and ^. For more details on those see the options below. - -There are differences between the two methods or checking versions because the -comparison methods on `Version` follow the specification while comparison ranges -are not part of the specification. Different packages and tools have taken it -upon themselves to come up with range rules. This has resulted in differences. -For example, npm/js and Cargo/Rust follow similar patterns which PHP has a -different pattern for ^. The comparison features in this package follow the -npm/js and Cargo/Rust lead because applications using it have followed similar -patters with their versions. - -Checking a version against version constraints is one of the most featureful -parts of the package. - - c, err := semver.NewConstraint(">= 1.2.3") - if err != nil { - // Handle constraint not being parsable. - } - - v, err := semver.NewVersion("1.3") - if err != nil { - // Handle version not being parsable. - } - // Check if the version meets the constraints. The a variable will be true. - a := c.Check(v) - -# Basic Comparisons - -There are two elements to the comparisons. First, a comparison string is a list -of comma or space separated AND comparisons. These are then separated by || (OR) -comparisons. For example, `">= 1.2 < 3.0.0 || >= 4.2.3"` is looking for a -comparison that's greater than or equal to 1.2 and less than 3.0.0 or is -greater than or equal to 4.2.3. This can also be written as -`">= 1.2, < 3.0.0 || >= 4.2.3"` - -The basic comparisons are: - - - `=`: equal (aliased to no operator) - - `!=`: not equal - - `>`: greater than - - `<`: less than - - `>=`: greater than or equal to - - `<=`: less than or equal to - -# Hyphen Range Comparisons - -There are multiple methods to handle ranges and the first is hyphens ranges. -These look like: - - - `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5` - - `2.3.4 - 4.5` which is equivalent to `>= 2.3.4 <= 4.5` - -# Wildcards In Comparisons - -The `x`, `X`, and `*` characters can be used as a wildcard character. This works -for all comparison operators. When used on the `=` operator it falls -back to the tilde operation. For example, - - - `1.2.x` is equivalent to `>= 1.2.0 < 1.3.0` - - `>= 1.2.x` is equivalent to `>= 1.2.0` - - `<= 2.x` is equivalent to `<= 3` - - `*` is equivalent to `>= 0.0.0` - -Tilde Range Comparisons (Patch) - -The tilde (`~`) comparison operator is for patch level ranges when a minor -version is specified and major level changes when the minor number is missing. -For example, - - - `~1.2.3` is equivalent to `>= 1.2.3 < 1.3.0` - - `~1` is equivalent to `>= 1, < 2` - - `~2.3` is equivalent to `>= 2.3 < 2.4` - - `~1.2.x` is equivalent to `>= 1.2.0 < 1.3.0` - - `~1.x` is equivalent to `>= 1 < 2` - -Caret Range Comparisons (Major) - -The caret (`^`) comparison operator is for major level changes once a stable -(1.0.0) release has occurred. Prior to a 1.0.0 release the minor versions acts -as the API stability level. This is useful when comparisons of API versions as a -major change is API breaking. For example, - - - `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0` - - `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0` - - `^2.3` is equivalent to `>= 2.3, < 3` - - `^2.x` is equivalent to `>= 2.0.0, < 3` - - `^0.2.3` is equivalent to `>=0.2.3 <0.3.0` - - `^0.2` is equivalent to `>=0.2.0 <0.3.0` - - `^0.0.3` is equivalent to `>=0.0.3 <0.0.4` - - `^0.0` is equivalent to `>=0.0.0 <0.1.0` - - `^0` is equivalent to `>=0.0.0 <1.0.0` - -# Validation - -In addition to testing a version against a constraint, a version can be validated -against a constraint. When validation fails a slice of errors containing why a -version didn't meet the constraint is returned. For example, - - c, err := semver.NewConstraint("<= 1.2.3, >= 1.4") - if err != nil { - // Handle constraint not being parseable. - } - - v, _ := semver.NewVersion("1.3") - if err != nil { - // Handle version not being parseable. - } - - // Validate a version against a constraint. - a, msgs := c.Validate(v) - // a is false - for _, m := range msgs { - fmt.Println(m) - - // Loops over the errors which would read - // "1.3 is greater than 1.2.3" - // "1.3 is less than 1.4" - } -*/ -package semver diff --git a/vendor/github.com/Masterminds/semver/v3/version.go b/vendor/github.com/Masterminds/semver/v3/version.go deleted file mode 100644 index 7c4bed3..0000000 --- a/vendor/github.com/Masterminds/semver/v3/version.go +++ /dev/null @@ -1,639 +0,0 @@ -package semver - -import ( - "bytes" - "database/sql/driver" - "encoding/json" - "errors" - "fmt" - "regexp" - "strconv" - "strings" -) - -// The compiled version of the regex created at init() is cached here so it -// only needs to be created once. -var versionRegex *regexp.Regexp - -var ( - // ErrInvalidSemVer is returned a version is found to be invalid when - // being parsed. - ErrInvalidSemVer = errors.New("Invalid Semantic Version") - - // ErrEmptyString is returned when an empty string is passed in for parsing. - ErrEmptyString = errors.New("Version string empty") - - // ErrInvalidCharacters is returned when invalid characters are found as - // part of a version - ErrInvalidCharacters = errors.New("Invalid characters in version") - - // ErrSegmentStartsZero is returned when a version segment starts with 0. - // This is invalid in SemVer. - ErrSegmentStartsZero = errors.New("Version segment starts with 0") - - // ErrInvalidMetadata is returned when the metadata is an invalid format - ErrInvalidMetadata = errors.New("Invalid Metadata string") - - // ErrInvalidPrerelease is returned when the pre-release is an invalid format - ErrInvalidPrerelease = errors.New("Invalid Prerelease string") -) - -// semVerRegex is the regular expression used to parse a semantic version. -const semVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` + - `(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` + - `(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` - -// Version represents a single semantic version. -type Version struct { - major, minor, patch uint64 - pre string - metadata string - original string -} - -func init() { - versionRegex = regexp.MustCompile("^" + semVerRegex + "$") -} - -const ( - num string = "0123456789" - allowed string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-" + num -) - -// StrictNewVersion parses a given version and returns an instance of Version or -// an error if unable to parse the version. Only parses valid semantic versions. -// Performs checking that can find errors within the version. -// If you want to coerce a version such as 1 or 1.2 and parse it as the 1.x -// releases of semver did, use the NewVersion() function. -func StrictNewVersion(v string) (*Version, error) { - // Parsing here does not use RegEx in order to increase performance and reduce - // allocations. - - if len(v) == 0 { - return nil, ErrEmptyString - } - - // Split the parts into [0]major, [1]minor, and [2]patch,prerelease,build - parts := strings.SplitN(v, ".", 3) - if len(parts) != 3 { - return nil, ErrInvalidSemVer - } - - sv := &Version{ - original: v, - } - - // check for prerelease or build metadata - var extra []string - if strings.ContainsAny(parts[2], "-+") { - // Start with the build metadata first as it needs to be on the right - extra = strings.SplitN(parts[2], "+", 2) - if len(extra) > 1 { - // build metadata found - sv.metadata = extra[1] - parts[2] = extra[0] - } - - extra = strings.SplitN(parts[2], "-", 2) - if len(extra) > 1 { - // prerelease found - sv.pre = extra[1] - parts[2] = extra[0] - } - } - - // Validate the number segments are valid. This includes only having positive - // numbers and no leading 0's. - for _, p := range parts { - if !containsOnly(p, num) { - return nil, ErrInvalidCharacters - } - - if len(p) > 1 && p[0] == '0' { - return nil, ErrSegmentStartsZero - } - } - - // Extract the major, minor, and patch elements onto the returned Version - var err error - sv.major, err = strconv.ParseUint(parts[0], 10, 64) - if err != nil { - return nil, err - } - - sv.minor, err = strconv.ParseUint(parts[1], 10, 64) - if err != nil { - return nil, err - } - - sv.patch, err = strconv.ParseUint(parts[2], 10, 64) - if err != nil { - return nil, err - } - - // No prerelease or build metadata found so returning now as a fastpath. - if sv.pre == "" && sv.metadata == "" { - return sv, nil - } - - if sv.pre != "" { - if err = validatePrerelease(sv.pre); err != nil { - return nil, err - } - } - - if sv.metadata != "" { - if err = validateMetadata(sv.metadata); err != nil { - return nil, err - } - } - - return sv, nil -} - -// NewVersion parses a given version and returns an instance of Version or -// an error if unable to parse the version. If the version is SemVer-ish it -// attempts to convert it to SemVer. If you want to validate it was a strict -// semantic version at parse time see StrictNewVersion(). -func NewVersion(v string) (*Version, error) { - m := versionRegex.FindStringSubmatch(v) - if m == nil { - return nil, ErrInvalidSemVer - } - - sv := &Version{ - metadata: m[8], - pre: m[5], - original: v, - } - - var err error - sv.major, err = strconv.ParseUint(m[1], 10, 64) - if err != nil { - return nil, fmt.Errorf("Error parsing version segment: %s", err) - } - - if m[2] != "" { - sv.minor, err = strconv.ParseUint(strings.TrimPrefix(m[2], "."), 10, 64) - if err != nil { - return nil, fmt.Errorf("Error parsing version segment: %s", err) - } - } else { - sv.minor = 0 - } - - if m[3] != "" { - sv.patch, err = strconv.ParseUint(strings.TrimPrefix(m[3], "."), 10, 64) - if err != nil { - return nil, fmt.Errorf("Error parsing version segment: %s", err) - } - } else { - sv.patch = 0 - } - - // Perform some basic due diligence on the extra parts to ensure they are - // valid. - - if sv.pre != "" { - if err = validatePrerelease(sv.pre); err != nil { - return nil, err - } - } - - if sv.metadata != "" { - if err = validateMetadata(sv.metadata); err != nil { - return nil, err - } - } - - return sv, nil -} - -// New creates a new instance of Version with each of the parts passed in as -// arguments instead of parsing a version string. -func New(major, minor, patch uint64, pre, metadata string) *Version { - v := Version{ - major: major, - minor: minor, - patch: patch, - pre: pre, - metadata: metadata, - original: "", - } - - v.original = v.String() - - return &v -} - -// MustParse parses a given version and panics on error. -func MustParse(v string) *Version { - sv, err := NewVersion(v) - if err != nil { - panic(err) - } - return sv -} - -// String converts a Version object to a string. -// Note, if the original version contained a leading v this version will not. -// See the Original() method to retrieve the original value. Semantic Versions -// don't contain a leading v per the spec. Instead it's optional on -// implementation. -func (v Version) String() string { - var buf bytes.Buffer - - fmt.Fprintf(&buf, "%d.%d.%d", v.major, v.minor, v.patch) - if v.pre != "" { - fmt.Fprintf(&buf, "-%s", v.pre) - } - if v.metadata != "" { - fmt.Fprintf(&buf, "+%s", v.metadata) - } - - return buf.String() -} - -// Original returns the original value passed in to be parsed. -func (v *Version) Original() string { - return v.original -} - -// Major returns the major version. -func (v Version) Major() uint64 { - return v.major -} - -// Minor returns the minor version. -func (v Version) Minor() uint64 { - return v.minor -} - -// Patch returns the patch version. -func (v Version) Patch() uint64 { - return v.patch -} - -// Prerelease returns the pre-release version. -func (v Version) Prerelease() string { - return v.pre -} - -// Metadata returns the metadata on the version. -func (v Version) Metadata() string { - return v.metadata -} - -// originalVPrefix returns the original 'v' prefix if any. -func (v Version) originalVPrefix() string { - // Note, only lowercase v is supported as a prefix by the parser. - if v.original != "" && v.original[:1] == "v" { - return v.original[:1] - } - return "" -} - -// IncPatch produces the next patch version. -// If the current version does not have prerelease/metadata information, -// it unsets metadata and prerelease values, increments patch number. -// If the current version has any of prerelease or metadata information, -// it unsets both values and keeps current patch value -func (v Version) IncPatch() Version { - vNext := v - // according to http://semver.org/#spec-item-9 - // Pre-release versions have a lower precedence than the associated normal version. - // according to http://semver.org/#spec-item-10 - // Build metadata SHOULD be ignored when determining version precedence. - if v.pre != "" { - vNext.metadata = "" - vNext.pre = "" - } else { - vNext.metadata = "" - vNext.pre = "" - vNext.patch = v.patch + 1 - } - vNext.original = v.originalVPrefix() + "" + vNext.String() - return vNext -} - -// IncMinor produces the next minor version. -// Sets patch to 0. -// Increments minor number. -// Unsets metadata. -// Unsets prerelease status. -func (v Version) IncMinor() Version { - vNext := v - vNext.metadata = "" - vNext.pre = "" - vNext.patch = 0 - vNext.minor = v.minor + 1 - vNext.original = v.originalVPrefix() + "" + vNext.String() - return vNext -} - -// IncMajor produces the next major version. -// Sets patch to 0. -// Sets minor to 0. -// Increments major number. -// Unsets metadata. -// Unsets prerelease status. -func (v Version) IncMajor() Version { - vNext := v - vNext.metadata = "" - vNext.pre = "" - vNext.patch = 0 - vNext.minor = 0 - vNext.major = v.major + 1 - vNext.original = v.originalVPrefix() + "" + vNext.String() - return vNext -} - -// SetPrerelease defines the prerelease value. -// Value must not include the required 'hyphen' prefix. -func (v Version) SetPrerelease(prerelease string) (Version, error) { - vNext := v - if len(prerelease) > 0 { - if err := validatePrerelease(prerelease); err != nil { - return vNext, err - } - } - vNext.pre = prerelease - vNext.original = v.originalVPrefix() + "" + vNext.String() - return vNext, nil -} - -// SetMetadata defines metadata value. -// Value must not include the required 'plus' prefix. -func (v Version) SetMetadata(metadata string) (Version, error) { - vNext := v - if len(metadata) > 0 { - if err := validateMetadata(metadata); err != nil { - return vNext, err - } - } - vNext.metadata = metadata - vNext.original = v.originalVPrefix() + "" + vNext.String() - return vNext, nil -} - -// LessThan tests if one version is less than another one. -func (v *Version) LessThan(o *Version) bool { - return v.Compare(o) < 0 -} - -// GreaterThan tests if one version is greater than another one. -func (v *Version) GreaterThan(o *Version) bool { - return v.Compare(o) > 0 -} - -// Equal tests if two versions are equal to each other. -// Note, versions can be equal with different metadata since metadata -// is not considered part of the comparable version. -func (v *Version) Equal(o *Version) bool { - return v.Compare(o) == 0 -} - -// Compare compares this version to another one. It returns -1, 0, or 1 if -// the version smaller, equal, or larger than the other version. -// -// Versions are compared by X.Y.Z. Build metadata is ignored. Prerelease is -// lower than the version without a prerelease. Compare always takes into account -// prereleases. If you want to work with ranges using typical range syntaxes that -// skip prereleases if the range is not looking for them use constraints. -func (v *Version) Compare(o *Version) int { - // Compare the major, minor, and patch version for differences. If a - // difference is found return the comparison. - if d := compareSegment(v.Major(), o.Major()); d != 0 { - return d - } - if d := compareSegment(v.Minor(), o.Minor()); d != 0 { - return d - } - if d := compareSegment(v.Patch(), o.Patch()); d != 0 { - return d - } - - // At this point the major, minor, and patch versions are the same. - ps := v.pre - po := o.Prerelease() - - if ps == "" && po == "" { - return 0 - } - if ps == "" { - return 1 - } - if po == "" { - return -1 - } - - return comparePrerelease(ps, po) -} - -// UnmarshalJSON implements JSON.Unmarshaler interface. -func (v *Version) UnmarshalJSON(b []byte) error { - var s string - if err := json.Unmarshal(b, &s); err != nil { - return err - } - temp, err := NewVersion(s) - if err != nil { - return err - } - v.major = temp.major - v.minor = temp.minor - v.patch = temp.patch - v.pre = temp.pre - v.metadata = temp.metadata - v.original = temp.original - return nil -} - -// MarshalJSON implements JSON.Marshaler interface. -func (v Version) MarshalJSON() ([]byte, error) { - return json.Marshal(v.String()) -} - -// UnmarshalText implements the encoding.TextUnmarshaler interface. -func (v *Version) UnmarshalText(text []byte) error { - temp, err := NewVersion(string(text)) - if err != nil { - return err - } - - *v = *temp - - return nil -} - -// MarshalText implements the encoding.TextMarshaler interface. -func (v Version) MarshalText() ([]byte, error) { - return []byte(v.String()), nil -} - -// Scan implements the SQL.Scanner interface. -func (v *Version) Scan(value interface{}) error { - var s string - s, _ = value.(string) - temp, err := NewVersion(s) - if err != nil { - return err - } - v.major = temp.major - v.minor = temp.minor - v.patch = temp.patch - v.pre = temp.pre - v.metadata = temp.metadata - v.original = temp.original - return nil -} - -// Value implements the Driver.Valuer interface. -func (v Version) Value() (driver.Value, error) { - return v.String(), nil -} - -func compareSegment(v, o uint64) int { - if v < o { - return -1 - } - if v > o { - return 1 - } - - return 0 -} - -func comparePrerelease(v, o string) int { - // split the prelease versions by their part. The separator, per the spec, - // is a . - sparts := strings.Split(v, ".") - oparts := strings.Split(o, ".") - - // Find the longer length of the parts to know how many loop iterations to - // go through. - slen := len(sparts) - olen := len(oparts) - - l := slen - if olen > slen { - l = olen - } - - // Iterate over each part of the prereleases to compare the differences. - for i := 0; i < l; i++ { - // Since the lentgh of the parts can be different we need to create - // a placeholder. This is to avoid out of bounds issues. - stemp := "" - if i < slen { - stemp = sparts[i] - } - - otemp := "" - if i < olen { - otemp = oparts[i] - } - - d := comparePrePart(stemp, otemp) - if d != 0 { - return d - } - } - - // Reaching here means two versions are of equal value but have different - // metadata (the part following a +). They are not identical in string form - // but the version comparison finds them to be equal. - return 0 -} - -func comparePrePart(s, o string) int { - // Fastpath if they are equal - if s == o { - return 0 - } - - // When s or o are empty we can use the other in an attempt to determine - // the response. - if s == "" { - if o != "" { - return -1 - } - return 1 - } - - if o == "" { - if s != "" { - return 1 - } - return -1 - } - - // When comparing strings "99" is greater than "103". To handle - // cases like this we need to detect numbers and compare them. According - // to the semver spec, numbers are always positive. If there is a - at the - // start like -99 this is to be evaluated as an alphanum. numbers always - // have precedence over alphanum. Parsing as Uints because negative numbers - // are ignored. - - oi, n1 := strconv.ParseUint(o, 10, 64) - si, n2 := strconv.ParseUint(s, 10, 64) - - // The case where both are strings compare the strings - if n1 != nil && n2 != nil { - if s > o { - return 1 - } - return -1 - } else if n1 != nil { - // o is a string and s is a number - return -1 - } else if n2 != nil { - // s is a string and o is a number - return 1 - } - // Both are numbers - if si > oi { - return 1 - } - return -1 -} - -// Like strings.ContainsAny but does an only instead of any. -func containsOnly(s string, comp string) bool { - return strings.IndexFunc(s, func(r rune) bool { - return !strings.ContainsRune(comp, r) - }) == -1 -} - -// From the spec, "Identifiers MUST comprise only -// ASCII alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty. -// Numeric identifiers MUST NOT include leading zeroes.". These segments can -// be dot separated. -func validatePrerelease(p string) error { - eparts := strings.Split(p, ".") - for _, p := range eparts { - if containsOnly(p, num) { - if len(p) > 1 && p[0] == '0' { - return ErrSegmentStartsZero - } - } else if !containsOnly(p, allowed) { - return ErrInvalidPrerelease - } - } - - return nil -} - -// From the spec, "Build metadata MAY be denoted by -// appending a plus sign and a series of dot separated identifiers immediately -// following the patch or pre-release version. Identifiers MUST comprise only -// ASCII alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty." -func validateMetadata(m string) error { - eparts := strings.Split(m, ".") - for _, p := range eparts { - if !containsOnly(p, allowed) { - return ErrInvalidMetadata - } - } - return nil -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 9c5f4cf..cef0811 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,6 +1,3 @@ -# github.com/Masterminds/semver/v3 v3.2.1 -## explicit; go 1.18 -github.com/Masterminds/semver/v3 # github.com/davecgh/go-spew v1.1.1 ## explicit github.com/davecgh/go-spew/spew From 75aeeee95b8f2d2a6747d729220648d7b5946033 Mon Sep 17 00:00:00 2001 From: Fabian Fulga Date: Fri, 9 Aug 2024 10:12:15 +0300 Subject: [PATCH 11/12] Add more common execution elements to common --- execution/common/commands.go | 21 +++++++++++++++++++++ execution/execution.go | 20 +++++++++++++------- execution/v0.1.0/execution.go | 21 --------------------- execution/v0.1.0/execution_test.go | 6 +++--- execution/v0.1.1/execution.go | 21 --------------------- execution/v0.1.1/execution_test.go | 6 +++--- 6 files changed, 40 insertions(+), 55 deletions(-) diff --git a/execution/common/commands.go b/execution/common/commands.go index 3e18b44..d95d928 100644 --- a/execution/common/commands.go +++ b/execution/common/commands.go @@ -17,10 +17,12 @@ package common import ( "bytes" "encoding/json" + "errors" "fmt" "io" "os" + gErrors "github.com/cloudbase/garm-provider-common/errors" "github.com/cloudbase/garm-provider-common/params" "github.com/mattn/go-isatty" ) @@ -38,6 +40,13 @@ const ( GetVersionCommand ExecutionCommand = "GetVersion" ) +const ( + // ExitCodeNotFound is an exit code that indicates a Not Found error + ExitCodeNotFound int = 30 + // ExitCodeDuplicate is an exit code that indicates a duplicate error + ExitCodeDuplicate int = 31 +) + func GetBoostrapParamsFromStdin(c ExecutionCommand) (params.BootstrapInstance, error) { var bootstrapParams params.BootstrapInstance if c == CreateInstanceCommand { @@ -68,3 +77,15 @@ func GetBoostrapParamsFromStdin(c ExecutionCommand) (params.BootstrapInstance, e // If the command is not CreateInstance, we don't need to read from stdin return params.BootstrapInstance{}, nil } + +func ResolveErrorToExitCode(err error) int { + if err != nil { + if errors.Is(err, gErrors.ErrNotFound) { + return ExitCodeNotFound + } else if errors.Is(err, gErrors.ErrDuplicateEntity) { + return ExitCodeDuplicate + } + return 1 + } + return 0 +} diff --git a/execution/execution.go b/execution/execution.go index db9141a..1bec58d 100644 --- a/execution/execution.go +++ b/execution/execution.go @@ -24,9 +24,11 @@ import ( ) type Environment struct { - EnvironmentV010 executionv010.EnvironmentV010 - EnvironmentV011 executionv011.EnvironmentV011 - InterfaceVersion string + EnvironmentV010 executionv010.EnvironmentV010 + EnvironmentV011 executionv011.EnvironmentV011 + InterfaceVersion string + ProviderConfigFile string + ControllerID string } func GetEnvironment() (Environment, error) { @@ -39,8 +41,10 @@ func GetEnvironment() (Environment, error) { return Environment{}, err } return Environment{ - EnvironmentV010: env, - InterfaceVersion: interfaceVersion, + EnvironmentV010: env, + ProviderConfigFile: env.ProviderConfigFile, + ControllerID: env.ControllerID, + InterfaceVersion: interfaceVersion, }, nil case common.Version011: env, err := executionv011.GetEnvironment() @@ -48,8 +52,10 @@ func GetEnvironment() (Environment, error) { return Environment{}, err } return Environment{ - EnvironmentV011: env, - InterfaceVersion: interfaceVersion, + EnvironmentV011: env, + ProviderConfigFile: env.ProviderConfigFile, + ControllerID: env.ControllerID, + InterfaceVersion: interfaceVersion, }, nil default: return Environment{}, fmt.Errorf("unsupported interface version: %s", interfaceVersion) diff --git a/execution/v0.1.0/execution.go b/execution/v0.1.0/execution.go index c209458..ef0e66b 100644 --- a/execution/v0.1.0/execution.go +++ b/execution/v0.1.0/execution.go @@ -17,36 +17,15 @@ package executionv010 import ( "context" "encoding/json" - "errors" "fmt" "os" "golang.org/x/mod/semver" - gErrors "github.com/cloudbase/garm-provider-common/errors" common "github.com/cloudbase/garm-provider-common/execution/common" "github.com/cloudbase/garm-provider-common/params" ) -const ( - // ExitCodeNotFound is an exit code that indicates a Not Found error - ExitCodeNotFound int = 30 - // ExitCodeDuplicate is an exit code that indicates a duplicate error - ExitCodeDuplicate int = 31 -) - -func ResolveErrorToExitCode(err error) int { - if err != nil { - if errors.Is(err, gErrors.ErrNotFound) { - return ExitCodeNotFound - } else if errors.Is(err, gErrors.ErrDuplicateEntity) { - return ExitCodeDuplicate - } - return 1 - } - return 0 -} - func GetEnvironment() (EnvironmentV010, error) { env := EnvironmentV010{ Command: common.ExecutionCommand(os.Getenv("GARM_COMMAND")), diff --git a/execution/v0.1.0/execution_test.go b/execution/v0.1.0/execution_test.go index e32d66a..a859b1f 100644 --- a/execution/v0.1.0/execution_test.go +++ b/execution/v0.1.0/execution_test.go @@ -102,12 +102,12 @@ func TestResolveErrorToExitCode(t *testing.T) { { name: "not found error", err: gErrors.ErrNotFound, - code: ExitCodeNotFound, + code: common.ExitCodeNotFound, }, { name: "duplicate entity error", err: gErrors.ErrDuplicateEntity, - code: ExitCodeDuplicate, + code: common.ExitCodeDuplicate, }, { name: "other error", @@ -118,7 +118,7 @@ func TestResolveErrorToExitCode(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - code := ResolveErrorToExitCode(tc.err) + code := common.ResolveErrorToExitCode(tc.err) require.Equal(t, tc.code, code) }) } diff --git a/execution/v0.1.1/execution.go b/execution/v0.1.1/execution.go index 95132b0..d69ef20 100644 --- a/execution/v0.1.1/execution.go +++ b/execution/v0.1.1/execution.go @@ -17,36 +17,15 @@ package executionv011 import ( "context" "encoding/json" - "errors" "fmt" "os" "golang.org/x/mod/semver" - gErrors "github.com/cloudbase/garm-provider-common/errors" common "github.com/cloudbase/garm-provider-common/execution/common" "github.com/cloudbase/garm-provider-common/params" ) -const ( - // ExitCodeNotFound is an exit code that indicates a Not Found error - ExitCodeNotFound int = 30 - // ExitCodeDuplicate is an exit code that indicates a duplicate error - ExitCodeDuplicate int = 31 -) - -func ResolveErrorToExitCode(err error) int { - if err != nil { - if errors.Is(err, gErrors.ErrNotFound) { - return ExitCodeNotFound - } else if errors.Is(err, gErrors.ErrDuplicateEntity) { - return ExitCodeDuplicate - } - return 1 - } - return 0 -} - func GetEnvironment() (EnvironmentV011, error) { env := EnvironmentV011{ Command: common.ExecutionCommand(os.Getenv("GARM_COMMAND")), diff --git a/execution/v0.1.1/execution_test.go b/execution/v0.1.1/execution_test.go index 6f6f026..f36250e 100644 --- a/execution/v0.1.1/execution_test.go +++ b/execution/v0.1.1/execution_test.go @@ -117,12 +117,12 @@ func TestResolveErrorToExitCode(t *testing.T) { { name: "not found error", err: gErrors.ErrNotFound, - code: ExitCodeNotFound, + code: common.ExitCodeNotFound, }, { name: "duplicate entity error", err: gErrors.ErrDuplicateEntity, - code: ExitCodeDuplicate, + code: common.ExitCodeDuplicate, }, { name: "other error", @@ -133,7 +133,7 @@ func TestResolveErrorToExitCode(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - code := ResolveErrorToExitCode(tc.err) + code := common.ResolveErrorToExitCode(tc.err) require.Equal(t, tc.code, code) }) } From 5f6acefeea896acdd792c86c3649f210843d7e15 Mon Sep 17 00:00:00 2001 From: Fabian Fulga Date: Fri, 9 Aug 2024 13:42:35 +0300 Subject: [PATCH 12/12] Add common execution Run --- execution/execution.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/execution/execution.go b/execution/execution.go index 1bec58d..a6da559 100644 --- a/execution/execution.go +++ b/execution/execution.go @@ -15,6 +15,7 @@ package execution import ( + "context" "fmt" "os" @@ -23,6 +24,11 @@ import ( executionv011 "github.com/cloudbase/garm-provider-common/execution/v0.1.1" ) +type ExternalProvider interface { + executionv010.ExternalProvider + executionv011.ExternalProvider +} + type Environment struct { EnvironmentV010 executionv010.EnvironmentV010 EnvironmentV011 executionv011.EnvironmentV011 @@ -61,3 +67,14 @@ func GetEnvironment() (Environment, error) { return Environment{}, fmt.Errorf("unsupported interface version: %s", interfaceVersion) } } + +func Run(ctx context.Context, provider ExternalProvider, env Environment) (string, error) { + switch env.InterfaceVersion { + case common.Version010: + return executionv010.Run(ctx, provider, env.EnvironmentV010) + case common.Version011: + return executionv011.Run(ctx, provider, env.EnvironmentV011) + default: + return "", fmt.Errorf("unsupported interface version: %s", env.InterfaceVersion) + } +}