mirror of https://github.com/hashicorp/consul
128 lines
3.7 KiB
Go
128 lines
3.7 KiB
Go
|
package agent
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"net/http"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
cachetype "github.com/hashicorp/consul/agent/cache-types"
|
||
|
"github.com/hashicorp/consul/agent/structs"
|
||
|
"github.com/hashicorp/consul/lib"
|
||
|
"github.com/mitchellh/mapstructure"
|
||
|
)
|
||
|
|
||
|
func (s *HTTPServer) DiscoveryChainRead(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||
|
var args structs.DiscoveryChainRequest
|
||
|
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
args.Name = strings.TrimPrefix(req.URL.Path, "/v1/discovery-chain/")
|
||
|
if args.Name == "" {
|
||
|
return nil, BadRequestError{Reason: "Missing chain name"}
|
||
|
}
|
||
|
|
||
|
args.EvaluateInDatacenter = req.URL.Query().Get("compile-dc")
|
||
|
// TODO(namespaces): args.EvaluateInNamespace = req.URL.Query().Get("compile-namespace")
|
||
|
|
||
|
if req.Method == "POST" {
|
||
|
var raw map[string]interface{}
|
||
|
if err := decodeBody(req, &raw, nil); err != nil {
|
||
|
return nil, BadRequestError{Reason: fmt.Sprintf("Request decoding failed: %v", err)}
|
||
|
}
|
||
|
|
||
|
apiReq, err := decodeDiscoveryChainReadRequest(raw)
|
||
|
if err != nil {
|
||
|
return nil, BadRequestError{Reason: fmt.Sprintf("Request decoding failed: %v", err)}
|
||
|
}
|
||
|
|
||
|
args.OverrideProtocol = apiReq.OverrideProtocol
|
||
|
args.OverrideConnectTimeout = apiReq.OverrideConnectTimeout
|
||
|
|
||
|
if apiReq.OverrideMeshGateway.Mode != "" {
|
||
|
_, err := structs.ValidateMeshGatewayMode(string(apiReq.OverrideMeshGateway.Mode))
|
||
|
if err != nil {
|
||
|
resp.WriteHeader(http.StatusBadRequest)
|
||
|
fmt.Fprint(resp, "Invalid OverrideMeshGateway.Mode parameter")
|
||
|
return nil, nil
|
||
|
}
|
||
|
args.OverrideMeshGateway = apiReq.OverrideMeshGateway
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Make the RPC request
|
||
|
var out structs.DiscoveryChainResponse
|
||
|
defer setMeta(resp, &out.QueryMeta)
|
||
|
|
||
|
if args.QueryOptions.UseCache {
|
||
|
raw, m, err := s.agent.cache.Get(cachetype.CompiledDiscoveryChainName, &args)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defer setCacheMeta(resp, &m)
|
||
|
|
||
|
reply, ok := raw.(*structs.DiscoveryChainResponse)
|
||
|
if !ok {
|
||
|
// This should never happen, but we want to protect against panics
|
||
|
return nil, fmt.Errorf("internal error: response type not correct")
|
||
|
}
|
||
|
out = *reply
|
||
|
} else {
|
||
|
RETRY_ONCE:
|
||
|
if err := s.agent.RPC("DiscoveryChain.Get", &args, &out); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if args.QueryOptions.AllowStale && args.MaxStaleDuration > 0 && args.MaxStaleDuration < out.LastContact {
|
||
|
args.AllowStale = false
|
||
|
args.MaxStaleDuration = 0
|
||
|
goto RETRY_ONCE
|
||
|
}
|
||
|
}
|
||
|
out.ConsistencyLevel = args.QueryOptions.ConsistencyLevel()
|
||
|
|
||
|
return discoveryChainReadResponse{Chain: out.Chain}, nil
|
||
|
}
|
||
|
|
||
|
// discoveryChainReadRequest is the API variation of structs.DiscoveryChainRequest
|
||
|
type discoveryChainReadRequest struct {
|
||
|
OverrideMeshGateway structs.MeshGatewayConfig
|
||
|
OverrideProtocol string
|
||
|
OverrideConnectTimeout time.Duration
|
||
|
}
|
||
|
|
||
|
// discoveryChainReadResponse is the API variation of structs.DiscoveryChainResponse
|
||
|
type discoveryChainReadResponse struct {
|
||
|
Chain *structs.CompiledDiscoveryChain
|
||
|
}
|
||
|
|
||
|
func decodeDiscoveryChainReadRequest(raw map[string]interface{}) (*discoveryChainReadRequest, error) {
|
||
|
// lib.TranslateKeys doesn't understand []map[string]interface{} so we have
|
||
|
// to do this part first.
|
||
|
raw = lib.PatchSliceOfMaps(raw, nil, nil)
|
||
|
|
||
|
lib.TranslateKeys(raw, map[string]string{
|
||
|
"override_mesh_gateway": "overridemeshgateway",
|
||
|
"override_protocol": "overrideprotocol",
|
||
|
"override_connect_timeout": "overrideconnecttimeout",
|
||
|
})
|
||
|
|
||
|
var apiReq discoveryChainReadRequest
|
||
|
decodeConf := &mapstructure.DecoderConfig{
|
||
|
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
|
||
|
Result: &apiReq,
|
||
|
WeaklyTypedInput: true,
|
||
|
}
|
||
|
|
||
|
decoder, err := mapstructure.NewDecoder(decodeConf)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if err := decoder.Decode(raw); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return &apiReq, nil
|
||
|
}
|