Support for connect native services in topology view. (#12098)

This commit is contained in:
Florian Apolloner 2022-02-16 22:51:54 +01:00 committed by GitHub
parent c17aa3720f
commit f01f00fc84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 237 additions and 8 deletions

3
.changelog/12098.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:feature
ui: Support connect-native services in the Topology view.
```

View File

@ -3461,6 +3461,7 @@ func (s *Store) ServiceTopology(
err error
fullyTransparent bool
hasTransparent bool
connectNative bool
)
switch kind {
case structs.ServiceKindIngressGateway:
@ -3505,6 +3506,9 @@ func (s *Store) ServiceTopology(
// transparent proxy mode. If ANY instance isn't in the right mode then the warming applies.
fullyTransparent = false
}
if proxy.ServiceConnect.Native {
connectNative = true
}
}
default:
@ -3526,8 +3530,8 @@ func (s *Store) ServiceTopology(
upstreamDecisions := make(map[string]structs.IntentionDecisionSummary)
// Only transparent proxies have upstreams from intentions
if hasTransparent {
// Only transparent proxies / connect native services have upstreams from intentions
if hasTransparent || connectNative {
idx, intentionUpstreams, err := s.intentionTopologyTxn(tx, ws, sn, false, defaultAllow)
if err != nil {
return 0, nil, err
@ -3607,8 +3611,8 @@ func (s *Store) ServiceTopology(
sn = structs.NewServiceName(upstream.Service.Proxy.DestinationServiceName, &upstream.Service.EnterpriseMeta)
}
// Avoid returning upstreams from intentions when none of the proxy instances of the target are in transparent mode.
if !hasTransparent && upstreamSources[sn.String()] != structs.TopologySourceRegistration {
// Avoid returning upstreams from intentions when none of the proxy instances of the target are in transparent mode or connect native.
if !hasTransparent && !connectNative && upstreamSources[sn.String()] != structs.TopologySourceRegistration {
continue
}
upstreams = append(upstreams, upstream)
@ -3711,6 +3715,7 @@ func (s *Store) ServiceTopology(
}
idx, unfilteredDownstreams, err := s.combinedServiceNodesTxn(tx, ws, downstreamNames)
if err != nil {
return 0, nil, fmt.Errorf("failed to get downstreams for %q: %v", sn.String(), err)
}
@ -3734,8 +3739,8 @@ func (s *Store) ServiceTopology(
if downstream.Service.Kind == structs.ServiceKindConnectProxy {
sn = structs.NewServiceName(downstream.Service.Proxy.DestinationServiceName, &downstream.Service.EnterpriseMeta)
}
if _, ok := tproxyMap[sn]; !ok && downstreamSources[sn.String()] != structs.TopologySourceRegistration {
// If downstream is not a transparent proxy, remove references
if _, ok := tproxyMap[sn]; !ok && !downstream.Service.Connect.Native && downstreamSources[sn.String()] != structs.TopologySourceRegistration {
// If downstream is not a transparent proxy or connect native, remove references
delete(downstreamSources, sn.String())
delete(downstreamDecisions, sn.String())
continue

View File

@ -35,6 +35,7 @@ type ServiceSummary struct {
GatewayConfig GatewayConfig
TransparentProxy bool
transparentProxySet bool
ConnectNative bool
structs.EnterpriseMeta
}
@ -422,6 +423,7 @@ func summarizeServices(dump structs.ServiceDump, cfg *config.RuntimeConfig, dc s
sum.Kind = svc.Kind
sum.Datacenter = csn.Node.Datacenter
sum.InstanceCount += 1
sum.ConnectNative = svc.Connect.Native
if svc.Kind == structs.ServiceKindConnectProxy {
sn := structs.NewServiceName(svc.Proxy.DestinationServiceName, &svc.EnterpriseMeta)
hasProxy[sn] = true

View File

@ -1281,6 +1281,142 @@ func TestUIServiceTopology(t *testing.T) {
},
},
},
"Node cnative": {
Datacenter: "dc1",
Node: "cnative",
Address: "127.0.0.6",
Checks: structs.HealthChecks{
&structs.HealthCheck{
Node: "cnative",
CheckID: "cnative:alive",
Name: "cnative-liveness",
Status: api.HealthPassing,
},
},
},
"Service cbackend on cnative": {
Datacenter: "dc1",
Node: "cnative",
SkipNodeUpdate: true,
Service: &structs.NodeService{
Kind: structs.ServiceKindTypical,
ID: "cbackend",
Service: "cbackend",
Port: 8080,
Address: "198.18.1.70",
},
Checks: structs.HealthChecks{
&structs.HealthCheck{
Node: "cnative",
CheckID: "cnative:cbackend",
Name: "cbackend-liveness",
Status: api.HealthPassing,
ServiceID: "cbackend",
ServiceName: "cbackend",
},
},
},
"Service cbackend-proxy on cnative": {
Datacenter: "dc1",
Node: "cnative",
SkipNodeUpdate: true,
Service: &structs.NodeService{
Kind: structs.ServiceKindConnectProxy,
ID: "cbackend-proxy",
Service: "cbackend-proxy",
Port: 8443,
Address: "198.18.1.70",
Proxy: structs.ConnectProxyConfig{
DestinationServiceName: "cbackend",
},
},
Checks: structs.HealthChecks{
&structs.HealthCheck{
Node: "cnative",
CheckID: "cnative:cbackend-proxy",
Name: "cbackend proxy listening",
Status: api.HealthCritical,
ServiceID: "cbackend-proxy",
ServiceName: "cbackend-proxy",
},
},
},
"Service cfrontend on cnative": {
Datacenter: "dc1",
Node: "cnative",
SkipNodeUpdate: true,
Service: &structs.NodeService{
Kind: structs.ServiceKindTypical,
ID: "cfrontend",
Service: "cfrontend",
Port: 9080,
Address: "198.18.1.70",
},
Checks: structs.HealthChecks{
&structs.HealthCheck{
Node: "cnative",
CheckID: "cnative:cfrontend",
Name: "cfrontend-liveness",
Status: api.HealthPassing,
ServiceID: "cfrontend",
ServiceName: "cfrontend",
},
},
},
"Service cfrontend-proxy on cnative": {
Datacenter: "dc1",
Node: "cnative",
SkipNodeUpdate: true,
Service: &structs.NodeService{
Kind: structs.ServiceKindConnectProxy,
ID: "cfrontend-proxy",
Service: "cfrontend-proxy",
Port: 9443,
Address: "198.18.1.70",
Proxy: structs.ConnectProxyConfig{
DestinationServiceName: "cfrontend",
Upstreams: structs.Upstreams{
{
DestinationName: "cproxy",
LocalBindPort: 123,
},
},
},
},
Checks: structs.HealthChecks{
&structs.HealthCheck{
Node: "cnative",
CheckID: "cnative:cfrontend-proxy",
Name: "cfrontend proxy listening",
Status: api.HealthCritical,
ServiceID: "cfrontend-proxy",
ServiceName: "cfrontend-proxy",
},
},
},
"Service cproxy on cnative": {
Datacenter: "dc1",
Node: "cnative",
SkipNodeUpdate: true,
Service: &structs.NodeService{
Kind: structs.ServiceKindTypical,
ID: "cproxy",
Service: "cproxy",
Port: 1111,
Address: "198.18.1.70",
Connect: structs.ServiceConnect{Native: true},
},
Checks: structs.HealthChecks{
&structs.HealthCheck{
Node: "cnative",
CheckID: "cnative:cproxy",
Name: "cproxy-liveness",
Status: api.HealthPassing,
ServiceID: "cproxy",
ServiceName: "cproxy",
},
},
},
}
for _, args := range registrations {
var out struct{}
@ -1292,6 +1428,8 @@ func TestUIServiceTopology(t *testing.T) {
// wildcard deny intention
// api -> web exact intention
// web -> redis exact intention
// cfrontend -> cproxy exact intention
// cproxy -> cbackend exact intention
{
entries := []structs.ConfigEntryRequest{
{
@ -1391,6 +1529,32 @@ func TestUIServiceTopology(t *testing.T) {
},
},
},
{
Datacenter: "dc1",
Entry: &structs.ServiceIntentionsConfigEntry{
Kind: structs.ServiceIntentions,
Name: "cproxy",
Sources: []*structs.SourceIntention{
{
Action: structs.IntentionActionAllow,
Name: "cfrontend",
},
},
},
},
{
Datacenter: "dc1",
Entry: &structs.ServiceIntentionsConfigEntry{
Kind: structs.ServiceIntentions,
Name: "cbackend",
Sources: []*structs.SourceIntention{
{
Action: structs.IntentionActionAllow,
Name: "cproxy",
},
},
},
},
}
for _, req := range entries {
out := false
@ -1620,6 +1784,60 @@ func TestUIServiceTopology(t *testing.T) {
FilteredByACLs: false,
},
},
{
name: "cproxy",
httpReq: func() *http.Request {
req, _ := http.NewRequest("GET", "/v1/internal/ui/service-topology/cproxy?kind=", nil)
return req
}(),
want: &ServiceTopology{
Protocol: "http",
TransparentProxy: false,
Upstreams: []*ServiceTopologySummary{
{
ServiceSummary: ServiceSummary{
Name: "cbackend",
Datacenter: "dc1",
Nodes: []string{"cnative"},
InstanceCount: 1,
ChecksPassing: 2,
ChecksWarning: 0,
ChecksCritical: 1,
EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
},
Intention: structs.IntentionDecisionSummary{
DefaultAllow: true,
Allowed: true,
HasPermissions: false,
HasExact: true,
},
Source: structs.TopologySourceSpecificIntention,
},
},
Downstreams: []*ServiceTopologySummary{
{
ServiceSummary: ServiceSummary{
Name: "cfrontend",
Datacenter: "dc1",
Nodes: []string{"cnative"},
InstanceCount: 1,
ChecksPassing: 2,
ChecksWarning: 0,
ChecksCritical: 1,
EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
},
Intention: structs.IntentionDecisionSummary{
DefaultAllow: true,
Allowed: true,
HasPermissions: false,
HasExact: true,
},
Source: structs.TopologySourceRegistration,
},
},
FilteredByACLs: false,
},
},
}
for _, tc := range tcs {

View File

@ -92,7 +92,7 @@
@disabled={{disabled}}
@oncreate={{action @oncreate item @service}}
/>
{{else if (and (not-eq item.Datacenter '') item.Intention.Allowed (not item.TransparentProxy) (eq item.Source 'specific-intention'))}}
{{else if (and (not-eq item.Datacenter '') item.Intention.Allowed (not item.TransparentProxy) (not item.ConnectNative) (eq item.Source 'specific-intention'))}}
<TopologyMetrics::Popover
@type='not-defined'
@service={{@service}}

View File

@ -14,6 +14,7 @@ export default class Topology extends Model {
@attr('string') Protocol;
@attr('boolean') FilteredByACLs;
@attr('boolean') TransparentProxy;
@attr('boolean') ConnectNative;
@attr() Upstreams; // Service[]
@attr() Downstreams; // Service[],
@attr() meta; // {}
@ -25,7 +26,7 @@ export default class Topology extends Model {
undefinedDownstream =
this.Downstreams.filter(
item =>
item.Source === 'specific-intention' && !item.TransparentProxy && item.Intention.Allowed
item.Source === 'specific-intention' && !item.TransparentProxy && !item.ConnectNative && item.Intention.Allowed
).length !== 0;
return undefinedDownstream;