Skip to content

Commit 247756e

Browse files
committed
connect: expose an API endpoint to compile the discovery chain
1 parent f66c962 commit 247756e

10 files changed

+183
-111
lines changed

agent/cache-types/discovery_chain.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func (c *CompiledDiscoveryChain) Fetch(opts cache.FetchOptions, req cache.Reques
3838

3939
// Fetch
4040
var reply structs.DiscoveryChainResponse
41-
if err := c.RPC.RPC("ConfigEntry.ReadDiscoveryChain", reqReal, &reply); err != nil {
41+
if err := c.RPC.RPC("DiscoveryChain.Get", reqReal, &reply); err != nil {
4242
return result, err
4343
}
4444

agent/cache-types/discovery_chain_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,16 @@ func TestCompiledDiscoveryChain(t *testing.T) {
2222
// Expect the proper RPC call. This also sets the expected value
2323
// since that is return-by-pointer in the arguments.
2424
var resp *structs.DiscoveryChainResponse
25-
rpc.On("RPC", "ConfigEntry.ReadDiscoveryChain", mock.Anything, mock.Anything).Return(nil).
25+
rpc.On("RPC", "DiscoveryChain.Get", mock.Anything, mock.Anything).Return(nil).
2626
Run(func(args mock.Arguments) {
2727
req := args.Get(1).(*structs.DiscoveryChainRequest)
2828
require.Equal(t, uint64(24), req.QueryOptions.MinQueryIndex)
2929
require.Equal(t, 1*time.Second, req.QueryOptions.MaxQueryTime)
3030
require.True(t, req.AllowStale)
3131

3232
reply := args.Get(2).(*structs.DiscoveryChainResponse)
33-
reply.ConfigEntries = entries
3433
reply.Chain = chain
34+
reply.Entries = entries.Flatten()
3535
reply.QueryMeta.Index = 48
3636
resp = reply
3737
})

agent/consul/config_endpoint.go

-62
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66

77
metrics "github.com/armon/go-metrics"
88
"github.com/hashicorp/consul/acl"
9-
"github.com/hashicorp/consul/agent/consul/discoverychain"
109
"github.com/hashicorp/consul/agent/consul/state"
1110
"github.com/hashicorp/consul/agent/structs"
1211
memdb "github.com/hashicorp/go-memdb"
@@ -313,64 +312,3 @@ func (c *ConfigEntry) ResolveServiceConfig(args *structs.ServiceConfigRequest, r
313312
return nil
314313
})
315314
}
316-
317-
func (c *ConfigEntry) ReadDiscoveryChain(args *structs.DiscoveryChainRequest, reply *structs.DiscoveryChainResponse) error {
318-
if done, err := c.srv.forward("ConfigEntry.ReadDiscoveryChain", args, args, reply); done {
319-
return err
320-
}
321-
defer metrics.MeasureSince([]string{"config_entry", "read_discovery_chain"}, time.Now())
322-
323-
// Fetch the ACL token, if any.
324-
rule, err := c.srv.ResolveToken(args.Token)
325-
if err != nil {
326-
return err
327-
}
328-
if rule != nil && !rule.ServiceRead(args.Name) {
329-
return acl.ErrPermissionDenied
330-
}
331-
332-
if args.Name == "" {
333-
return fmt.Errorf("Must provide service name")
334-
}
335-
336-
evalDC := args.EvaluateInDatacenter
337-
if evalDC == "" {
338-
evalDC = c.srv.config.Datacenter
339-
}
340-
341-
evalNS := args.EvaluateInNamespace
342-
if evalNS == "" {
343-
// TODO(namespaces) pull from something else?
344-
evalNS = "default"
345-
}
346-
347-
return c.srv.blockingQuery(
348-
&args.QueryOptions,
349-
&reply.QueryMeta,
350-
func(ws memdb.WatchSet, state *state.Store) error {
351-
index, entries, err := state.ReadDiscoveryChainConfigEntries(ws, args.Name)
352-
if err != nil {
353-
return err
354-
}
355-
356-
// Then we compile it into something useful.
357-
chain, err := discoverychain.Compile(discoverychain.CompileRequest{
358-
ServiceName: args.Name,
359-
CurrentNamespace: evalNS,
360-
CurrentDatacenter: evalDC,
361-
OverrideMeshGateway: args.OverrideMeshGateway,
362-
OverrideProtocol: args.OverrideProtocol,
363-
OverrideConnectTimeout: args.OverrideConnectTimeout,
364-
Entries: entries,
365-
})
366-
if err != nil {
367-
return err
368-
}
369-
370-
reply.Index = index
371-
reply.ConfigEntries = entries
372-
reply.Chain = chain
373-
374-
return nil
375-
})
376-
}
+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package consul
2+
3+
import (
4+
"fmt"
5+
"time"
6+
7+
metrics "github.com/armon/go-metrics"
8+
"github.com/hashicorp/consul/acl"
9+
"github.com/hashicorp/consul/agent/consul/discoverychain"
10+
"github.com/hashicorp/consul/agent/consul/state"
11+
"github.com/hashicorp/consul/agent/structs"
12+
memdb "github.com/hashicorp/go-memdb"
13+
)
14+
15+
type DiscoveryChain struct {
16+
srv *Server
17+
}
18+
19+
func (c *DiscoveryChain) Get(args *structs.DiscoveryChainRequest, reply *structs.DiscoveryChainResponse) error {
20+
if done, err := c.srv.forward("DiscoveryChain.Get", args, args, reply); done {
21+
return err
22+
}
23+
defer metrics.MeasureSince([]string{"discoverychain", "get"}, time.Now())
24+
25+
// Fetch the ACL token, if any.
26+
rule, err := c.srv.ResolveToken(args.Token)
27+
if err != nil {
28+
return err
29+
}
30+
if rule != nil && !rule.ServiceRead(args.Name) {
31+
return acl.ErrPermissionDenied
32+
}
33+
34+
if args.Name == "" {
35+
return fmt.Errorf("Must provide service name")
36+
}
37+
38+
evalDC := args.EvaluateInDatacenter
39+
if evalDC == "" {
40+
evalDC = c.srv.config.Datacenter
41+
}
42+
43+
evalNS := args.EvaluateInNamespace
44+
if evalNS == "" {
45+
// TODO(namespaces) pull from something else?
46+
evalNS = "default"
47+
}
48+
49+
return c.srv.blockingQuery(
50+
&args.QueryOptions,
51+
&reply.QueryMeta,
52+
func(ws memdb.WatchSet, state *state.Store) error {
53+
index, entries, err := state.ReadDiscoveryChainConfigEntries(ws, args.Name)
54+
if err != nil {
55+
return err
56+
}
57+
58+
// Then we compile it into something useful.
59+
chain, err := discoverychain.Compile(discoverychain.CompileRequest{
60+
ServiceName: args.Name,
61+
CurrentNamespace: evalNS,
62+
CurrentDatacenter: evalDC,
63+
OverrideMeshGateway: args.OverrideMeshGateway,
64+
OverrideProtocol: args.OverrideProtocol,
65+
OverrideConnectTimeout: args.OverrideConnectTimeout,
66+
Entries: entries,
67+
})
68+
if err != nil {
69+
return err
70+
}
71+
72+
reply.Index = index
73+
reply.Entries = entries.Flatten()
74+
reply.Chain = chain
75+
76+
return nil
77+
})
78+
}

agent/consul/server_oss.go

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ func init() {
66
registerEndpoint(func(s *Server) interface{} { return NewCoordinate(s) })
77
registerEndpoint(func(s *Server) interface{} { return &ConfigEntry{s} })
88
registerEndpoint(func(s *Server) interface{} { return &ConnectCA{srv: s} })
9+
registerEndpoint(func(s *Server) interface{} { return &DiscoveryChain{s} })
910
registerEndpoint(func(s *Server) interface{} { return &Health{s} })
1011
registerEndpoint(func(s *Server) interface{} { return &Intention{s} })
1112
registerEndpoint(func(s *Server) interface{} { return &Internal{s} })

agent/discovery_chain_endpoint.go

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package agent
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"strings"
7+
"time"
8+
9+
"github.com/hashicorp/consul/agent/structs"
10+
)
11+
12+
func (s *HTTPServer) ConnectDiscoveryChainGet(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
13+
var args structs.DiscoveryChainRequest
14+
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
15+
return nil, nil
16+
}
17+
18+
args.Name = strings.TrimPrefix(req.URL.Path, "/v1/connect/discovery-chain/")
19+
if args.Name == "" {
20+
resp.WriteHeader(http.StatusBadRequest)
21+
fmt.Fprint(resp, "Missing chain name")
22+
return nil, nil
23+
}
24+
25+
args.EvaluateInDatacenter = req.URL.Query().Get("eval_dc")
26+
// TODO(namespaces): args.EvaluateInNamespace = req.URL.Query().Get("eval_namespace")
27+
28+
overrideMeshGatewayMode := req.URL.Query().Get("override_mesh_gateway_mode")
29+
if overrideMeshGatewayMode != "" {
30+
mode, err := structs.ValidateMeshGatewayMode(overrideMeshGatewayMode)
31+
if err != nil {
32+
resp.WriteHeader(http.StatusBadRequest)
33+
fmt.Fprint(resp, "Invalid override_mesh_gateway_mode parameter")
34+
return nil, nil
35+
}
36+
args.OverrideMeshGateway.Mode = mode
37+
}
38+
39+
args.OverrideProtocol = req.URL.Query().Get("override_protocol")
40+
overrideTimeoutString := req.URL.Query().Get("override_connect_timeout")
41+
if overrideTimeoutString != "" {
42+
d, err := time.ParseDuration(overrideTimeoutString)
43+
if err != nil {
44+
resp.WriteHeader(http.StatusBadRequest)
45+
fmt.Fprint(resp, "Invalid override_connect_timeout parameter")
46+
return nil, nil
47+
}
48+
args.OverrideConnectTimeout = d
49+
}
50+
51+
// Make the RPC request
52+
var out structs.DiscoveryChainResponse
53+
defer setMeta(resp, &out.QueryMeta)
54+
55+
if err := s.agent.RPC("DiscoveryChain.Get", &args, &out); err != nil {
56+
return nil, err
57+
}
58+
59+
apiOut := apiDiscoveryChainResponse{
60+
Chain: out.Chain,
61+
Entries: out.Entries,
62+
}
63+
64+
return apiOut, nil
65+
}
66+
67+
type apiDiscoveryChainResponse struct {
68+
Chain *structs.CompiledDiscoveryChain
69+
Entries []structs.ConfigEntry
70+
}

