From 97c03e871a0a6fb7f91e9a7fc010c8a43d2d497d Mon Sep 17 00:00:00 2001 From: Donatas Kucinskas <donce.lt@gmail.com> Date: Fri, 26 Apr 2019 11:49:08 +0300 Subject: [PATCH 1/2] Add CLI command for retrieving NAT status --- cmd/commands/cli/command.go | 16 +++++++++++++ tequilapi/client/client.go | 13 ++++++++++ tequilapi/client/client_test.go | 42 +++++++++++++++++++++++++++++++++ tequilapi/client/dto.go | 6 +++++ 4 files changed, 77 insertions(+) diff --git a/cmd/commands/cli/command.go b/cmd/commands/cli/command.go index 456215ecad..be259ad961 100644 --- a/cmd/commands/cli/command.go +++ b/cmd/commands/cli/command.go @@ -144,6 +144,7 @@ func (c *cliApp) handleActions(line string) { {"help", c.help}, {"status", c.status}, {"healthcheck", c.healthcheck}, + {"nat", c.natStatus}, {"ip", c.ip}, {"disconnect", c.disconnect}, {"stop", c.stopClient}, @@ -449,6 +450,20 @@ func (c *cliApp) healthcheck() { info(buildString) } +func (c *cliApp) natStatus() { + status, err := c.tequilapi.NATStatus() + if err != nil { + warn("Failed to retrieve NAT traversal status:", err) + return + } + + if status.Error == "" { + infof("NAT traversal status: %q\n", status.Status) + } else { + infof("NAT traversal status: %q (error: %q)\n", status.Status, status.Error) + } +} + func (c *cliApp) proposals(filter string) { proposals := c.fetchProposals() c.fetchedProposals = proposals @@ -673,6 +688,7 @@ func newAutocompleter(tequilapi *tequilapi_client.Client, proposals []tequilapi_ ), readline.PcItem("status"), readline.PcItem("healthcheck"), + readline.PcItem("nat"), readline.PcItem("proposals"), readline.PcItem("ip"), readline.PcItem("disconnect"), diff --git a/tequilapi/client/client.go b/tequilapi/client/client.go index 0cec5e39bd..e12d7538bb 100644 --- a/tequilapi/client/client.go +++ b/tequilapi/client/client.go @@ -336,6 +336,19 @@ func (client *Client) ServiceStop(id string) error { return nil } +// NATStatus returns status of NAT traversal +func (client *Client) NATStatus() (NATStatusDTO, error) { + status := NATStatusDTO{} + + response, err := client.http.Get("nat/status", nil) + if err != nil { + return status, err + } + + err = parseResponseJSON(response, &status) + return status, err +} + // ServiceSessions returns all currently running sessions func (client *Client) ServiceSessions() (ServiceSessionListDTO, error) { sessions := ServiceSessionListDTO{} diff --git a/tequilapi/client/client_test.go b/tequilapi/client/client_test.go index f94a950875..45e0375dc1 100644 --- a/tequilapi/client/client_test.go +++ b/tequilapi/client/client_test.go @@ -21,6 +21,7 @@ import ( "errors" "io" "net/http" + "net/http/httptest" "strings" "testing" @@ -33,6 +34,37 @@ const errorMessage = ` } ` +func Test_NATStatus_ReturnsStatus(t *testing.T) { + httpClient := mockHTTPClient( + t, + http.MethodGet, + "/nat/status", + http.StatusOK, + `{"status": "failure", "error": "mock error"}`, + ) + client := Client{http: httpClient} + + status, err := client.NATStatus() + + assert.NoError(t, err) + assert.Equal(t, "failure", status.Status) + assert.Equal(t, "mock error", status.Error) +} + +func Test_NATStatus_ReturnsError(t *testing.T) { + httpClient := mockHTTPClient( + t, + http.MethodGet, + "/nat/status", + http.StatusInternalServerError, + ``, + ) + client := Client{http: httpClient} + + _, err := client.NATStatus() + assert.Error(t, err) +} + func TestConnectionErrorIsReturnedByClientInsteadOfDoubleParsing(t *testing.T) { responseBody := &trackingCloser{ Reader: strings.NewReader(errorMessage), @@ -58,6 +90,16 @@ func TestConnectionErrorIsReturnedByClientInsteadOfDoubleParsing(t *testing.T) { assert.True(t, responseBody.Closed) } +func mockHTTPClient(t *testing.T, method, url string, statusCode int, response string) httpClientInterface { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, method, r.Method) + assert.Equal(t, url, r.URL.Path) + w.Write([]byte(response)) + w.WriteHeader(statusCode) + })) + return newHTTPClient(server.URL, "", "") +} + type requestDoer func(req *http.Request) (*http.Response, error) func (f requestDoer) Do(req *http.Request) (*http.Response, error) { diff --git a/tequilapi/client/dto.go b/tequilapi/client/dto.go index 8532832e61..77f13a775b 100644 --- a/tequilapi/client/dto.go +++ b/tequilapi/client/dto.go @@ -159,3 +159,9 @@ type ServiceSessionDTO struct { type AccessPoliciesRequest struct { IDs []string `json:"ids"` } + +// NATStatusDTO gives information about NAT traversal success or failure +type NATStatusDTO struct { + Status string `json:"status"` + Error string `json:"error,omitempty"` +} From c1651837074c497dee1e2da8cceac65ab26f540d Mon Sep 17 00:00:00 2001 From: Donatas Kucinskas <donce.lt@gmail.com> Date: Fri, 26 Apr 2019 11:51:17 +0300 Subject: [PATCH 2/2] Refactor CLI actions to unify declaration --- cmd/commands/cli/command.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd/commands/cli/command.go b/cmd/commands/cli/command.go index be259ad961..bdde3da62b 100644 --- a/cmd/commands/cli/command.go +++ b/cmd/commands/cli/command.go @@ -154,15 +154,15 @@ func (c *cliApp) handleActions(line string) { command string handler func(argsString string) }{ - {command: "connect", handler: c.connect}, - {command: "unlock", handler: c.unlock}, - {command: "identities", handler: c.identities}, - {command: "payout", handler: c.payout}, - {command: "version", handler: c.version}, - {command: "license", handler: c.license}, - {command: "registration", handler: c.registration}, - {command: "proposals", handler: c.proposals}, - {command: "service", handler: c.service}, + {"connect", c.connect}, + {"unlock", c.unlock}, + {"identities", c.identities}, + {"payout", c.payout}, + {"version", c.version}, + {"license", c.license}, + {"registration", c.registration}, + {"proposals", c.proposals}, + {"service", c.service}, } for _, cmd := range staticCmds {