summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2022-02-24 10:35:42 +0100
committerMartin Polden <mpolden@mpolden.no>2022-02-24 10:44:11 +0100
commitbee22704f41a52588d47c821e196ad98e83445c0 (patch)
treef842bc6f59f7262b7092477262d3b5ee07c3b3d4 /client
parent00f177dc5cb9162fc6a15c245d68045c5c69fcbe (diff)
Print error if CLI version is incompatible
Diffstat (limited to 'client')
-rw-r--r--client/go/cmd/helpers.go17
-rw-r--r--client/go/cmd/log_test.go19
-rw-r--r--client/go/vespa/target.go36
-rw-r--r--client/go/vespa/target_test.go23
4 files changed, 95 insertions, 0 deletions
diff --git a/client/go/cmd/helpers.go b/client/go/cmd/helpers.go
index 03bedd93fc2..00a57e6cfa7 100644
--- a/client/go/cmd/helpers.go
+++ b/client/go/cmd/helpers.go
@@ -13,6 +13,8 @@ import (
"strings"
"time"
+ "github.com/vespa-engine/vespa/client/go/build"
+ "github.com/vespa-engine/vespa/client/go/version"
"github.com/vespa-engine/vespa/client/go/vespa"
)
@@ -147,6 +149,21 @@ func getApiURL() string {
}
func getTarget() (vespa.Target, error) {
+ clientVersion, err := version.Parse(build.Version)
+ if err != nil {
+ return nil, err
+ }
+ target, err := createTarget()
+ if err != nil {
+ return nil, err
+ }
+ if err := target.CheckVersion(clientVersion); err != nil {
+ printErrHint(err, "This is not a fatal error, but this version may not work as expected", "Try 'vespa version' to check for a new version")
+ }
+ return target, nil
+}
+
+func createTarget() (vespa.Target, error) {
targetType, err := getTargetType()
if err != nil {
return nil, err
diff --git a/client/go/cmd/log_test.go b/client/go/cmd/log_test.go
index 4b32927ca17..9d895d1f244 100644
--- a/client/go/cmd/log_test.go
+++ b/client/go/cmd/log_test.go
@@ -6,6 +6,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
+ "github.com/vespa-engine/vespa/client/go/build"
)
func TestLog(t *testing.T) {
@@ -26,3 +27,21 @@ func TestLog(t *testing.T) {
_, errOut := execute(command{homeDir: homeDir, args: []string{"log", "--from", "2021-09-27T13:12:49Z", "--to", "2021-09-27T13:15:00", "1h"}}, t, httpClient)
assert.Equal(t, "Error: invalid period: cannot combine --from/--to with relative value: 1h\n", errOut)
}
+
+func TestLogOldClient(t *testing.T) {
+ buildVersion := build.Version
+ build.Version = "7.0.0"
+ homeDir := filepath.Join(t.TempDir(), ".vespa")
+ pkgDir := mockApplicationPackage(t, false)
+ httpClient := &mockHttpClient{}
+ httpClient.NextResponse(200, `{"minVersion": "8.0.0"}`)
+ execute(command{homeDir: homeDir, args: []string{"config", "set", "application", "t1.a1.i1"}}, t, httpClient)
+ execute(command{homeDir: homeDir, args: []string{"config", "set", "target", "cloud"}}, t, httpClient)
+ execute(command{homeDir: homeDir, args: []string{"api-key"}}, t, httpClient)
+ execute(command{homeDir: homeDir, args: []string{"cert", pkgDir}}, t, httpClient)
+ out, errOut := execute(command{homeDir: homeDir, args: []string{"log"}}, t, httpClient)
+ assert.Equal(t, "", out)
+ expected := "Error: client version 7.0.0 is less than the minimum supported version: 8.0.0\nHint: This is not a fatal error, but this version may not work as expected\nHint: Try 'vespa version' to check for a new version\n"
+ assert.Equal(t, expected, errOut)
+ build.Version = buildVersion
+}
diff --git a/client/go/vespa/target.go b/client/go/vespa/target.go
index 6ccfa8b8c6c..ae4934d788d 100644
--- a/client/go/vespa/target.go
+++ b/client/go/vespa/target.go
@@ -20,6 +20,7 @@ import (
"github.com/vespa-engine/vespa/client/go/auth0"
"github.com/vespa-engine/vespa/client/go/util"
+ "github.com/vespa-engine/vespa/client/go/version"
)
const (
@@ -54,6 +55,9 @@ type Target interface {
// SignRequest signs request with given keyID as required by the implementation of this target.
SignRequest(request *http.Request, keyID string) error
+
+ // CheckVersion verifies whether clientVersion is compatible with this target.
+ CheckVersion(clientVersion version.Version) error
}
// TLSOptions configures the certificate to use for service requests.
@@ -88,6 +92,8 @@ type customTarget struct {
func (t *customTarget) SignRequest(req *http.Request, sigKeyId string) error { return nil }
+func (t *customTarget) CheckVersion(version version.Version) error { return nil }
+
// Do sends request to this service. Any required authentication happens automatically.
func (s *Service) Do(request *http.Request, timeout time.Duration) (*http.Response, error) {
if s.TLSOptions.KeyPair.Certificate != nil {
@@ -290,6 +296,36 @@ func (t *cloudTarget) SignRequest(req *http.Request, sigKeyId string) error {
return nil
}
+func (t *cloudTarget) CheckVersion(clientVersion version.Version) error {
+ if clientVersion.IsZero() { // development version is always fine
+ return nil
+ }
+ req, err := http.NewRequest("GET", fmt.Sprintf("%s/cli/v1/", t.apiURL), nil)
+ if err != nil {
+ return err
+ }
+ response, err := util.HttpDo(req, 10*time.Second, "")
+ if err != nil {
+ return err
+ }
+ defer response.Body.Close()
+ var cliResponse struct {
+ MinVersion string `json:"minVersion"`
+ }
+ dec := json.NewDecoder(response.Body)
+ if err := dec.Decode(&cliResponse); err != nil {
+ return err
+ }
+ minVersion, err := version.Parse(cliResponse.MinVersion)
+ if err != nil {
+ return err
+ }
+ if clientVersion.Less(minVersion) {
+ return fmt.Errorf("client version %s is less than the minimum supported version: %s", clientVersion, minVersion)
+ }
+ return nil
+}
+
func (t *cloudTarget) addAuth0AccessToken(request *http.Request) error {
a, err := auth0.GetAuth0(t.authConfigPath, t.systemName, t.apiURL)
if err != nil {
diff --git a/client/go/vespa/target_test.go b/client/go/vespa/target_test.go
index 0cfe9f1962c..5aa20b22465 100644
--- a/client/go/vespa/target_test.go
+++ b/client/go/vespa/target_test.go
@@ -13,6 +13,7 @@ import (
"time"
"github.com/stretchr/testify/assert"
+ "github.com/vespa-engine/vespa/client/go/version"
)
type mockVespaApi struct {
@@ -22,6 +23,9 @@ type mockVespaApi struct {
func (v *mockVespaApi) mockVespaHandler(w http.ResponseWriter, req *http.Request) {
switch req.URL.Path {
+ case "/cli/v1/":
+ response := `{"minVersion":"8.0.0"}`
+ w.Write([]byte(response))
case "/application/v4/tenant/t1/application/a1/instance/i1/environment/dev/region/us-north-1":
response := "{}"
if v.deploymentConverged {
@@ -137,6 +141,25 @@ func TestLog(t *testing.T) {
assert.Equal(t, expected, buf.String())
}
+func TestCheckVersion(t *testing.T) {
+ vc := mockVespaApi{}
+ srv := httptest.NewServer(http.HandlerFunc(vc.mockVespaHandler))
+ defer srv.Close()
+
+ target := createCloudTarget(t, srv.URL, ioutil.Discard)
+ assert.Nil(t, target.CheckVersion(mustVersion("8.0.0")))
+ assert.Nil(t, target.CheckVersion(mustVersion("8.1.0")))
+ assert.NotNil(t, target.CheckVersion(mustVersion("7.0.0")))
+}
+
+func mustVersion(s string) version.Version {
+ v, err := version.Parse(s)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
func createCloudTarget(t *testing.T, url string, logWriter io.Writer) Target {
kp, err := CreateKeyPair()
assert.Nil(t, err)