agent/http_oss.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ func init() {
7777
registerEndpoint("/v1/connect/intentions/match", []string{"GET"}, (*HTTPServer).IntentionMatch)
7878
registerEndpoint("/v1/connect/intentions/check", []string{"GET"}, (*HTTPServer).IntentionCheck)
7979
registerEndpoint("/v1/connect/intentions/", []string{"GET", "PUT", "DELETE"}, (*HTTPServer).IntentionSpecific)
80+
registerEndpoint("/v1/connect/discovery-chain/", []string{"GET"}, (*HTTPServer).ConnectDiscoveryChainGet)
8081
registerEndpoint("/v1/coordinate/datacenters", []string{"GET"}, (*HTTPServer).CoordinateDatacenters)
8182
registerEndpoint("/v1/coordinate/nodes", []string{"GET"}, (*HTTPServer).CoordinateNodes)
8283
registerEndpoint("/v1/coordinate/node/", []string{"GET"}, (*HTTPServer).CoordinateNode)
@@ -88,7 +89,6 @@ func init() {
8889
registerEndpoint("/v1/health/state/", []string{"GET"}, (*HTTPServer).HealthChecksInState)
8990
registerEndpoint("/v1/health/service/", []string{"GET"}, (*HTTPServer).HealthServiceNodes)
9091
registerEndpoint("/v1/health/connect/", []string{"GET"}, (*HTTPServer).HealthConnectServiceNodes)
91-
registerEndpoint("/v1/internal/discovery-chain/", []string{"GET"}, (*HTTPServer).InternalDiscoveryChain)
9292
registerEndpoint("/v1/internal/ui/nodes", []string{"GET"}, (*HTTPServer).UINodes)
9393
registerEndpoint("/v1/internal/ui/node/", []string{"GET"}, (*HTTPServer).UINodeInfo)
9494
registerEndpoint("/v1/internal/ui/services", []string{"GET"}, (*HTTPServer).UIServices)

agent/internal_endpoint.go

-40
This file was deleted.

agent/structs/config_entry_discoverychain.go

+28-3
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,32 @@ func NewDiscoveryChainConfigEntries() *DiscoveryChainConfigEntries {
883883
}
884884
}
885885

886+
func (e *DiscoveryChainConfigEntries) Flatten() []ConfigEntry {
887+
n := len(e.Routers) + len(e.Splitters) + len(e.Resolvers) + len(e.Services)
888+
if e.GlobalProxy != nil {
889+
n++
890+
}
891+
out := make([]ConfigEntry, 0, n)
892+
893+
for _, v := range e.Routers {
894+
out = append(out, v)
895+
}
896+
for _, v := range e.Splitters {
897+
out = append(out, v)
898+
}
899+
for _, v := range e.Resolvers {
900+
out = append(out, v)
901+
}
902+
for _, v := range e.Services {
903+
out = append(out, v)
904+
}
905+
if e.GlobalProxy != nil {
906+
out = append(out, e.GlobalProxy)
907+
}
908+
909+
return out
910+
}
911+
886912
func (e *DiscoveryChainConfigEntries) GetRouter(name string) *ServiceRouterConfigEntry {
887913
if e.Routers != nil {
888914
return e.Routers[name]
@@ -1051,10 +1077,9 @@ func (r *DiscoveryChainRequest) CacheInfo() cache.RequestInfo {
10511077
return info
10521078
}
10531079

1054-
// TODO(rb): either fix the compiled results, or take the derived data and stash it here in a json/msgpack-friendly way?
10551080
type DiscoveryChainResponse struct {
1056-
ConfigEntries *DiscoveryChainConfigEntries `json:",omitempty"` // TODO(rb): remove these?
1057-
Chain *CompiledDiscoveryChain `json:",omitempty"`
1081+
Chain *CompiledDiscoveryChain
1082+
Entries []ConfigEntry
10581083
QueryMeta
10591084
}
10601085

agent/structs/discovery_chain.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ type CompiledDiscoveryChain struct {
2424
// If set, this value should be used to prefix/suffix any generated load
2525
// balancer data plane objects to avoid sharing customized and
2626
// non-customized versions.
27-
CustomizationHash string
27+
CustomizationHash string `json:",omitempty"`
2828

2929
// Protocol is the overall protocol shared by everything in the chain.
30-
Protocol string
30+
Protocol string `json:",omitempty"`
3131

3232
// StartNode is the first key into the Nodes map that should be followed
3333
// when walking the discovery chain.

0 commit comments

Comments
 (0)