Parse values given to ?passing in the API

This PR fixes GH-2212 in the most backwards-compatible way I can think
of. If the user does not pass a value for `?passing`, it's assumed to be
true, which mirrors the current behavior. However, if the user passes
any value for passing, that value is parsed as a bool using strconv.

It's important to note that this is technically a breaking change.
Previously using `?passing=false` would return only passing nodes. While
this behavior is obviously incorrect, it was the previous behavior. We
should call this out very clearly in the CHANGELOG.
This commit is contained in:
Seth Vargo 2017-06-09 14:36:00 -04:00
parent 2f2131bab4
commit b20902b7e2
No known key found for this signature in database
GPG Key ID: C921994F9C27E0FF
2 changed files with 87 additions and 15 deletions

View File

@ -3,6 +3,7 @@ package agent
import (
"fmt"
"net/http"
"strconv"
"strings"
"github.com/hashicorp/consul/api"
@ -148,7 +149,22 @@ func (s *HTTPServer) HealthServiceNodes(resp http.ResponseWriter, req *http.Requ
// Filter to only passing if specified
if _, ok := params[api.HealthPassing]; ok {
out.Nodes = filterNonPassing(out.Nodes)
val := params.Get(api.HealthPassing)
// Backwards-compat to allow users to specify ?passing without a value. This
// should be removed in Consul 0.10.
if val == "" {
out.Nodes = filterNonPassing(out.Nodes)
} else {
filter, err := strconv.ParseBool(val)
if err != nil {
resp.WriteHeader(400)
fmt.Fprint(resp, "Invalid value for ?passing")
return nil, nil
}
if filter {
out.Nodes = filterNonPassing(out.Nodes)
}
}
}
// Translate addresses after filtering so we don't waste effort.

View File

@ -1,7 +1,9 @@
package agent
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"reflect"
@ -15,7 +17,7 @@ import (
func TestHealthChecksInState(t *testing.T) {
t.Parallel()
t.Run("", func(t *testing.T) {
t.Run("warning", func(t *testing.T) {
a := NewTestAgent(t.Name(), nil)
defer a.Shutdown()
@ -38,7 +40,7 @@ func TestHealthChecksInState(t *testing.T) {
})
})
t.Run("", func(t *testing.T) {
t.Run("passing", func(t *testing.T) {
a := NewTestAgent(t.Name(), nil)
defer a.Shutdown()
@ -612,20 +614,74 @@ func TestHealthServiceNodes_PassingFilter(t *testing.T) {
t.Fatalf("err: %v", err)
}
req, _ := http.NewRequest("GET", "/v1/health/service/consul?passing", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.HealthServiceNodes(resp, req)
if err != nil {
t.Fatalf("err: %v", err)
}
t.Run("bc_no_query_value", func(t *testing.T) {
req, _ := http.NewRequest("GET", "/v1/health/service/consul?passing", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.HealthServiceNodes(resp, req)
if err != nil {
t.Fatalf("err: %v", err)
}
assertIndex(t, resp)
assertIndex(t, resp)
// Should be 0 health check for consul
nodes := obj.(structs.CheckServiceNodes)
if len(nodes) != 0 {
t.Fatalf("bad: %v", obj)
}
// Should be 0 health check for consul
nodes := obj.(structs.CheckServiceNodes)
if len(nodes) != 0 {
t.Fatalf("bad: %v", obj)
}
})
t.Run("passing_true", func(t *testing.T) {
req, _ := http.NewRequest("GET", "/v1/health/service/consul?passing=true", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.HealthServiceNodes(resp, req)
if err != nil {
t.Fatalf("err: %v", err)
}
assertIndex(t, resp)
// Should be 0 health check for consul
nodes := obj.(structs.CheckServiceNodes)
if len(nodes) != 0 {
t.Fatalf("bad: %v", obj)
}
})
t.Run("passing_false", func(t *testing.T) {
req, _ := http.NewRequest("GET", "/v1/health/service/consul?passing=false", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.HealthServiceNodes(resp, req)
if err != nil {
t.Fatalf("err: %v", err)
}
assertIndex(t, resp)
// Should be 0 health check for consul
nodes := obj.(structs.CheckServiceNodes)
if len(nodes) != 1 {
t.Fatalf("bad: %v", obj)
}
})
t.Run("passing_bad", func(t *testing.T) {
req, _ := http.NewRequest("GET", "/v1/health/service/consul?passing=nope-nope-nope", nil)
resp := httptest.NewRecorder()
a.srv.HealthServiceNodes(resp, req)
if code := resp.Code; code != 400 {
t.Errorf("bad response code %d, expected %d", code, 400)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
if !bytes.Contains(body, []byte("Invalid value for ?passing")) {
t.Errorf("bad %s", body)
}
})
}
func TestHealthServiceNodes_WanTranslation(t *testing.T) {