2019-08-02 20:34:54 +00:00
|
|
|
package agent
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
2022-06-01 17:17:14 +00:00
|
|
|
"strings"
|
2019-08-02 20:34:54 +00:00
|
|
|
"time"
|
|
|
|
|
2020-10-04 17:54:56 +00:00
|
|
|
"github.com/mitchellh/mapstructure"
|
|
|
|
|
2022-04-05 21:10:06 +00:00
|
|
|
"github.com/hashicorp/consul/acl"
|
2019-08-02 20:34:54 +00:00
|
|
|
cachetype "github.com/hashicorp/consul/agent/cache-types"
|
|
|
|
"github.com/hashicorp/consul/agent/structs"
|
2020-05-27 18:42:01 +00:00
|
|
|
"github.com/hashicorp/consul/lib/decode"
|
2019-08-02 20:34:54 +00:00
|
|
|
)
|
|
|
|
|
2020-09-04 18:42:15 +00:00
|
|
|
func (s *HTTPHandlers) DiscoveryChainRead(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
2019-08-02 20:34:54 +00:00
|
|
|
var args structs.DiscoveryChainRequest
|
|
|
|
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2022-06-01 17:17:14 +00:00
|
|
|
args.Name = strings.TrimPrefix(req.URL.Path, "/v1/discovery-chain/")
|
2019-08-02 20:34:54 +00:00
|
|
|
if args.Name == "" {
|
2022-04-29 17:42:49 +00:00
|
|
|
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing chain name"}
|
2019-08-02 20:34:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
args.EvaluateInDatacenter = req.URL.Query().Get("compile-dc")
|
2022-04-05 21:10:06 +00:00
|
|
|
var entMeta acl.EnterpriseMeta
|
2020-01-24 15:04:58 +00:00
|
|
|
if err := s.parseEntMetaNoWildcard(req, &entMeta); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
args.WithEnterpriseMeta(&entMeta)
|
2019-08-02 20:34:54 +00:00
|
|
|
|
|
|
|
if req.Method == "POST" {
|
|
|
|
var raw map[string]interface{}
|
2019-10-29 18:13:36 +00:00
|
|
|
if err := decodeBody(req.Body, &raw); err != nil {
|
2022-04-29 17:42:49 +00:00
|
|
|
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: fmt.Sprintf("Request decoding failed: %v", err)}
|
2019-08-02 20:34:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
apiReq, err := decodeDiscoveryChainReadRequest(raw)
|
|
|
|
if err != nil {
|
2022-04-29 17:42:49 +00:00
|
|
|
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: fmt.Sprintf("Request decoding failed: %v", err)}
|
2019-08-02 20:34:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
args.OverrideProtocol = apiReq.OverrideProtocol
|
|
|
|
args.OverrideConnectTimeout = apiReq.OverrideConnectTimeout
|
|
|
|
|
|
|
|
if apiReq.OverrideMeshGateway.Mode != "" {
|
|
|
|
_, err := structs.ValidateMeshGatewayMode(string(apiReq.OverrideMeshGateway.Mode))
|
|
|
|
if err != nil {
|
2022-04-29 17:42:49 +00:00
|
|
|
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Invalid OverrideMeshGateway.Mode parameter"}
|
2019-08-02 20:34:54 +00:00
|
|
|
}
|
|
|
|
args.OverrideMeshGateway = apiReq.OverrideMeshGateway
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make the RPC request
|
|
|
|
var out structs.DiscoveryChainResponse
|
|
|
|
defer setMeta(resp, &out.QueryMeta)
|
|
|
|
|
2020-10-04 17:54:56 +00:00
|
|
|
if args.QueryOptions.UseCache {
|
2020-06-15 15:01:25 +00:00
|
|
|
raw, m, err := s.agent.cache.Get(req.Context(), cachetype.CompiledDiscoveryChainName, &args)
|
2019-08-02 20:34:54 +00:00
|
|
|
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:
|
2022-12-14 15:24:22 +00:00
|
|
|
if err := s.agent.RPC(req.Context(), "DiscoveryChain.Get", &args, &out); err != nil {
|
2019-08-02 20:34:54 +00:00
|
|
|
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 {
|
2020-05-27 18:28:28 +00:00
|
|
|
OverrideMeshGateway structs.MeshGatewayConfig `alias:"override_mesh_gateway"`
|
|
|
|
OverrideProtocol string `alias:"override_protocol"`
|
|
|
|
OverrideConnectTimeout time.Duration `alias:"override_connect_timeout"`
|
2019-08-02 20:34:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// discoveryChainReadResponse is the API variation of structs.DiscoveryChainResponse
|
|
|
|
type discoveryChainReadResponse struct {
|
|
|
|
Chain *structs.CompiledDiscoveryChain
|
|
|
|
}
|
|
|
|
|
|
|
|
func decodeDiscoveryChainReadRequest(raw map[string]interface{}) (*discoveryChainReadRequest, error) {
|
|
|
|
var apiReq discoveryChainReadRequest
|
2020-05-27 17:02:22 +00:00
|
|
|
// TODO(dnephin): at this time only JSON payloads are read, so it is unlikely
|
|
|
|
// that HookWeakDecodeFromSlice is necessary. It was added while porting
|
|
|
|
// from lib.PatchSliceOfMaps to decode.HookWeakDecodeFromSlice. It may be
|
|
|
|
// safe to remove in the future.
|
2019-08-02 20:34:54 +00:00
|
|
|
decodeConf := &mapstructure.DecoderConfig{
|
2020-05-27 18:42:01 +00:00
|
|
|
DecodeHook: mapstructure.ComposeDecodeHookFunc(
|
2020-05-27 17:02:22 +00:00
|
|
|
decode.HookWeakDecodeFromSlice,
|
2020-05-27 18:42:01 +00:00
|
|
|
decode.HookTranslateKeys,
|
|
|
|
mapstructure.StringToTimeDurationHookFunc(),
|
|
|
|
),
|
2019-08-02 20:34:54 +00:00
|
|
|
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
|
|
|
|
}
|