diff options
author | Martin Polden <mpolden@mpolden.no> | 2022-02-24 10:35:42 +0100 |
---|---|---|
committer | Martin Polden <mpolden@mpolden.no> | 2022-02-24 10:44:11 +0100 |
commit | bee22704f41a52588d47c821e196ad98e83445c0 (patch) | |
tree | f842bc6f59f7262b7092477262d3b5ee07c3b3d4 /client | |
parent | 00f177dc5cb9162fc6a15c245d68045c5c69fcbe (diff) |
Print error if CLI version is incompatible
Diffstat (limited to 'client')
-rw-r--r-- | client/go/cmd/helpers.go | 17 | ||||
-rw-r--r-- | client/go/cmd/log_test.go | 19 | ||||
-rw-r--r-- | client/go/vespa/target.go | 36 | ||||
-rw-r--r-- | client/go/vespa/target_test.go | 23 |
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) |