Use encoding/json as JSON decoder instead of mapstructure (#6680)
Fixes #6147
This commit is contained in:
parent
2fdfdaec3d
commit
7a4be7863d
|
@ -6,7 +6,6 @@ import (
|
|||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
|
@ -273,53 +272,6 @@ func (s *HTTPServer) ACLPolicyCreate(resp http.ResponseWriter, req *http.Request
|
|||
return s.aclPolicyWriteInternal(resp, req, "", true)
|
||||
}
|
||||
|
||||
// fixTimeAndHashFields is used to help in decoding the ExpirationTTL, ExpirationTime, CreateTime, and Hash
|
||||
// attributes from the ACL Token/Policy create/update requests. It is needed
|
||||
// to help mapstructure decode things properly when decodeBody is used.
|
||||
func fixTimeAndHashFields(raw interface{}) error {
|
||||
rawMap, ok := raw.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
if val, ok := rawMap["ExpirationTTL"]; ok {
|
||||
if sval, ok := val.(string); ok {
|
||||
d, err := time.ParseDuration(sval)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rawMap["ExpirationTTL"] = d
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := rawMap["ExpirationTime"]; ok {
|
||||
if sval, ok := val.(string); ok {
|
||||
t, err := time.Parse(time.RFC3339, sval)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rawMap["ExpirationTime"] = t
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := rawMap["CreateTime"]; ok {
|
||||
if sval, ok := val.(string); ok {
|
||||
t, err := time.Parse(time.RFC3339, sval)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rawMap["CreateTime"] = t
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := rawMap["Hash"]; ok {
|
||||
if sval, ok := val.(string); ok {
|
||||
rawMap["Hash"] = []byte(sval)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *HTTPServer) ACLPolicyWrite(resp http.ResponseWriter, req *http.Request, policyID string) (interface{}, error) {
|
||||
return s.aclPolicyWriteInternal(resp, req, policyID, false)
|
||||
}
|
||||
|
@ -331,7 +283,7 @@ func (s *HTTPServer) aclPolicyWriteInternal(resp http.ResponseWriter, req *http.
|
|||
s.parseToken(req, &args.Token)
|
||||
s.parseEntMeta(req, &args.Policy.EnterpriseMeta)
|
||||
|
||||
if err := decodeBody(req, &args.Policy, fixTimeAndHashFields); err != nil {
|
||||
if err := decodeBody(req.Body, &args.Policy); err != nil {
|
||||
return nil, BadRequestError{Reason: fmt.Sprintf("Policy decoding failed: %v", err)}
|
||||
}
|
||||
|
||||
|
@ -521,7 +473,7 @@ func (s *HTTPServer) aclTokenSetInternal(resp http.ResponseWriter, req *http.Req
|
|||
s.parseToken(req, &args.Token)
|
||||
s.parseEntMeta(req, &args.ACLToken.EnterpriseMeta)
|
||||
|
||||
if err := decodeBody(req, &args.ACLToken, fixTimeAndHashFields); err != nil {
|
||||
if err := decodeBody(req.Body, &args.ACLToken); err != nil {
|
||||
return nil, BadRequestError{Reason: fmt.Sprintf("Token decoding failed: %v", err)}
|
||||
}
|
||||
|
||||
|
@ -567,8 +519,7 @@ func (s *HTTPServer) ACLTokenClone(resp http.ResponseWriter, req *http.Request,
|
|||
}
|
||||
|
||||
s.parseEntMeta(req, &args.ACLToken.EnterpriseMeta)
|
||||
|
||||
if err := decodeBody(req, &args.ACLToken, fixTimeAndHashFields); err != nil && err.Error() != "EOF" {
|
||||
if err := decodeBody(req.Body, &args.ACLToken); err != nil {
|
||||
return nil, BadRequestError{Reason: fmt.Sprintf("Token decoding failed: %v", err)}
|
||||
}
|
||||
s.parseToken(req, &args.Token)
|
||||
|
@ -705,7 +656,7 @@ func (s *HTTPServer) ACLRoleWrite(resp http.ResponseWriter, req *http.Request, r
|
|||
s.parseToken(req, &args.Token)
|
||||
s.parseEntMeta(req, &args.Role.EnterpriseMeta)
|
||||
|
||||
if err := decodeBody(req, &args.Role, fixTimeAndHashFields); err != nil {
|
||||
if err := decodeBody(req.Body, &args.Role); err != nil {
|
||||
return nil, BadRequestError{Reason: fmt.Sprintf("Role decoding failed: %v", err)}
|
||||
}
|
||||
|
||||
|
@ -844,7 +795,7 @@ func (s *HTTPServer) ACLBindingRuleWrite(resp http.ResponseWriter, req *http.Req
|
|||
s.parseToken(req, &args.Token)
|
||||
s.parseEntMeta(req, &args.BindingRule.EnterpriseMeta)
|
||||
|
||||
if err := decodeBody(req, &args.BindingRule, fixTimeAndHashFields); err != nil {
|
||||
if err := decodeBody(req.Body, &args.BindingRule); err != nil {
|
||||
return nil, BadRequestError{Reason: fmt.Sprintf("BindingRule decoding failed: %v", err)}
|
||||
}
|
||||
|
||||
|
@ -980,7 +931,7 @@ func (s *HTTPServer) ACLAuthMethodWrite(resp http.ResponseWriter, req *http.Requ
|
|||
s.parseToken(req, &args.Token)
|
||||
s.parseEntMeta(req, &args.AuthMethod.EnterpriseMeta)
|
||||
|
||||
if err := decodeBody(req, &args.AuthMethod, fixTimeAndHashFields); err != nil {
|
||||
if err := decodeBody(req.Body, &args.AuthMethod); err != nil {
|
||||
return nil, BadRequestError{Reason: fmt.Sprintf("AuthMethod decoding failed: %v", err)}
|
||||
}
|
||||
|
||||
|
@ -1029,7 +980,7 @@ func (s *HTTPServer) ACLLogin(resp http.ResponseWriter, req *http.Request) (inte
|
|||
s.parseDC(req, &args.Datacenter)
|
||||
s.parseEntMeta(req, &args.Auth.EnterpriseMeta)
|
||||
|
||||
if err := decodeBody(req, &args.Auth, nil); err != nil {
|
||||
if err := decodeBody(req.Body, &args.Auth); err != nil {
|
||||
return nil, BadRequestError{Reason: fmt.Sprintf("Failed to decode request body:: %v", err)}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ func (s *HTTPServer) aclSet(resp http.ResponseWriter, req *http.Request, update
|
|||
|
||||
// Handle optional request body
|
||||
if req.ContentLength > 0 {
|
||||
if err := decodeBody(req, &args.ACL, nil); err != nil {
|
||||
if err := decodeBody(req.Body, &args.ACL); err != nil {
|
||||
resp.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
return nil, nil
|
||||
|
|
|
@ -457,11 +457,8 @@ func (s *HTTPServer) syncChanges() {
|
|||
|
||||
func (s *HTTPServer) AgentRegisterCheck(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
var args structs.CheckDefinition
|
||||
// Fixup the type decode of TTL or Interval.
|
||||
decodeCB := func(raw interface{}) error {
|
||||
return FixupCheckType(raw)
|
||||
}
|
||||
if err := decodeBody(req, &args, decodeCB); err != nil {
|
||||
|
||||
if err := decodeBody(req.Body, &args); err != nil {
|
||||
resp.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
return nil, nil
|
||||
|
@ -606,7 +603,7 @@ type checkUpdate struct {
|
|||
// APIs.
|
||||
func (s *HTTPServer) AgentCheckUpdate(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
var update checkUpdate
|
||||
if err := decodeBody(req, &update, nil); err != nil {
|
||||
if err := decodeBody(req.Body, &update); err != nil {
|
||||
resp.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
return nil, nil
|
||||
|
@ -758,7 +755,7 @@ func (s *HTTPServer) AgentRegisterService(resp http.ResponseWriter, req *http.Re
|
|||
var args structs.ServiceDefinition
|
||||
// Fixup the type decode of TTL or Interval if a check if provided.
|
||||
|
||||
if err := decodeBody(req, &args, registerServiceDecodeCB); err != nil {
|
||||
if err := decodeBody(req.Body, &args); err != nil {
|
||||
resp.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
return nil, nil
|
||||
|
@ -894,76 +891,6 @@ func (s *HTTPServer) AgentRegisterService(resp http.ResponseWriter, req *http.Re
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// registerServiceDecodeCB is used in AgentRegisterService for request body decoding
|
||||
func registerServiceDecodeCB(raw interface{}) error {
|
||||
rawMap, ok := raw.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// see https://github.com/hashicorp/consul/pull/3557 why we need this
|
||||
// and why we should get rid of it.
|
||||
lib.TranslateKeys(rawMap, map[string]string{
|
||||
"enable_tag_override": "EnableTagOverride",
|
||||
// Proxy Upstreams
|
||||
"destination_name": "DestinationName", // string
|
||||
"destination_type": "DestinationType", // string
|
||||
"destination_namespace": "DestinationNamespace", // string
|
||||
"local_bind_port": "LocalBindPort", // int
|
||||
"local_bind_address": "LocalBindAddress", // string
|
||||
// Proxy Config
|
||||
"destination_service_name": "DestinationServiceName", // string (Proxy.)
|
||||
"destination_service_id": "DestinationServiceID", // string
|
||||
"local_service_port": "LocalServicePort", // int
|
||||
"local_service_address": "LocalServiceAddress", // string
|
||||
// SidecarService
|
||||
"sidecar_service": "SidecarService", // ServiceDefinition (Connect.)
|
||||
// Expose Config
|
||||
"local_path_port": "LocalPathPort", // int (Proxy.Expose.Paths.)
|
||||
"listener_port": "ListenerPort", // int
|
||||
|
||||
// DON'T Recurse into these opaque config maps or we might mangle user's
|
||||
// keys. Note empty canonical is a special sentinel to prevent recursion.
|
||||
"Meta": "",
|
||||
|
||||
"tagged_addresses": "TaggedAddresses", // map[string]structs.ServiceAddress{Address string; Port int}
|
||||
|
||||
// upstreams is an array but this prevents recursion into config field of
|
||||
// any item in the array.
|
||||
"Proxy.Config": "",
|
||||
"Proxy.Upstreams.Config": "",
|
||||
"Connect.Proxy.Config": "",
|
||||
"Connect.Proxy.Upstreams.Config": "",
|
||||
|
||||
// Same exceptions as above, but for a nested sidecar_service note we use
|
||||
// the canonical form SidecarService since that is translated by the time
|
||||
// the lookup here happens.
|
||||
"Connect.SidecarService.Meta": "",
|
||||
"Connect.SidecarService.Proxy.Config": "",
|
||||
"Connect.SidecarService.Proxy.Upstreams.config": "",
|
||||
})
|
||||
|
||||
for k, v := range rawMap {
|
||||
switch strings.ToLower(k) {
|
||||
case "check":
|
||||
if err := FixupCheckType(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case "checks":
|
||||
chkTypes, ok := v.([]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, chkType := range chkTypes {
|
||||
if err := FixupCheckType(chkType); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *HTTPServer) AgentDeregisterService(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
serviceID := strings.TrimPrefix(req.URL.Path, "/v1/agent/service/deregister/")
|
||||
|
||||
|
@ -1180,7 +1107,7 @@ func (s *HTTPServer) AgentToken(resp http.ResponseWriter, req *http.Request) (in
|
|||
// The body is just the token, but it's in a JSON object so we can add
|
||||
// fields to this later if needed.
|
||||
var args api.AgentToken
|
||||
if err := decodeBody(req, &args, nil); err != nil {
|
||||
if err := decodeBody(req.Body, &args); err != nil {
|
||||
resp.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
return nil, nil
|
||||
|
@ -1339,7 +1266,7 @@ func (s *HTTPServer) AgentConnectAuthorize(resp http.ResponseWriter, req *http.R
|
|||
|
||||
// Decode the request from the request body
|
||||
var authReq structs.ConnectAuthorizeRequest
|
||||
if err := decodeBody(req, &authReq, nil); err != nil {
|
||||
if err := decodeBody(req.Body, &authReq); err != nil {
|
||||
return nil, BadRequestError{fmt.Sprintf("Request decode failed: %v", err)}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,14 +10,12 @@ import (
|
|||
"github.com/hashicorp/consul/agent/structs"
|
||||
)
|
||||
|
||||
var durations = NewDurationFixer("interval", "timeout", "deregistercriticalserviceafter")
|
||||
|
||||
func (s *HTTPServer) CatalogRegister(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
metrics.IncrCounterWithLabels([]string{"client", "api", "catalog_register"}, 1,
|
||||
[]metrics.Label{{Name: "node", Value: s.nodeName()}})
|
||||
|
||||
var args structs.RegisterRequest
|
||||
if err := decodeBody(req, &args, durations.FixupDurations); err != nil {
|
||||
if err := decodeBody(req.Body, &args); err != nil {
|
||||
resp.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
return nil, nil
|
||||
|
@ -46,7 +44,7 @@ func (s *HTTPServer) CatalogDeregister(resp http.ResponseWriter, req *http.Reque
|
|||
[]metrics.Label{{Name: "node", Value: s.nodeName()}})
|
||||
|
||||
var args structs.DeregisterRequest
|
||||
if err := decodeBody(req, &args, nil); err != nil {
|
||||
if err := decodeBody(req.Body, &args); err != nil {
|
||||
resp.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
return nil, nil
|
||||
|
|
|
@ -103,7 +103,7 @@ func (s *HTTPServer) ConfigApply(resp http.ResponseWriter, req *http.Request) (i
|
|||
s.parseToken(req, &args.Token)
|
||||
|
||||
var raw map[string]interface{}
|
||||
if err := decodeBody(req, &raw, nil); err != nil {
|
||||
if err := decodeBodyDeprecated(req, &raw, nil); err != nil {
|
||||
return nil, BadRequestError{Reason: fmt.Sprintf("Request decoding failed: %v", err)}
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ func (s *HTTPServer) ConnectCAConfigurationSet(resp http.ResponseWriter, req *ht
|
|||
var args structs.CARequest
|
||||
s.parseDC(req, &args.Datacenter)
|
||||
s.parseToken(req, &args.Token)
|
||||
if err := decodeBody(req, &args.Config, nil); err != nil {
|
||||
if err := decodeBody(req.Body, &args.Config); err != nil {
|
||||
resp.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
return nil, nil
|
||||
|
|
|
@ -150,7 +150,7 @@ func (s *HTTPServer) CoordinateUpdate(resp http.ResponseWriter, req *http.Reques
|
|||
}
|
||||
|
||||
args := structs.CoordinateUpdateRequest{}
|
||||
if err := decodeBody(req, &args, nil); err != nil {
|
||||
if err := decodeBody(req.Body, &args); err != nil {
|
||||
resp.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
return nil, nil
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package agent
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
@ -28,7 +29,7 @@ func (s *HTTPServer) DiscoveryChainRead(resp http.ResponseWriter, req *http.Requ
|
|||
|
||||
if req.Method == "POST" {
|
||||
var raw map[string]interface{}
|
||||
if err := decodeBody(req, &raw, nil); err != nil {
|
||||
if err := decodeBody(req.Body, &raw); err != nil {
|
||||
return nil, BadRequestError{Reason: fmt.Sprintf("Request decoding failed: %v", err)}
|
||||
}
|
||||
|
||||
|
@ -91,6 +92,63 @@ type discoveryChainReadRequest struct {
|
|||
OverrideConnectTimeout time.Duration
|
||||
}
|
||||
|
||||
func (t *discoveryChainReadRequest) UnmarshalJSON(data []byte) (err error) {
|
||||
type Alias discoveryChainReadRequest
|
||||
aux := &struct {
|
||||
OverrideConnectTimeout interface{}
|
||||
OverrideProtocol interface{}
|
||||
OverrideMeshGateway *struct{ Mode interface{} }
|
||||
|
||||
OverrideConnectTimeoutSnake interface{} `json:"override_connect_timeout"`
|
||||
OverrideProtocolSnake interface{} `json:"override_protocol"`
|
||||
OverrideMeshGatewaySnake *struct{ Mode interface{} } `json:"override_mesh_gateway"`
|
||||
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(t),
|
||||
}
|
||||
if err = json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if aux.OverrideConnectTimeout == nil {
|
||||
aux.OverrideConnectTimeout = aux.OverrideConnectTimeoutSnake
|
||||
}
|
||||
if aux.OverrideProtocol == nil {
|
||||
aux.OverrideProtocol = aux.OverrideProtocolSnake
|
||||
}
|
||||
if aux.OverrideMeshGateway == nil {
|
||||
aux.OverrideMeshGateway = aux.OverrideMeshGatewaySnake
|
||||
}
|
||||
|
||||
// weakly typed input
|
||||
if aux.OverrideProtocol != nil {
|
||||
switch v := aux.OverrideProtocol.(type) {
|
||||
case string, float64, bool:
|
||||
t.OverrideProtocol = fmt.Sprintf("%v", v)
|
||||
default:
|
||||
return fmt.Errorf("OverrideProtocol: invalid type %T", v)
|
||||
}
|
||||
}
|
||||
if aux.OverrideMeshGateway != nil {
|
||||
t.OverrideMeshGateway.Mode = structs.MeshGatewayMode(fmt.Sprintf("%v", aux.OverrideMeshGateway.Mode))
|
||||
}
|
||||
|
||||
// duration
|
||||
if aux.OverrideConnectTimeout != nil {
|
||||
switch v := aux.OverrideConnectTimeout.(type) {
|
||||
case string:
|
||||
if t.OverrideConnectTimeout, err = time.ParseDuration(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case float64:
|
||||
t.OverrideConnectTimeout = time.Duration(v)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// discoveryChainReadResponse is the API variation of structs.DiscoveryChainResponse
|
||||
type discoveryChainReadResponse struct {
|
||||
Chain *structs.CompiledDiscoveryChain
|
||||
|
|
|
@ -572,8 +572,17 @@ func (s *HTTPServer) Index(resp http.ResponseWriter, req *http.Request) {
|
|||
http.Redirect(resp, req, s.agent.config.UIContentPath, http.StatusMovedPermanently) // 301
|
||||
}
|
||||
|
||||
// decodeBody is used to decode a JSON request body
|
||||
func decodeBody(req *http.Request, out interface{}, cb func(interface{}) error) error {
|
||||
func decodeBody(body io.Reader, out interface{}) error {
|
||||
if body == nil {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
return json.NewDecoder(body).Decode(&out)
|
||||
}
|
||||
|
||||
// decodeBodyDeprecated is deprecated, please ues decodeBody above.
|
||||
// decodeBodyDeprecated is used to decode a JSON request body
|
||||
func decodeBodyDeprecated(req *http.Request, out interface{}, cb func(interface{}) error) error {
|
||||
// This generally only happens in tests since real HTTP requests set
|
||||
// a non-nil body with no content. We guard against it anyways to prevent
|
||||
// a panic. The EOF response is the same behavior as an empty reader.
|
||||
|
|
|
@ -33,8 +33,7 @@ package agent
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -348,13 +347,13 @@ var translateScriptArgsTCs = []translateKeyTestCase{
|
|||
jsonFmtStr: "{" + scriptFields[0] + "," + scriptFields[2] + "}",
|
||||
equalityFn: scriptArgsEqFn,
|
||||
},
|
||||
// {
|
||||
// desc: "scriptArgs: second and third set",
|
||||
// in: []interface{}{`["2"]`, `["3"]`},
|
||||
// want: []string{"2"},
|
||||
// jsonFmtStr: "{" + scriptFields[1] + "," + scriptFields[2] + "}",
|
||||
// equalityFn: scriptArgsEqFn,
|
||||
// },
|
||||
{
|
||||
desc: "scriptArgs: second and third set",
|
||||
in: []interface{}{`["2"]`, `["3"]`},
|
||||
want: []string{"2"},
|
||||
jsonFmtStr: "{" + scriptFields[1] + "," + scriptFields[2] + "}",
|
||||
equalityFn: scriptArgsEqFn,
|
||||
},
|
||||
{
|
||||
desc: "scriptArgs: first set",
|
||||
in: []interface{}{`["1"]`},
|
||||
|
@ -618,15 +617,6 @@ var translateServiceIDTCs = []translateKeyTestCase{
|
|||
},
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/acl_endpoint.go:
|
||||
// 327 s.parseToken(req, &args.Token)
|
||||
// 328
|
||||
// 329: if err := decodeBody(req, &args.Policy, fixTimeAndHashFields); err != nil {
|
||||
// 330 return nil, BadRequestError{Reason: fmt.Sprintf("Policy decoding failed: %v", err)}
|
||||
// 331 }
|
||||
// ==================================
|
||||
|
||||
// ACLPolicySetRequest:
|
||||
// Policy structs.ACLPolicy
|
||||
// ID string
|
||||
|
@ -646,12 +636,15 @@ func TestDecodeACLPolicyWrite(t *testing.T) {
|
|||
|
||||
for _, tc := range hashTestCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
jsonStr := fmt.Sprintf(`{"Hash": %s}`, tc.hashes.in)
|
||||
|
||||
jsonStr := fmt.Sprintf(`{
|
||||
"Hash": %s
|
||||
}`, tc.hashes.in)
|
||||
body := bytes.NewBuffer([]byte(jsonStr))
|
||||
req := httptest.NewRequest("POST", "http://foo.com", body)
|
||||
|
||||
var out structs.ACLPolicy
|
||||
err := decodeBody(req, &out, fixTimeAndHashFields)
|
||||
err := decodeBody(body, &out)
|
||||
|
||||
if err != nil && !tc.wantErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -666,15 +659,6 @@ func TestDecodeACLPolicyWrite(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/acl_endpoint.go:
|
||||
// 511 s.parseToken(req, &args.Token)
|
||||
// 512
|
||||
// 513: if err := decodeBody(req, &args.ACLToken, fixTimeAndHashFields); err != nil {
|
||||
// 514 return nil, BadRequestError{Reason: fmt.Sprintf("Token decoding failed: %v", err)}
|
||||
// 515 }
|
||||
// ==================================
|
||||
|
||||
// ACLTokenSetRequest:
|
||||
// ACLToken structs.ACLToken
|
||||
// AccessorID string
|
||||
|
@ -704,9 +688,7 @@ func TestDecodeACLPolicyWrite(t *testing.T) {
|
|||
// Datacenter string
|
||||
// WriteRequest structs.WriteRequest
|
||||
// Token string
|
||||
|
||||
func TestDecodeACLToken(t *testing.T) {
|
||||
|
||||
for _, tc := range translateValueTestCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
// set up request body
|
||||
|
@ -728,13 +710,12 @@ func TestDecodeACLToken(t *testing.T) {
|
|||
"Hash": %s
|
||||
}`, expTime, expTTL, createTime, hash))
|
||||
|
||||
// set up request
|
||||
body := bytes.NewBuffer(bodyBytes)
|
||||
req := httptest.NewRequest("POST", "http://foo.com", body)
|
||||
|
||||
// decode body
|
||||
var out structs.ACLToken
|
||||
err := decodeBody(req, &out, fixTimeAndHashFields)
|
||||
|
||||
err := decodeBody(body, &out)
|
||||
if err != nil && !tc.wantErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -775,27 +756,6 @@ func TestDecodeACLToken(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/acl_endpoint.go:
|
||||
// 555 }
|
||||
// 556
|
||||
// 557: if err := decodeBody(req, &args.ACLToken, fixTimeAndHashFields); err != nil && err.Error() != "EOF" {
|
||||
// 558 return nil, BadRequestError{Reason: fmt.Sprintf("Token decoding failed: %v", err)}
|
||||
// 559 }
|
||||
// ==================================
|
||||
func TestDecodeACLTokenClone(t *testing.T) {
|
||||
t.Skip("COVERED BY ABOVE (same structs.ACLTokenSetRequest).")
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/acl_endpoint.go:
|
||||
// 689 s.parseToken(req, &args.Token)
|
||||
// 690
|
||||
// 691: if err := decodeBody(req, &args.Role, fixTimeAndHashFields); err != nil {
|
||||
// 692 return nil, BadRequestError{Reason: fmt.Sprintf("Role decoding failed: %v", err)}
|
||||
// 693 }
|
||||
// ==================================
|
||||
|
||||
// ACLRoleSetRequest:
|
||||
// Role structs.ACLRole
|
||||
// ID string
|
||||
|
@ -816,15 +776,17 @@ func TestDecodeACLTokenClone(t *testing.T) {
|
|||
// Token string
|
||||
|
||||
func TestDecodeACLRoleWrite(t *testing.T) {
|
||||
|
||||
for _, tc := range hashTestCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
jsonStr := fmt.Sprintf(`{"Hash": %s}`, tc.hashes.in)
|
||||
|
||||
jsonStr := fmt.Sprintf(`{
|
||||
"Hash": %s
|
||||
}`, tc.hashes.in)
|
||||
body := bytes.NewBuffer([]byte(jsonStr))
|
||||
req := httptest.NewRequest("POST", "http://foo.com", body)
|
||||
|
||||
var out structs.ACLRole
|
||||
err := decodeBody(req, &out, fixTimeAndHashFields)
|
||||
err := decodeBody(body, &out)
|
||||
|
||||
if err == nil && tc.wantErr {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
|
@ -839,111 +801,6 @@ func TestDecodeACLRoleWrite(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/acl_endpoint.go:
|
||||
// 822 s.parseToken(req, &args.Token)
|
||||
// 823
|
||||
// 824: if err := decodeBody(req, &args.BindingRule, fixTimeAndHashFields); err != nil {
|
||||
// 825 return nil, BadRequestError{Reason: fmt.Sprintf("BindingRule decoding failed: %v", err)}
|
||||
// 826 }
|
||||
// ==================================
|
||||
//
|
||||
// ACLBindingRuleSetRequest:
|
||||
// BindingRule structs.ACLBindingRule
|
||||
// ID string
|
||||
// Description string
|
||||
// AuthMethod string
|
||||
// Selector string
|
||||
// BindType string
|
||||
// BindName string
|
||||
// RaftIndex structs.RaftIndex
|
||||
// CreateIndex uint64
|
||||
// ModifyIndex uint64
|
||||
// Datacenter string
|
||||
// WriteRequest structs.WriteRequest
|
||||
// Token string
|
||||
func TestDecodeACLBindingRuleWrite(t *testing.T) {
|
||||
t.Skip("DONE. no special fields to parse; fixTimeAndHashFields: no time or hash fields.")
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/acl_endpoint.go:
|
||||
// 954 s.parseToken(req, &args.Token)
|
||||
// 955
|
||||
// 956: if err := decodeBody(req, &args.AuthMethod, fixTimeAndHashFields); err != nil {
|
||||
// 957 return nil, BadRequestError{Reason: fmt.Sprintf("AuthMethod decoding failed: %v", err)}
|
||||
// 958 }
|
||||
// ==================================
|
||||
// ACLAuthMethodSetRequest:
|
||||
// AuthMethod structs.ACLAuthMethod
|
||||
// Name string
|
||||
// Type string
|
||||
// Description string
|
||||
// Config map[string]interface {}
|
||||
// RaftIndex structs.RaftIndex
|
||||
// CreateIndex uint64
|
||||
// ModifyIndex uint64
|
||||
// Datacenter string
|
||||
// WriteRequest structs.WriteRequest
|
||||
// Token string
|
||||
func TestDecodeACLAuthMethodWrite(t *testing.T) {
|
||||
t.Skip("DONE. no special fields to parse; fixTimeAndHashFields: no time or hash fields.")
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/acl_endpoint.go:
|
||||
// 1000 s.parseDC(req, &args.Datacenter)
|
||||
// 1001
|
||||
// 1002: if err := decodeBody(req, &args.Auth, nil); err != nil {
|
||||
// 1003 return nil, BadRequestError{Reason: fmt.Sprintf("Failed to decode request body:: %v", err)}
|
||||
// 1004 }
|
||||
// ==================================
|
||||
// ACLLoginRequest:
|
||||
// Auth *structs.ACLLoginParams
|
||||
// AuthMethod string
|
||||
// BearerToken string
|
||||
// Meta map[string]string
|
||||
// Datacenter string
|
||||
// WriteRequest structs.WriteRequest
|
||||
// Token string
|
||||
func TestDecodeACLLogin(t *testing.T) {
|
||||
t.Skip("DONE. no special fields to parse; no decodeBody callback used.")
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/acl_endpoint_legacy.go:
|
||||
// 66 // Handle optional request body
|
||||
// 67 if req.ContentLength > 0 {
|
||||
// 68: if err := decodeBody(req, &args.ACL, nil); err != nil {
|
||||
// 69 resp.WriteHeader(http.StatusBadRequest)
|
||||
// 70 fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
// ==================================
|
||||
//
|
||||
// ACLRequest:
|
||||
// Datacenter string
|
||||
// Op structs.ACLOp
|
||||
// ACL structs.ACL
|
||||
// ID string
|
||||
// Name string
|
||||
// Type string
|
||||
// Rules string
|
||||
// RaftIndex structs.RaftIndex
|
||||
// CreateIndex uint64
|
||||
// ModifyIndex uint64
|
||||
// WriteRequest structs.WriteRequest
|
||||
// Token string
|
||||
func TestDecodeACLUpdate(t *testing.T) {
|
||||
t.Skip("DONE. no special fields to parse; no decodeBody callback used.")
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/agent_endpoint.go:
|
||||
// 461 return FixupCheckType(raw)
|
||||
// 462 }
|
||||
// 463: if err := decodeBody(req, &args, decodeCB); err != nil {
|
||||
// 464 resp.WriteHeader(http.StatusBadRequest)
|
||||
// 465 fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
// ==================================
|
||||
// CheckDefinition:
|
||||
// ID types.CheckID
|
||||
// Name string
|
||||
|
@ -976,16 +833,17 @@ func TestDecodeAgentRegisterCheck(t *testing.T) {
|
|||
t.Run(tc.desc, func(t *testing.T) {
|
||||
// set up request body
|
||||
jsonStr := fmt.Sprintf(`{
|
||||
|
||||
"Interval": %[1]s,
|
||||
"Timeout": %[1]s,
|
||||
"TTL": %[1]s,
|
||||
"DeregisterCriticalServiceAfter": %[1]s
|
||||
}`, tc.durations.in)
|
||||
body := bytes.NewBuffer([]byte(jsonStr))
|
||||
req := httptest.NewRequest("POST", "http://foo.com", body)
|
||||
|
||||
var out structs.CheckDefinition
|
||||
err := decodeBody(req, &out, FixupCheckType)
|
||||
err := decodeBody(body, &out)
|
||||
|
||||
if err == nil && tc.wantErr {
|
||||
t.Fatal("expected err, got nil")
|
||||
}
|
||||
|
@ -998,19 +856,17 @@ func TestDecodeAgentRegisterCheck(t *testing.T) {
|
|||
}
|
||||
})
|
||||
}
|
||||
// decodeCB:
|
||||
// - Header field
|
||||
// - translate keys
|
||||
|
||||
for _, tc := range checkTypeHeaderTestCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
// set up request body
|
||||
jsonStr := fmt.Sprintf(`{"Header": %s}`, tc.in)
|
||||
|
||||
body := bytes.NewBuffer([]byte(jsonStr))
|
||||
req := httptest.NewRequest("POST", "http://foo.com", body)
|
||||
|
||||
var out structs.CheckDefinition
|
||||
err := decodeBody(req, &out, FixupCheckType)
|
||||
err := decodeBody(body, &out)
|
||||
|
||||
if err == nil && tc.wantErr {
|
||||
t.Fatal("expected err, got nil")
|
||||
}
|
||||
|
@ -1027,11 +883,12 @@ func TestDecodeAgentRegisterCheck(t *testing.T) {
|
|||
for _, tc := range tcs {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
jsonStr := fmt.Sprintf(tc.jsonFmtStr, tc.in...)
|
||||
|
||||
body := bytes.NewBuffer([]byte(jsonStr))
|
||||
req := httptest.NewRequest("POST", "http://foo.com", body)
|
||||
|
||||
var out structs.CheckDefinition
|
||||
err := decodeBody(req, &out, FixupCheckType)
|
||||
err := decodeBody(body, &out)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -1045,42 +902,6 @@ func TestDecodeAgentRegisterCheck(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/agent_endpoint.go:
|
||||
// 603 func (s *HTTPServer) AgentCheckUpdate(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
// 604 var update checkUpdate
|
||||
// 605: if err := decodeBody(req, &update, nil); err != nil {
|
||||
// 606 resp.WriteHeader(http.StatusBadRequest)
|
||||
// 607 fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
// ==================================
|
||||
// type checkUpdate struct {
|
||||
// Status string
|
||||
// Output string
|
||||
// }
|
||||
func TestDecodeAgentCheckUpdate(t *testing.T) {
|
||||
t.Skip("DONE. no special fields to parse; no decodeBody callback used.")
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/agent_endpoint.go:
|
||||
// 822 return nil
|
||||
// 823 }
|
||||
// 824: if err := decodeBody(req, &args, decodeCB); err != nil {
|
||||
// 825 resp.WriteHeader(http.StatusBadRequest)
|
||||
// 826 fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
// ==================================
|
||||
//
|
||||
// decodeCB:
|
||||
// -----------
|
||||
// 1. lib.TranslateKeys()
|
||||
// 2. FixupCheckType
|
||||
// a. lib.TranslateKeys()
|
||||
// b. parseDuration()
|
||||
// c. parseHeaderMap()
|
||||
//
|
||||
//
|
||||
// Type fields:
|
||||
// -----------
|
||||
// ServiceDefinition:
|
||||
// Kind structs.ServiceKind
|
||||
// ID string
|
||||
|
@ -1151,8 +972,6 @@ func TestDecodeAgentCheckUpdate(t *testing.T) {
|
|||
// Native bool
|
||||
// SidecarService *structs.ServiceDefinition
|
||||
func TestDecodeAgentRegisterService(t *testing.T) {
|
||||
var callback = registerServiceDecodeCB
|
||||
|
||||
// key translation tests:
|
||||
// decodeCB fields:
|
||||
// --------------------
|
||||
|
@ -1325,14 +1144,15 @@ func TestDecodeAgentRegisterService(t *testing.T) {
|
|||
desc: "DestinationNamespace: both set",
|
||||
in: []interface{}{`"a"`, `"b"`},
|
||||
want: "a",
|
||||
jsonFmtStr: `{"Proxy": {"Upstreams": [{` + destinationNamespaceFields[1] + `}]}}`,
|
||||
jsonFmtStr: `{"Proxy": {"Upstreams": [{` + strings.Join(destinationNamespaceFields, ",") + `}]}}`,
|
||||
|
||||
equalityFn: destinationNamespaceEqFn,
|
||||
},
|
||||
{
|
||||
desc: "DestinationNamespace: first set",
|
||||
in: []interface{}{`"a"`},
|
||||
want: "a",
|
||||
jsonFmtStr: `{"Proxy": {"Upstreams": [{` + destinationNamespaceFields[1] + `}]}}`,
|
||||
jsonFmtStr: `{"Proxy": {"Upstreams": [{` + destinationNamespaceFields[0] + `}]}}`,
|
||||
equalityFn: destinationNamespaceEqFn,
|
||||
},
|
||||
{
|
||||
|
@ -1843,10 +1663,10 @@ func TestDecodeAgentRegisterService(t *testing.T) {
|
|||
t.Run(tc.desc, func(t *testing.T) {
|
||||
checkJSONStr := fmt.Sprintf(tc.jsonFmtStr, tc.in...)
|
||||
body := bytes.NewBuffer([]byte(checkJSONStr))
|
||||
req := httptest.NewRequest("POST", "http://foo.com", body)
|
||||
|
||||
var out structs.ServiceDefinition
|
||||
err := decodeBody(req, &out, callback)
|
||||
err := decodeBody(body, &out)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -1864,26 +1684,26 @@ func TestDecodeAgentRegisterService(t *testing.T) {
|
|||
t.Run(tc.desc, func(t *testing.T) {
|
||||
// set up request body
|
||||
jsonStr := fmt.Sprintf(`{
|
||||
"Check": {
|
||||
"Interval": %[1]s,
|
||||
"Timeout": %[1]s,
|
||||
"TTL": %[1]s,
|
||||
"DeregisterCriticalServiceAfter": %[1]s
|
||||
},
|
||||
"Checks": [
|
||||
{
|
||||
"Check": {
|
||||
"Interval": %[1]s,
|
||||
"Timeout": %[1]s,
|
||||
"TTL": %[1]s,
|
||||
"DeregisterCriticalServiceAfter": %[1]s
|
||||
}
|
||||
]
|
||||
}`, tc.durations.in)
|
||||
},
|
||||
"Checks": [
|
||||
{
|
||||
"Interval": %[1]s,
|
||||
"Timeout": %[1]s,
|
||||
"TTL": %[1]s,
|
||||
"DeregisterCriticalServiceAfter": %[1]s
|
||||
}
|
||||
]
|
||||
}`, tc.durations.in)
|
||||
body := bytes.NewBuffer([]byte(jsonStr))
|
||||
req := httptest.NewRequest("POST", "http://foo.com", body)
|
||||
|
||||
var out structs.ServiceDefinition
|
||||
err := decodeBody(req, &out, callback)
|
||||
err := decodeBody(body, &out)
|
||||
|
||||
if err == nil && tc.wantErr {
|
||||
t.Fatal("expected err, got nil")
|
||||
}
|
||||
|
@ -1917,10 +1737,10 @@ func TestDecodeAgentRegisterService(t *testing.T) {
|
|||
}`, checkJSONStr)
|
||||
|
||||
body := bytes.NewBuffer([]byte(jsonStr))
|
||||
req := httptest.NewRequest("POST", "http://foo.com", body)
|
||||
|
||||
var out structs.ServiceDefinition
|
||||
err := decodeBody(req, &out, callback)
|
||||
err := decodeBody(body, &out)
|
||||
|
||||
if err == nil && tc.wantErr {
|
||||
t.Fatal("expected err, got nil")
|
||||
}
|
||||
|
@ -1951,10 +1771,10 @@ func TestDecodeAgentRegisterService(t *testing.T) {
|
|||
"Checks": [%[1]s]
|
||||
}`, checkJSONStr)
|
||||
body := bytes.NewBuffer([]byte(jsonStr))
|
||||
req := httptest.NewRequest("POST", "http://foo.com", body)
|
||||
|
||||
var out structs.ServiceDefinition
|
||||
err := decodeBody(req, &out, callback)
|
||||
err := decodeBody(body, &out)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -1971,44 +1791,6 @@ func TestDecodeAgentRegisterService(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/agent_endpoint.go:
|
||||
// 1173 // fields to this later if needed.
|
||||
// 1174 var args api.AgentToken
|
||||
// 1175: if err := decodeBody(req, &args, nil); err != nil {
|
||||
// 1176 resp.WriteHeader(http.StatusBadRequest)
|
||||
// 1177 fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
// ==================================
|
||||
// AgentToken:
|
||||
// Token string
|
||||
func TestDecodeAgentToken(t *testing.T) {
|
||||
t.Skip("DONE. no special fields to parse; no decodeBody callback used.")
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/agent_endpoint.go:
|
||||
// 1332 // Decode the request from the request body
|
||||
// 1333 var authReq structs.ConnectAuthorizeRequest
|
||||
// 1334: if err := decodeBody(req, &authReq, nil); err != nil {
|
||||
// 1335 return nil, BadRequestError{fmt.Sprintf("Request decode failed: %v", err)}
|
||||
// 1336 }
|
||||
// ==================================
|
||||
// ConnectAuthorizeRequest:
|
||||
// Target string
|
||||
// ClientCertURI string
|
||||
// ClientCertSerial string
|
||||
func TestDecodeAgentConnectAuthorize(t *testing.T) {
|
||||
t.Skip("DONE. no special fields to parse; no decodeBody callback used.")
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/catalog_endpoint.go:
|
||||
// 18
|
||||
// 19 var args structs.RegisterRequest
|
||||
// 20: if err := decodeBody(req, &args, durations.FixupDurations); err != nil {
|
||||
// 21 resp.WriteHeader(http.StatusBadRequest)
|
||||
// 22 fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
// ==================================
|
||||
// RegisterRequest:
|
||||
// Datacenter string
|
||||
// ID types.NodeID
|
||||
|
@ -2161,16 +1943,19 @@ func TestDecodeCatalogRegister(t *testing.T) {
|
|||
}
|
||||
}`, tc.durations.in)
|
||||
body := bytes.NewBuffer([]byte(jsonStr))
|
||||
req := httptest.NewRequest("POST", "http://foo.com", body)
|
||||
|
||||
var out structs.RegisterRequest
|
||||
err := decodeBody(req, &out, durations.FixupDurations)
|
||||
err := decodeBody(body, &out)
|
||||
|
||||
if err == nil && tc.wantErr {
|
||||
t.Fatal("expected err, got nil")
|
||||
}
|
||||
if err != nil && !tc.wantErr {
|
||||
t.Fatalf("expected nil error, got %v", err)
|
||||
}
|
||||
if err != nil && tc.wantErr {
|
||||
return // no point continuing
|
||||
}
|
||||
|
||||
// Service and Check will be nil if tc.wantErr == true && err != nil.
|
||||
// We don't want to panic upon trying to follow a nil pointer, so we
|
||||
|
@ -2199,118 +1984,12 @@ func TestDecodeCatalogRegister(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/catalog_endpoint.go:
|
||||
// 47
|
||||
// 48 var args structs.DeregisterRequest
|
||||
// 49: if err := decodeBody(req, &args, nil); err != nil {
|
||||
// 50 resp.WriteHeader(http.StatusBadRequest)
|
||||
// 51 fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
// ==================================
|
||||
// DeregisterRequest:
|
||||
// Datacenter string
|
||||
// Node string
|
||||
// ServiceID string
|
||||
// CheckID types.CheckID
|
||||
// WriteRequest structs.WriteRequest
|
||||
// Token string
|
||||
func TestDecodeCatalogDeregister(t *testing.T) {
|
||||
t.Skip("DONE. no special fields to parse; no decodeBody callback used.")
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/config_endpoint.go:
|
||||
// 104
|
||||
// 105 var raw map[string]interface{}
|
||||
// 106: if err := decodeBody(req, &raw, nil); err != nil {
|
||||
// 107 return nil, BadRequestError{Reason: fmt.Sprintf("Request decoding failed: %v", err)}
|
||||
// 108 }
|
||||
// ==================================
|
||||
func TestDecodeConfigApply(t *testing.T) {
|
||||
// TODO $$
|
||||
t.Skip("Leave this fn as-is? Decoding code should probably be the same for all config parsing.")
|
||||
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/connect_ca_endpoint.go:
|
||||
// 63 s.parseDC(req, &args.Datacenter)
|
||||
// 64 s.parseToken(req, &args.Token)
|
||||
// 65: if err := decodeBody(req, &args.Config, nil); err != nil {
|
||||
// 66 resp.WriteHeader(http.StatusBadRequest)
|
||||
// 67 fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
// ==================================
|
||||
// CARequest:
|
||||
// Config *structs.CAConfiguration
|
||||
// ClusterID string
|
||||
// Provider string
|
||||
// Config map[string]interface {}
|
||||
// RaftIndex structs.RaftIndex
|
||||
func TestDecodeConnectCAConfigurationSet(t *testing.T) {
|
||||
t.Skip("DONE. no special fields to parse; no decodeBody callback used.")
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/coordinate_endpoint.go:
|
||||
// 151
|
||||
// 152 args := structs.CoordinateUpdateRequest{}
|
||||
// 153: if err := decodeBody(req, &args, nil); err != nil {
|
||||
// 154 resp.WriteHeader(http.StatusBadRequest)
|
||||
// 155 fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
// ==================================
|
||||
// CoordinateUpdateRequest:
|
||||
// Datacenter string
|
||||
// Node string
|
||||
// Segment string
|
||||
// Coord *coordinate.Coordinate
|
||||
// Vec []float64
|
||||
// Error float64
|
||||
// Adjustment float64
|
||||
// Height float64
|
||||
// WriteRequest structs.WriteRequest
|
||||
// Token string
|
||||
func TestDecodeCoordinateUpdate(t *testing.T) {
|
||||
t.Skip("DONE. no special fields to parse; no decodeBody callback used.")
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/discovery_chain_endpoint.go:
|
||||
// 29 if req.Method == "POST" {
|
||||
// 30 var raw map[string]interface{}
|
||||
// 31: if err := decodeBody(req, &raw, nil); err != nil {
|
||||
// 32 return nil, BadRequestError{Reason: fmt.Sprintf("Request decoding failed: %v", err)}
|
||||
// 33 }
|
||||
// ==================================
|
||||
// discoveryChainReadRequest:
|
||||
// OverrideMeshGateway structs.MeshGatewayConfig
|
||||
// Mode structs.MeshGatewayMode // string
|
||||
// OverrideProtocol string
|
||||
// OverrideConnectTimeout time.Duration
|
||||
func TestDecodeDiscoveryChainRead(t *testing.T) {
|
||||
// Special Beast!
|
||||
|
||||
// This decodeBody call is a special beast, in that it decodes with decodeBody
|
||||
// into a map[string]interface{} and runs subsequent decoding logic outside of
|
||||
// the call.
|
||||
|
||||
// decode code copied from agent/discovery_chain_endpoint.go
|
||||
fullDecodeFn := func(req *http.Request, v *discoveryChainReadRequest) error {
|
||||
var raw map[string]interface{}
|
||||
if err := decodeBody(req, &raw, nil); err != nil {
|
||||
return fmt.Errorf("Request decoding failed: %v", err)
|
||||
}
|
||||
|
||||
apiReq, err := decodeDiscoveryChainReadRequest(raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Request decoding failed: %v", err)
|
||||
}
|
||||
|
||||
*v = *apiReq
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// It doesn't seem as though mapstructure does weakly typed durations.
|
||||
var weaklyTypedDurationTCs = []translateValueTestCase{
|
||||
{
|
||||
desc: "positive string integer (weakly typed)",
|
||||
|
@ -2335,11 +2014,9 @@ func TestDecodeDiscoveryChainRead(t *testing.T) {
|
|||
"OverrideConnectTimeout": %s
|
||||
}`, tc.durations.in)
|
||||
body := bytes.NewBuffer([]byte(jsonStr))
|
||||
req := httptest.NewRequest("POST", "http://foo.com", body)
|
||||
|
||||
var out discoveryChainReadRequest
|
||||
// fullDecodeFn is declared above in this test.
|
||||
err := fullDecodeFn(req, &out)
|
||||
err := decodeBody(body, &out)
|
||||
if err == nil && tc.wantErr {
|
||||
t.Fatal("expected err, got nil")
|
||||
}
|
||||
|
@ -2371,7 +2048,7 @@ func TestDecodeDiscoveryChainRead(t *testing.T) {
|
|||
{
|
||||
desc: "bool for string field (weakly typed)",
|
||||
in: `true`,
|
||||
want: "1",
|
||||
want: "true", // previously: "1"
|
||||
},
|
||||
{
|
||||
desc: "float for string field (weakly typed)",
|
||||
|
@ -2384,9 +2061,9 @@ func TestDecodeDiscoveryChainRead(t *testing.T) {
|
|||
wantErr: true,
|
||||
},
|
||||
{
|
||||
desc: "slice for string field (weakly typed)",
|
||||
in: `[]`,
|
||||
want: "",
|
||||
desc: "slice for string field (weakly typed)",
|
||||
in: `[]`,
|
||||
wantErr: true, // previously: want: ""
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -2394,15 +2071,14 @@ func TestDecodeDiscoveryChainRead(t *testing.T) {
|
|||
t.Run(tc.desc, func(t *testing.T) {
|
||||
// set up request body
|
||||
jsonStr := fmt.Sprintf(`{
|
||||
"OverrideProtocol": %[1]s,
|
||||
"OverrideMeshGateway": {"Mode": %[1]s}
|
||||
}`, tc.in)
|
||||
"OverrideProtocol": %[1]s,
|
||||
"OverrideMeshGateway": {"Mode": %[1]s}
|
||||
}`, tc.in)
|
||||
body := bytes.NewBuffer([]byte(jsonStr))
|
||||
req := httptest.NewRequest("POST", "http://foo.com", body)
|
||||
|
||||
var out discoveryChainReadRequest
|
||||
// fullDecodeFn is declared above in this test.
|
||||
err := fullDecodeFn(req, &out)
|
||||
err := decodeBody(body, &out)
|
||||
|
||||
if err == nil && tc.wantErr {
|
||||
t.Fatal("expected err, got nil")
|
||||
}
|
||||
|
@ -2552,8 +2228,6 @@ func TestDecodeDiscoveryChainRead(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
// from decodeDiscoveryChainReadRequest:
|
||||
//
|
||||
// lib.TranslateKeys(raw, map[string]string{
|
||||
// "override_mesh_gateway": "overridemeshgateway",
|
||||
// "override_protocol": "overrideprotocol",
|
||||
|
@ -2571,11 +2245,9 @@ func TestDecodeDiscoveryChainRead(t *testing.T) {
|
|||
t.Run(tc.desc, func(t *testing.T) {
|
||||
jsonStr := fmt.Sprintf(tc.jsonFmtStr, tc.in...)
|
||||
body := bytes.NewBuffer([]byte(jsonStr))
|
||||
req := httptest.NewRequest("POST", "http://foo.com", body)
|
||||
|
||||
var out discoveryChainReadRequest
|
||||
// fullDecodeFn is declared above in this test.
|
||||
err := fullDecodeFn(req, &out)
|
||||
err := decodeBody(body, &out)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -2589,14 +2261,6 @@ func TestDecodeDiscoveryChainRead(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/intentions_endpoint.go:
|
||||
// 66 s.parseDC(req, &args.Datacenter)
|
||||
// 67 s.parseToken(req, &args.Token)
|
||||
// 68: if err := decodeBody(req, &args.Intention, fixHashField); err != nil {
|
||||
// 69 return nil, fmt.Errorf("Failed to decode request body: %s", err)
|
||||
// 70 }
|
||||
// ==================================
|
||||
// IntentionRequest:
|
||||
// Datacenter string
|
||||
// Op structs.IntentionOp
|
||||
|
@ -2639,13 +2303,12 @@ func TestDecodeIntentionCreate(t *testing.T) {
|
|||
"Hash": %s
|
||||
}`, createdAt, updatedAt, hash))
|
||||
|
||||
// set up request
|
||||
body := bytes.NewBuffer(bodyBytes)
|
||||
req := httptest.NewRequest("POST", "http://foo.com", body)
|
||||
|
||||
// decode body
|
||||
var out structs.Intention
|
||||
err := decodeBody(req, &out, fixHashField)
|
||||
err := decodeBody(body, &out)
|
||||
|
||||
if tc.hashes != nil {
|
||||
// We should only check tc.wantErr for hashes in this case.
|
||||
//
|
||||
|
@ -2687,44 +2350,6 @@ func TestDecodeIntentionCreate(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/intentions_endpoint.go:
|
||||
// 259 s.parseDC(req, &args.Datacenter)
|
||||
// 260 s.parseToken(req, &args.Token)
|
||||
// 261: if err := decodeBody(req, &args.Intention, fixHashField); err != nil {
|
||||
// 262 return nil, BadRequestError{Reason: fmt.Sprintf("Request decode failed: %v", err)}
|
||||
// 263 }
|
||||
// ==================================
|
||||
func TestDecodeIntentionSpecificUpdate(t *testing.T) {
|
||||
t.Skip("DONE. COVERED BY ABOVE (same structs.Intention)")
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/operator_endpoint.go:
|
||||
// 77 var args keyringArgs
|
||||
// 78 if req.Method == "POST" || req.Method == "PUT" || req.Method == "DELETE" {
|
||||
// 79: if err := decodeBody(req, &args, nil); err != nil {
|
||||
// 80 return nil, BadRequestError{Reason: fmt.Sprintf("Request decode failed: %v", err)}
|
||||
// 81 }
|
||||
// ==================================
|
||||
// type keyringArgs struct {
|
||||
// Key string
|
||||
// Token string
|
||||
// RelayFactor uint8
|
||||
// LocalOnly bool // ?local-only; only used for GET requests
|
||||
// }
|
||||
func TestDecodeOperatorKeyringEndpoint(t *testing.T) {
|
||||
t.Skip("DONE. no special fields to parse; no decodeBody callback used.")
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/operator_endpoint.go:
|
||||
// 219 var conf api.AutopilotConfiguration
|
||||
// 220 durations := NewDurationFixer("lastcontactthreshold", "serverstabilizationtime")
|
||||
// 221: if err := decodeBody(req, &conf, durations.FixupDurations); err != nil {
|
||||
// 222 return nil, BadRequestError{Reason: fmt.Sprintf("Error parsing autopilot config: %v", err)}
|
||||
// 223 }
|
||||
// ==================================
|
||||
// AutopilotConfiguration:
|
||||
// CleanupDeadServers bool
|
||||
// LastContactThreshold *api.ReadableDuration
|
||||
|
@ -2745,10 +2370,10 @@ func TestDecodeOperatorAutopilotConfiguration(t *testing.T) {
|
|||
}`, tc.durations.in)
|
||||
|
||||
body := bytes.NewBuffer([]byte(jsonStr))
|
||||
req := httptest.NewRequest("POST", "http://foo.com", body)
|
||||
|
||||
var out api.AutopilotConfiguration
|
||||
err := decodeBody(req, &out, durations.FixupDurations)
|
||||
err := decodeBody(body, &out)
|
||||
|
||||
if err == nil && tc.wantErr {
|
||||
t.Fatal("expected err, got nil")
|
||||
}
|
||||
|
@ -2774,69 +2399,6 @@ func TestDecodeOperatorAutopilotConfiguration(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/prepared_query_endpoint.go:
|
||||
// 24 s.parseDC(req, &args.Datacenter)
|
||||
// 25 s.parseToken(req, &args.Token)
|
||||
// 26: if err := decodeBody(req, &args.Query, nil); err != nil {
|
||||
// 27 resp.WriteHeader(http.StatusBadRequest)
|
||||
// 28 fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
// ==================================
|
||||
// PreparedQueryRequest:
|
||||
// Datacenter string
|
||||
// Op structs.PreparedQueryOp
|
||||
// Query *structs.PreparedQuery
|
||||
// ID string
|
||||
// Name string
|
||||
// Session string
|
||||
// Token string
|
||||
// Template structs.QueryTemplateOptions
|
||||
// Type string
|
||||
// Regexp string
|
||||
// RemoveEmptyTags bool
|
||||
// Service structs.ServiceQuery
|
||||
// Service string
|
||||
// Failover structs.QueryDatacenterOptions
|
||||
// NearestN int
|
||||
// Datacenters []string
|
||||
// OnlyPassing bool
|
||||
// IgnoreCheckIDs []types.CheckID
|
||||
// Near string
|
||||
// Tags []string
|
||||
// NodeMeta map[string]string
|
||||
// ServiceMeta map[string]string
|
||||
// Connect bool
|
||||
// DNS structs.QueryDNSOptions
|
||||
// TTL string
|
||||
// RaftIndex structs.RaftIndex
|
||||
// CreateIndex uint64
|
||||
// ModifyIndex uint64
|
||||
// WriteRequest structs.WriteRequest
|
||||
// Token string
|
||||
func TestDecodePreparedQueryGeneral_Create(t *testing.T) {
|
||||
t.Skip("DONE. no special fields to parse; no decodeBody callback used.")
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/prepared_query_endpoint.go:
|
||||
// 254 s.parseToken(req, &args.Token)
|
||||
// 255 if req.ContentLength > 0 {
|
||||
// 256: if err := decodeBody(req, &args.Query, nil); err != nil {
|
||||
// 257 resp.WriteHeader(http.StatusBadRequest)
|
||||
// 258 fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
// ==================================
|
||||
func TestDecodePreparedQueryGeneral_Update(t *testing.T) {
|
||||
t.Skip("DONE. COVERED BY ABOVE (same structs.PreparedQuery)")
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/session_endpoint.go:
|
||||
// 54 return nil
|
||||
// 55 }
|
||||
// 56: if err := decodeBody(req, &args.Session, fixup); err != nil {
|
||||
// 57 resp.WriteHeader(http.StatusBadRequest)
|
||||
// 58 fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
// ==================================
|
||||
// SessionRequest:
|
||||
// Datacenter string
|
||||
// Op structs.SessionOp
|
||||
|
@ -2854,22 +2416,10 @@ func TestDecodePreparedQueryGeneral_Update(t *testing.T) {
|
|||
// WriteRequest structs.WriteRequest
|
||||
// Token string
|
||||
func TestDecodeSessionCreate(t *testing.T) {
|
||||
|
||||
// outSession var is shared among test cases b/c of the
|
||||
// nature/signature of the FixupChecks callback.
|
||||
var outSession structs.Session
|
||||
|
||||
// copied from agent/session_endpoint.go
|
||||
fixupCB := func(raw interface{}) error {
|
||||
if err := FixupLockDelay(raw); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := FixupChecks(raw, &outSession); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// lockDelayMinThreshold = 1000
|
||||
|
||||
sessionDurationTCs := append(positiveDurationTCs,
|
||||
|
@ -2901,25 +2451,6 @@ func TestDecodeSessionCreate(t *testing.T) {
|
|||
want: -5 * time.Second,
|
||||
},
|
||||
},
|
||||
// // Test cases that illicit bad behavior; Don't run them.
|
||||
// translateValueTestCase{
|
||||
// desc: "durations large, numeric and negative",
|
||||
// durations: &durationTC{
|
||||
// in: `-2000`,
|
||||
// want: time.Duration(-2000),
|
||||
// },
|
||||
// // --- FAIL: TestDecodeSessionCreate/durations_large,_numeric_and_negative (0.00s)
|
||||
// // http_decode_test.go:2665: expected LockDelay to be -2µs, got -33m20s
|
||||
// },
|
||||
// translateValueTestCase{
|
||||
// desc: "durations string, negative",
|
||||
// durations: &durationTC{
|
||||
// in: `"-50ms"`,
|
||||
// want: -50 * time.Millisecond,
|
||||
// },
|
||||
// // --- FAIL: TestDecodeSessionCreate/durations_string,_negative (0.00s)
|
||||
// // http_decode_test.go:2665: expected LockDelay to be -50ms, got -13888h53m20s
|
||||
// },
|
||||
)
|
||||
|
||||
for _, tc := range sessionDurationTCs {
|
||||
|
@ -2935,10 +2466,10 @@ func TestDecodeSessionCreate(t *testing.T) {
|
|||
}`, tc.durations.in)
|
||||
|
||||
body := bytes.NewBuffer([]byte(jsonStr))
|
||||
req := httptest.NewRequest("POST", "http://foo.com", body)
|
||||
|
||||
// outSession var is shared among test cases
|
||||
err := decodeBody(req, &outSession, fixupCB)
|
||||
|
||||
err := decodeBody(body, &outSession)
|
||||
if err == nil && tc.wantErr {
|
||||
t.Fatal("expected err, got nil")
|
||||
}
|
||||
|
@ -3002,10 +2533,8 @@ func TestDecodeSessionCreate(t *testing.T) {
|
|||
}`, tc.in)
|
||||
|
||||
body := bytes.NewBuffer([]byte(jsonStr))
|
||||
req := httptest.NewRequest("POST", "http://foo.com", body)
|
||||
|
||||
// outSession var is shared among test cases
|
||||
err := decodeBody(req, &outSession, fixupCB)
|
||||
err := decodeBody(body, &outSession)
|
||||
if err == nil && tc.wantErr {
|
||||
t.Fatal("expected err, got nil")
|
||||
}
|
||||
|
@ -3022,17 +2551,8 @@ func TestDecodeSessionCreate(t *testing.T) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/txn_endpoint.go:
|
||||
// 116 // associate the error with a given operation.
|
||||
// 117 var ops api.TxnOps
|
||||
// 118: if err := decodeBody(req, &ops, fixupTxnOps); err != nil {
|
||||
// 119 resp.WriteHeader(http.StatusBadRequest)
|
||||
// 120 fmt.Fprintf(resp, "Failed to parse body: %v", err)
|
||||
// ==================================
|
||||
// TxnOps:
|
||||
// KV *api.KVTxnOp
|
||||
// Verb api.KVOp
|
||||
|
@ -3181,10 +2701,10 @@ func TestDecodeTxnConvertOps(t *testing.T) {
|
|||
}]`, tc.durations.in)
|
||||
|
||||
body := bytes.NewBuffer([]byte(jsonStr))
|
||||
req := httptest.NewRequest("POST", "http://foo.com", body)
|
||||
|
||||
var out api.TxnOps
|
||||
err := decodeBody(req, &out, fixupTxnOps)
|
||||
err := decodeBody(body, &out)
|
||||
|
||||
if err == nil && tc.wantErr {
|
||||
t.Fatal("expected err, got nil")
|
||||
}
|
||||
|
@ -3226,20 +2746,6 @@ func TestDecodeTxnConvertOps(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// =======================================
|
||||
// Benchmarks:
|
||||
// ==================================
|
||||
// $GOPATH/github.com/hashicorp/consul/agent/http.go:
|
||||
// 574
|
||||
// 575 // decodeBody is used to decode a JSON request body
|
||||
// 576: func decodeBody(req *http.Request, out interface{}, cb func(interface{}) error) error {
|
||||
// 577 // This generally only happens in tests since real HTTP requests set
|
||||
// 578 // a non-nil body with no content. We guard against it anyways to prevent
|
||||
// ==================================
|
||||
func BenchmarkDecodeBody(b *testing.B) {
|
||||
b.Skip() // TODO: benchmark
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Helper funcs:
|
||||
// =========================================
|
||||
|
|
|
@ -9,21 +9,6 @@ import (
|
|||
"github.com/hashicorp/consul/agent/structs"
|
||||
)
|
||||
|
||||
// fixHashField is used to convert the JSON string to a []byte before handing to mapstructure
|
||||
func fixHashField(raw interface{}) error {
|
||||
rawMap, ok := raw.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
if val, ok := rawMap["Hash"]; ok {
|
||||
if sval, ok := val.(string); ok {
|
||||
rawMap["Hash"] = []byte(sval)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// /v1/connection/intentions
|
||||
func (s *HTTPServer) IntentionEndpoint(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
switch req.Method {
|
||||
|
@ -65,7 +50,7 @@ func (s *HTTPServer) IntentionCreate(resp http.ResponseWriter, req *http.Request
|
|||
}
|
||||
s.parseDC(req, &args.Datacenter)
|
||||
s.parseToken(req, &args.Token)
|
||||
if err := decodeBody(req, &args.Intention, fixHashField); err != nil {
|
||||
if err := decodeBody(req.Body, &args.Intention); err != nil {
|
||||
return nil, fmt.Errorf("Failed to decode request body: %s", err)
|
||||
}
|
||||
|
||||
|
@ -258,7 +243,7 @@ func (s *HTTPServer) IntentionSpecificUpdate(id string, resp http.ResponseWriter
|
|||
}
|
||||
s.parseDC(req, &args.Datacenter)
|
||||
s.parseToken(req, &args.Token)
|
||||
if err := decodeBody(req, &args.Intention, fixHashField); err != nil {
|
||||
if err := decodeBody(req.Body, &args.Intention); err != nil {
|
||||
return nil, BadRequestError{Reason: fmt.Sprintf("Request decode failed: %v", err)}
|
||||
}
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ type keyringArgs struct {
|
|||
func (s *HTTPServer) OperatorKeyringEndpoint(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
var args keyringArgs
|
||||
if req.Method == "POST" || req.Method == "PUT" || req.Method == "DELETE" {
|
||||
if err := decodeBody(req, &args, nil); err != nil {
|
||||
if err := decodeBody(req.Body, &args); err != nil {
|
||||
return nil, BadRequestError{Reason: fmt.Sprintf("Request decode failed: %v", err)}
|
||||
}
|
||||
}
|
||||
|
@ -218,8 +218,7 @@ func (s *HTTPServer) OperatorAutopilotConfiguration(resp http.ResponseWriter, re
|
|||
s.parseToken(req, &args.Token)
|
||||
|
||||
var conf api.AutopilotConfiguration
|
||||
durations := NewDurationFixer("lastcontactthreshold", "serverstabilizationtime")
|
||||
if err := decodeBody(req, &conf, durations.FixupDurations); err != nil {
|
||||
if err := decodeBody(req.Body, &conf); err != nil {
|
||||
return nil, BadRequestError{Reason: fmt.Sprintf("Error parsing autopilot config: %v", err)}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ func (s *HTTPServer) preparedQueryCreate(resp http.ResponseWriter, req *http.Req
|
|||
}
|
||||
s.parseDC(req, &args.Datacenter)
|
||||
s.parseToken(req, &args.Token)
|
||||
if err := decodeBody(req, &args.Query, nil); err != nil {
|
||||
if err := decodeBody(req.Body, &args.Query); err != nil {
|
||||
resp.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
return nil, nil
|
||||
|
@ -253,7 +253,7 @@ func (s *HTTPServer) preparedQueryUpdate(id string, resp http.ResponseWriter, re
|
|||
s.parseDC(req, &args.Datacenter)
|
||||
s.parseToken(req, &args.Token)
|
||||
if req.ContentLength > 0 {
|
||||
if err := decodeBody(req, &args.Query, nil); err != nil {
|
||||
if err := decodeBody(req.Body, &args.Query); err != nil {
|
||||
resp.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
return nil, nil
|
||||
|
|
|
@ -10,16 +10,6 @@ import (
|
|||
"github.com/hashicorp/consul/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// lockDelayMinThreshold is used to convert a numeric lock
|
||||
// delay value from nanoseconds to seconds if it is below this
|
||||
// threshold. Users often send a value like 5, which they assume
|
||||
// is seconds, but because Go uses nanosecond granularity, ends
|
||||
// up being very small. If we see a value below this threshold,
|
||||
// we multiply by time.Second
|
||||
lockDelayMinThreshold = 1000
|
||||
)
|
||||
|
||||
// sessionCreateResponse is used to wrap the session ID
|
||||
type sessionCreateResponse struct {
|
||||
ID string
|
||||
|
@ -44,16 +34,7 @@ func (s *HTTPServer) SessionCreate(resp http.ResponseWriter, req *http.Request)
|
|||
|
||||
// Handle optional request body
|
||||
if req.ContentLength > 0 {
|
||||
fixup := func(raw interface{}) error {
|
||||
if err := FixupLockDelay(raw); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := FixupChecks(raw, &args.Session); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := decodeBody(req, &args.Session, fixup); err != nil {
|
||||
if err := decodeBody(req.Body, &args.Session); err != nil {
|
||||
resp.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(resp, "Request decode failed: %v", err)
|
||||
return nil, nil
|
||||
|
@ -70,45 +51,6 @@ func (s *HTTPServer) SessionCreate(resp http.ResponseWriter, req *http.Request)
|
|||
return sessionCreateResponse{out}, nil
|
||||
}
|
||||
|
||||
// FixupLockDelay is used to handle parsing the JSON body to session/create
|
||||
// and properly parsing out the lock delay duration value.
|
||||
func FixupLockDelay(raw interface{}) error {
|
||||
rawMap, ok := raw.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
var key string
|
||||
for k := range rawMap {
|
||||
if strings.ToLower(k) == "lockdelay" {
|
||||
key = k
|
||||
break
|
||||
}
|
||||
}
|
||||
if key != "" {
|
||||
val := rawMap[key]
|
||||
// Convert a string value into an integer
|
||||
if vStr, ok := val.(string); ok {
|
||||
dur, err := time.ParseDuration(vStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dur < lockDelayMinThreshold {
|
||||
dur = dur * time.Second
|
||||
}
|
||||
rawMap[key] = dur
|
||||
}
|
||||
// Convert low value integers into seconds
|
||||
if vNum, ok := val.(float64); ok {
|
||||
dur := time.Duration(vNum)
|
||||
if dur < lockDelayMinThreshold {
|
||||
dur = dur * time.Second
|
||||
}
|
||||
rawMap[key] = dur
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FixupChecks is used to handle parsing the JSON body to default-add the Serf
|
||||
// health check if they didn't specify any checks, but to allow an empty list
|
||||
// to take out the Serf health check. This behavior broke when mapstructure was
|
||||
|
|
|
@ -223,39 +223,6 @@ func TestSessionCreate_NoCheck(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestFixupLockDelay(t *testing.T) {
|
||||
t.Parallel()
|
||||
inp := map[string]interface{}{
|
||||
"lockdelay": float64(15),
|
||||
}
|
||||
if err := FixupLockDelay(inp); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if inp["lockdelay"] != 15*time.Second {
|
||||
t.Fatalf("bad: %v", inp)
|
||||
}
|
||||
|
||||
inp = map[string]interface{}{
|
||||
"lockDelay": float64(15 * time.Second),
|
||||
}
|
||||
if err := FixupLockDelay(inp); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if inp["lockDelay"] != 15*time.Second {
|
||||
t.Fatalf("bad: %v", inp)
|
||||
}
|
||||
|
||||
inp = map[string]interface{}{
|
||||
"LockDelay": "15s",
|
||||
}
|
||||
if err := FixupLockDelay(inp); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if inp["LockDelay"] != 15*time.Second {
|
||||
t.Fatalf("bad: %v", inp)
|
||||
}
|
||||
}
|
||||
|
||||
func makeTestSession(t *testing.T, srv *HTTPServer) string {
|
||||
req, _ := http.NewRequest("PUT", "/v1/session/create", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
|
|
|
@ -2,6 +2,7 @@ package structs
|
|||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
|
@ -260,6 +261,35 @@ type ACLToken struct {
|
|||
RaftIndex
|
||||
}
|
||||
|
||||
func (t *ACLToken) UnmarshalJSON(data []byte) (err error) {
|
||||
type Alias ACLToken
|
||||
aux := &struct {
|
||||
ExpirationTTL interface{}
|
||||
Hash string
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(t),
|
||||
}
|
||||
if err = json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
if aux.ExpirationTTL != nil {
|
||||
switch v := aux.ExpirationTTL.(type) {
|
||||
case string:
|
||||
if t.ExpirationTTL, err = time.ParseDuration(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case float64:
|
||||
t.ExpirationTTL = time.Duration(v)
|
||||
}
|
||||
|
||||
}
|
||||
if aux.Hash != "" {
|
||||
t.Hash = []byte(aux.Hash)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *ACLToken) Clone() *ACLToken {
|
||||
t2 := *t
|
||||
t2.Policies = nil
|
||||
|
@ -539,6 +569,23 @@ type ACLPolicy struct {
|
|||
RaftIndex `hash:"ignore"`
|
||||
}
|
||||
|
||||
func (t *ACLPolicy) UnmarshalJSON(data []byte) error {
|
||||
type Alias ACLPolicy
|
||||
aux := &struct {
|
||||
Hash string
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(t),
|
||||
}
|
||||
if err := json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
if aux.Hash != "" {
|
||||
t.Hash = []byte(aux.Hash)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ACLPolicy) Clone() *ACLPolicy {
|
||||
p2 := *p
|
||||
p2.Datacenters = cloneStringSlice(p.Datacenters)
|
||||
|
@ -768,6 +815,23 @@ type ACLRole struct {
|
|||
RaftIndex `hash:"ignore"`
|
||||
}
|
||||
|
||||
func (t *ACLRole) UnmarshalJSON(data []byte) error {
|
||||
type Alias ACLRole
|
||||
aux := &struct {
|
||||
Hash string
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(t),
|
||||
}
|
||||
if err := json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
if aux.Hash != "" {
|
||||
t.Hash = []byte(aux.Hash)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ACLRole) Clone() *ACLRole {
|
||||
r2 := *r
|
||||
r2.Policies = nil
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
|
@ -42,6 +43,98 @@ type CheckDefinition struct {
|
|||
OutputMaxSize int
|
||||
}
|
||||
|
||||
func (t *CheckDefinition) UnmarshalJSON(data []byte) (err error) {
|
||||
type Alias CheckDefinition
|
||||
aux := &struct {
|
||||
// Parse special values
|
||||
Interval interface{}
|
||||
Timeout interface{}
|
||||
TTL interface{}
|
||||
DeregisterCriticalServiceAfter interface{}
|
||||
|
||||
// Translate fields
|
||||
|
||||
// "args" -> ScriptArgs
|
||||
Args []string `json:"args"`
|
||||
ScriptArgsSnake []string `json:"script_args"`
|
||||
DeregisterCriticalServiceAfterSnake interface{} `json:"deregister_critical_service_after"`
|
||||
DockerContainerIDSnake string `json:"docker_container_id"`
|
||||
TLSSkipVerifySnake bool `json:"tls_skip_verify"`
|
||||
ServiceIDSnake string `json:"service_id"`
|
||||
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(t),
|
||||
}
|
||||
if err = json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Translate Fields
|
||||
if aux.DeregisterCriticalServiceAfter == nil {
|
||||
aux.DeregisterCriticalServiceAfter = aux.DeregisterCriticalServiceAfterSnake
|
||||
}
|
||||
if len(t.ScriptArgs) == 0 {
|
||||
t.ScriptArgs = aux.Args
|
||||
}
|
||||
if len(t.ScriptArgs) == 0 {
|
||||
t.ScriptArgs = aux.ScriptArgsSnake
|
||||
}
|
||||
if t.DockerContainerID == "" {
|
||||
t.DockerContainerID = aux.DockerContainerIDSnake
|
||||
}
|
||||
if aux.TLSSkipVerifySnake {
|
||||
t.TLSSkipVerify = aux.TLSSkipVerifySnake
|
||||
}
|
||||
if t.ServiceID == "" {
|
||||
t.ServiceID = aux.ServiceIDSnake
|
||||
}
|
||||
|
||||
// Parse special values
|
||||
if aux.Interval != nil {
|
||||
switch v := aux.Interval.(type) {
|
||||
case string:
|
||||
if t.Interval, err = time.ParseDuration(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case float64:
|
||||
t.Interval = time.Duration(v)
|
||||
}
|
||||
}
|
||||
if aux.Timeout != nil {
|
||||
switch v := aux.Timeout.(type) {
|
||||
case string:
|
||||
if t.Timeout, err = time.ParseDuration(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case float64:
|
||||
t.Timeout = time.Duration(v)
|
||||
}
|
||||
}
|
||||
if aux.TTL != nil {
|
||||
switch v := aux.TTL.(type) {
|
||||
case string:
|
||||
if t.TTL, err = time.ParseDuration(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case float64:
|
||||
t.TTL = time.Duration(v)
|
||||
}
|
||||
}
|
||||
if aux.DeregisterCriticalServiceAfter != nil {
|
||||
switch v := aux.DeregisterCriticalServiceAfter.(type) {
|
||||
case string:
|
||||
if t.DeregisterCriticalServiceAfter, err = time.ParseDuration(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case float64:
|
||||
t.DeregisterCriticalServiceAfter = time.Duration(v)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CheckDefinition) HealthCheck(node string) *HealthCheck {
|
||||
health := &HealthCheck{
|
||||
Node: node,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
@ -8,6 +9,8 @@ import (
|
|||
"github.com/hashicorp/consul/types"
|
||||
)
|
||||
|
||||
type CheckTypes []*CheckType
|
||||
|
||||
// CheckType is used to create either the CheckMonitor or the CheckTTL.
|
||||
// The following types are supported: Script, HTTP, TCP, Docker, TTL, GRPC, Alias. Script,
|
||||
// HTTP, Docker, TCP and GRPC all require Interval. Only one of the types may
|
||||
|
@ -55,7 +58,91 @@ type CheckType struct {
|
|||
DeregisterCriticalServiceAfter time.Duration
|
||||
OutputMaxSize int
|
||||
}
|
||||
type CheckTypes []*CheckType
|
||||
|
||||
func (t *CheckType) UnmarshalJSON(data []byte) (err error) {
|
||||
type Alias CheckType
|
||||
aux := &struct {
|
||||
Interval interface{}
|
||||
Timeout interface{}
|
||||
TTL interface{}
|
||||
DeregisterCriticalServiceAfter interface{}
|
||||
|
||||
// Translate fields
|
||||
|
||||
// "args" -> ScriptArgs
|
||||
Args []string `json:"args"`
|
||||
ScriptArgsSnake []string `json:"script_args"`
|
||||
DeregisterCriticalServiceAfterSnake interface{} `json:"deregister_critical_service_after"`
|
||||
DockerContainerIDSnake string `json:"docker_container_id"`
|
||||
TLSSkipVerifySnake bool `json:"tls_skip_verify"`
|
||||
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(t),
|
||||
}
|
||||
if err = json.Unmarshal(data, aux); err != nil {
|
||||
return err
|
||||
}
|
||||
if aux.DeregisterCriticalServiceAfter == nil {
|
||||
aux.DeregisterCriticalServiceAfter = aux.DeregisterCriticalServiceAfterSnake
|
||||
}
|
||||
if len(t.ScriptArgs) == 0 {
|
||||
t.ScriptArgs = aux.Args
|
||||
}
|
||||
if len(t.ScriptArgs) == 0 {
|
||||
t.ScriptArgs = aux.ScriptArgsSnake
|
||||
}
|
||||
if t.DockerContainerID == "" {
|
||||
t.DockerContainerID = aux.DockerContainerIDSnake
|
||||
}
|
||||
if aux.TLSSkipVerifySnake {
|
||||
t.TLSSkipVerify = aux.TLSSkipVerifySnake
|
||||
}
|
||||
|
||||
if aux.Interval != nil {
|
||||
switch v := aux.Interval.(type) {
|
||||
case string:
|
||||
if t.Interval, err = time.ParseDuration(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case float64:
|
||||
t.Interval = time.Duration(v)
|
||||
}
|
||||
}
|
||||
if aux.Timeout != nil {
|
||||
switch v := aux.Timeout.(type) {
|
||||
case string:
|
||||
if t.Timeout, err = time.ParseDuration(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case float64:
|
||||
t.Timeout = time.Duration(v)
|
||||
}
|
||||
}
|
||||
if aux.TTL != nil {
|
||||
switch v := aux.TTL.(type) {
|
||||
case string:
|
||||
if t.TTL, err = time.ParseDuration(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case float64:
|
||||
t.TTL = time.Duration(v)
|
||||
}
|
||||
}
|
||||
if aux.DeregisterCriticalServiceAfter != nil {
|
||||
switch v := aux.DeregisterCriticalServiceAfter.(type) {
|
||||
case string:
|
||||
if t.DeregisterCriticalServiceAfter, err = time.ParseDuration(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case float64:
|
||||
t.DeregisterCriticalServiceAfter = time.Duration(v)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// Validate returns an error message if the check is invalid
|
||||
func (c *CheckType) Validate() error {
|
||||
|
|
|
@ -3,9 +3,10 @@ package structs
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/consul/lib"
|
||||
"log"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -120,6 +121,38 @@ type ConnectProxyConfig struct {
|
|||
Expose ExposeConfig `json:",omitempty"`
|
||||
}
|
||||
|
||||
func (t *ConnectProxyConfig) UnmarshalJSON(data []byte) (err error) {
|
||||
type Alias ConnectProxyConfig
|
||||
aux := &struct {
|
||||
DestinationServiceNameSnake string `json:"destination_service_name"`
|
||||
DestinationServiceIDSnake string `json:"destination_service_id"`
|
||||
LocalServiceAddressSnake string `json:"local_service_address"`
|
||||
LocalServicePortSnake int `json:"local_service_port"`
|
||||
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(t),
|
||||
}
|
||||
if err = json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
if t.DestinationServiceName == "" {
|
||||
t.DestinationServiceName = aux.DestinationServiceNameSnake
|
||||
}
|
||||
if t.DestinationServiceID == "" {
|
||||
t.DestinationServiceID = aux.DestinationServiceIDSnake
|
||||
}
|
||||
if t.LocalServiceAddress == "" {
|
||||
t.LocalServiceAddress = aux.LocalServiceAddressSnake
|
||||
}
|
||||
if t.LocalServicePort == 0 {
|
||||
t.LocalServicePort = aux.LocalServicePortSnake
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (c *ConnectProxyConfig) MarshalJSON() ([]byte, error) {
|
||||
type typeCopy ConnectProxyConfig
|
||||
copy := typeCopy(*c)
|
||||
|
@ -217,6 +250,41 @@ type Upstream struct {
|
|||
MeshGateway MeshGatewayConfig `json:",omitempty"`
|
||||
}
|
||||
|
||||
func (t *Upstream) UnmarshalJSON(data []byte) (err error) {
|
||||
type Alias Upstream
|
||||
aux := &struct {
|
||||
DestinationTypeSnake string `json:"destination_type"`
|
||||
DestinationNamespaceSnake string `json:"destination_namespace"`
|
||||
DestinationNameSnake string `json:"destination_name"`
|
||||
LocalBindPortSnake int `json:"local_bind_port"`
|
||||
LocalBindAddressSnake string `json:"local_bind_address"`
|
||||
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(t),
|
||||
}
|
||||
if err = json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
if t.DestinationType == "" {
|
||||
t.DestinationType = aux.DestinationTypeSnake
|
||||
}
|
||||
if t.DestinationNamespace == "" {
|
||||
t.DestinationNamespace = aux.DestinationNamespaceSnake
|
||||
}
|
||||
if t.DestinationName == "" {
|
||||
t.DestinationName = aux.DestinationNameSnake
|
||||
}
|
||||
if t.LocalBindPort == 0 {
|
||||
t.LocalBindPort = aux.LocalBindPortSnake
|
||||
}
|
||||
if t.LocalBindAddress == "" {
|
||||
t.LocalBindAddress = aux.LocalBindAddressSnake
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate sanity checks the struct is valid
|
||||
func (u *Upstream) Validate() error {
|
||||
switch u.DestinationType {
|
||||
|
@ -352,6 +420,29 @@ type ExposePath struct {
|
|||
ParsedFromCheck bool
|
||||
}
|
||||
|
||||
func (t *ExposePath) UnmarshalJSON(data []byte) (err error) {
|
||||
type Alias ExposePath
|
||||
aux := &struct {
|
||||
LocalPathPortSnake int `json:"local_path_port"`
|
||||
ListenerPortSnake int `json:"listener_port"`
|
||||
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(t),
|
||||
}
|
||||
if err = json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
if t.LocalPathPort == 0 {
|
||||
t.LocalPathPort = aux.LocalPathPortSnake
|
||||
}
|
||||
if t.ListenerPort == 0 {
|
||||
t.ListenerPort = aux.ListenerPortSnake
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ExposeConfig) ToAPI() api.ExposeConfig {
|
||||
paths := make([]api.ExposePath, 0)
|
||||
for _, p := range e.Paths {
|
||||
|
|
|
@ -2,6 +2,7 @@ package structs
|
|||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
@ -84,6 +85,26 @@ type Intention struct {
|
|||
RaftIndex
|
||||
}
|
||||
|
||||
func (t *Intention) UnmarshalJSON(data []byte) (err error) {
|
||||
type Alias Intention
|
||||
aux := &struct {
|
||||
Hash string
|
||||
CreatedAt, UpdatedAt string // effectively `json:"-"` on Intention type
|
||||
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(t),
|
||||
}
|
||||
if err = json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if aux.Hash != "" {
|
||||
t.Hash = []byte(aux.Hash)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Intention) SetHash(force bool) []byte {
|
||||
if force || x.Hash == nil {
|
||||
hash, err := blake2b.New256(nil)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
)
|
||||
|
||||
|
@ -31,6 +33,30 @@ type ServiceDefinition struct {
|
|||
Connect *ServiceConnect
|
||||
}
|
||||
|
||||
func (t *ServiceDefinition) UnmarshalJSON(data []byte) (err error) {
|
||||
type Alias ServiceDefinition
|
||||
|
||||
aux := &struct {
|
||||
EnableTagOverrideSnake bool `json:"enable_tag_override"`
|
||||
TaggedAddressesSnake map[string]ServiceAddress `json:"tagged_addresses"`
|
||||
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(t),
|
||||
}
|
||||
if err = json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
if aux.EnableTagOverrideSnake {
|
||||
t.EnableTagOverride = aux.EnableTagOverrideSnake
|
||||
}
|
||||
if len(t.TaggedAddresses) == 0 {
|
||||
t.TaggedAddresses = aux.TaggedAddressesSnake
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ServiceDefinition) NodeService() *NodeService {
|
||||
ns := &NodeService{
|
||||
Kind: s.Kind,
|
||||
|
|
|
@ -99,6 +99,14 @@ const (
|
|||
// MaxLockDelay provides a maximum LockDelay value for
|
||||
// a session. Any value above this will not be respected.
|
||||
MaxLockDelay = 60 * time.Second
|
||||
|
||||
// lockDelayMinThreshold is used in JSON decoding to convert a
|
||||
// numeric lockdelay value from nanoseconds to seconds if it is
|
||||
// below thisthreshold. Users often send a value like 5, which
|
||||
// they assumeis seconds, but because Go uses nanosecond granularity,
|
||||
// ends up being very small. If we see a value below this threshold,
|
||||
// we multiply by time.Second
|
||||
lockDelayMinThreshold = 1000
|
||||
)
|
||||
|
||||
// metaKeyFormat checks if a metadata key string is valid
|
||||
|
@ -877,6 +885,24 @@ type ServiceConnect struct {
|
|||
SidecarService *ServiceDefinition `json:",omitempty" bexpr:"-"`
|
||||
}
|
||||
|
||||
func (t *ServiceConnect) UnmarshalJSON(data []byte) (err error) {
|
||||
type Alias ServiceConnect
|
||||
aux := &struct {
|
||||
SidecarServiceSnake *ServiceDefinition `json:"sidecar_service"`
|
||||
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(t),
|
||||
}
|
||||
if err = json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
if t.SidecarService == nil {
|
||||
t.SidecarService = aux.SidecarServiceSnake
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsSidecarProxy returns true if the NodeService is a sidecar proxy.
|
||||
func (s *NodeService) IsSidecarProxy() bool {
|
||||
return s.Kind == ServiceKindConnectProxy && s.Proxy.DestinationServiceID != ""
|
||||
|
@ -1194,33 +1220,58 @@ func (d *HealthCheckDefinition) MarshalJSON() ([]byte, error) {
|
|||
return json.Marshal(exported)
|
||||
}
|
||||
|
||||
func (d *HealthCheckDefinition) UnmarshalJSON(data []byte) error {
|
||||
func (t *HealthCheckDefinition) UnmarshalJSON(data []byte) (err error) {
|
||||
type Alias HealthCheckDefinition
|
||||
aux := &struct {
|
||||
Interval string
|
||||
Timeout string
|
||||
DeregisterCriticalServiceAfter string
|
||||
Interval interface{}
|
||||
Timeout interface{}
|
||||
DeregisterCriticalServiceAfter interface{}
|
||||
TTL interface{}
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(d),
|
||||
Alias: (*Alias)(t),
|
||||
}
|
||||
if err := json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
var err error
|
||||
if aux.Interval != "" {
|
||||
if d.Interval, err = time.ParseDuration(aux.Interval); err != nil {
|
||||
return err
|
||||
if aux.Interval != nil {
|
||||
switch v := aux.Interval.(type) {
|
||||
case string:
|
||||
if t.Interval, err = time.ParseDuration(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case float64:
|
||||
t.Interval = time.Duration(v)
|
||||
}
|
||||
}
|
||||
if aux.Timeout != "" {
|
||||
if d.Timeout, err = time.ParseDuration(aux.Timeout); err != nil {
|
||||
return err
|
||||
if aux.Timeout != nil {
|
||||
switch v := aux.Timeout.(type) {
|
||||
case string:
|
||||
if t.Timeout, err = time.ParseDuration(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case float64:
|
||||
t.Timeout = time.Duration(v)
|
||||
}
|
||||
}
|
||||
if aux.DeregisterCriticalServiceAfter != "" {
|
||||
if d.DeregisterCriticalServiceAfter, err = time.ParseDuration(aux.DeregisterCriticalServiceAfter); err != nil {
|
||||
return err
|
||||
if aux.DeregisterCriticalServiceAfter != nil {
|
||||
switch v := aux.DeregisterCriticalServiceAfter.(type) {
|
||||
case string:
|
||||
if t.DeregisterCriticalServiceAfter, err = time.ParseDuration(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case float64:
|
||||
t.DeregisterCriticalServiceAfter = time.Duration(v)
|
||||
}
|
||||
}
|
||||
if aux.TTL != nil {
|
||||
switch v := aux.TTL.(type) {
|
||||
case string:
|
||||
if t.TTL, err = time.ParseDuration(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case float64:
|
||||
t.TTL = time.Duration(v)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -1654,6 +1705,8 @@ const (
|
|||
SessionTTLMultiplier = 2
|
||||
)
|
||||
|
||||
type Sessions []*Session
|
||||
|
||||
// Session is used to represent an open session in the KV store.
|
||||
// This issued to associate node checks with acquired locks.
|
||||
type Session struct {
|
||||
|
@ -1667,7 +1720,36 @@ type Session struct {
|
|||
|
||||
RaftIndex
|
||||
}
|
||||
type Sessions []*Session
|
||||
|
||||
func (t *Session) UnmarshalJSON(data []byte) (err error) {
|
||||
type Alias Session
|
||||
aux := &struct {
|
||||
LockDelay interface{}
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(t),
|
||||
}
|
||||
if err = json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
if aux.LockDelay != nil {
|
||||
var dur time.Duration
|
||||
switch v := aux.LockDelay.(type) {
|
||||
case string:
|
||||
if dur, err = time.ParseDuration(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case float64:
|
||||
dur = time.Duration(v)
|
||||
}
|
||||
// Convert low value integers into seconds
|
||||
if dur < lockDelayMinThreshold {
|
||||
dur = dur * time.Second
|
||||
}
|
||||
t.LockDelay = dur
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SessionOp string
|
||||
|
||||
|
|
|
@ -51,40 +51,6 @@ func decodeValue(rawKV interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// fixupTxnOp looks for non-nil Txn operations and passes them on for
|
||||
// value conversion.
|
||||
func fixupTxnOp(rawOp interface{}) error {
|
||||
rawMap, ok := rawOp.(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected raw op type: %T", rawOp)
|
||||
}
|
||||
for k, v := range rawMap {
|
||||
switch strings.ToLower(k) {
|
||||
case "kv":
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
return decodeValue(v)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fixupTxnOps takes the raw decoded JSON and base64 decodes values in Txn ops,
|
||||
// replacing them with byte arrays.
|
||||
func fixupTxnOps(raw interface{}) error {
|
||||
rawSlice, ok := raw.([]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected raw type: %t", raw)
|
||||
}
|
||||
for _, rawOp := range rawSlice {
|
||||
if err := fixupTxnOp(rawOp); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isWrite returns true if the given operation alters the state store.
|
||||
func isWrite(op api.KVOp) bool {
|
||||
switch op {
|
||||
|
@ -115,7 +81,7 @@ func (s *HTTPServer) convertOps(resp http.ResponseWriter, req *http.Request) (st
|
|||
// decode it, we will return a 400 since we don't have enough context to
|
||||
// associate the error with a given operation.
|
||||
var ops api.TxnOps
|
||||
if err := decodeBody(req, &ops, fixupTxnOps); err != nil {
|
||||
if err := decodeBody(req.Body, &ops); err != nil {
|
||||
resp.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(resp, "Failed to parse body: %v", err)
|
||||
return nil, 0, false
|
||||
|
|
|
@ -1004,7 +1004,7 @@ func TestAPI_AgentChecks_Docker(t *testing.T) {
|
|||
t.Fatalf("missing service association for check: %v", check)
|
||||
}
|
||||
if check.Type != "docker" {
|
||||
t.Fatalf("expected type ttl, got %s", check.Type)
|
||||
t.Fatalf("expected type docker, got %s", check.Type)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,40 +95,63 @@ func (d *HealthCheckDefinition) MarshalJSON() ([]byte, error) {
|
|||
return json.Marshal(out)
|
||||
}
|
||||
|
||||
func (d *HealthCheckDefinition) UnmarshalJSON(data []byte) error {
|
||||
func (t *HealthCheckDefinition) UnmarshalJSON(data []byte) (err error) {
|
||||
type Alias HealthCheckDefinition
|
||||
aux := &struct {
|
||||
Interval string
|
||||
Timeout string
|
||||
DeregisterCriticalServiceAfter string
|
||||
IntervalDuration interface{}
|
||||
TimeoutDuration interface{}
|
||||
DeregisterCriticalServiceAfterDuration interface{}
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(d),
|
||||
Alias: (*Alias)(t),
|
||||
}
|
||||
if err := json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Parse the values into both the time.Duration and old ReadableDuration fields.
|
||||
var err error
|
||||
if aux.Interval != "" {
|
||||
if d.IntervalDuration, err = time.ParseDuration(aux.Interval); err != nil {
|
||||
return err
|
||||
|
||||
if aux.IntervalDuration == nil {
|
||||
t.IntervalDuration = time.Duration(t.Interval)
|
||||
} else {
|
||||
switch v := aux.IntervalDuration.(type) {
|
||||
case string:
|
||||
if t.IntervalDuration, err = time.ParseDuration(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case float64:
|
||||
t.IntervalDuration = time.Duration(v)
|
||||
}
|
||||
d.Interval = ReadableDuration(d.IntervalDuration)
|
||||
t.Interval = ReadableDuration(t.IntervalDuration)
|
||||
}
|
||||
if aux.Timeout != "" {
|
||||
if d.TimeoutDuration, err = time.ParseDuration(aux.Timeout); err != nil {
|
||||
return err
|
||||
|
||||
if aux.TimeoutDuration == nil {
|
||||
t.TimeoutDuration = time.Duration(t.Timeout)
|
||||
} else {
|
||||
switch v := aux.TimeoutDuration.(type) {
|
||||
case string:
|
||||
if t.TimeoutDuration, err = time.ParseDuration(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case float64:
|
||||
t.TimeoutDuration = time.Duration(v)
|
||||
}
|
||||
d.Timeout = ReadableDuration(d.TimeoutDuration)
|
||||
t.Timeout = ReadableDuration(t.TimeoutDuration)
|
||||
}
|
||||
if aux.DeregisterCriticalServiceAfter != "" {
|
||||
if d.DeregisterCriticalServiceAfterDuration, err = time.ParseDuration(aux.DeregisterCriticalServiceAfter); err != nil {
|
||||
return err
|
||||
if aux.DeregisterCriticalServiceAfterDuration == nil {
|
||||
t.DeregisterCriticalServiceAfterDuration = time.Duration(t.DeregisterCriticalServiceAfter)
|
||||
} else {
|
||||
switch v := aux.DeregisterCriticalServiceAfterDuration.(type) {
|
||||
case string:
|
||||
if t.DeregisterCriticalServiceAfterDuration, err = time.ParseDuration(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case float64:
|
||||
t.DeregisterCriticalServiceAfterDuration = time.Duration(v)
|
||||
}
|
||||
d.DeregisterCriticalServiceAfter = ReadableDuration(d.DeregisterCriticalServiceAfterDuration)
|
||||
t.DeregisterCriticalServiceAfter = ReadableDuration(t.DeregisterCriticalServiceAfterDuration)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -134,19 +134,28 @@ func (d *ReadableDuration) MarshalJSON() ([]byte, error) {
|
|||
return []byte(fmt.Sprintf(`"%s"`, d.Duration().String())), nil
|
||||
}
|
||||
|
||||
func (d *ReadableDuration) UnmarshalJSON(raw []byte) error {
|
||||
func (d *ReadableDuration) UnmarshalJSON(raw []byte) (err error) {
|
||||
if d == nil {
|
||||
return fmt.Errorf("cannot unmarshal to nil pointer")
|
||||
}
|
||||
|
||||
var dur time.Duration
|
||||
str := string(raw)
|
||||
if len(str) < 2 || str[0] != '"' || str[len(str)-1] != '"' {
|
||||
return fmt.Errorf("must be enclosed with quotes: %s", str)
|
||||
}
|
||||
dur, err := time.ParseDuration(str[1 : len(str)-1])
|
||||
if err != nil {
|
||||
return err
|
||||
if len(str) >= 2 && str[0] == '"' && str[len(str)-1] == '"' {
|
||||
// quoted string
|
||||
dur, err = time.ParseDuration(str[1 : len(str)-1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// no quotes, not a string
|
||||
v, err := strconv.ParseFloat(str, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dur = time.Duration(v)
|
||||
}
|
||||
|
||||
*d = ReadableDuration(dur)
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue