Support for connect native services in topology view. (#12098)
This commit is contained in:
parent
3984d82e90
commit
895da50986
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:feature
|
||||||
|
ui: Support connect-native services in the Topology view.
|
||||||
|
```
|
|
@ -3461,6 +3461,7 @@ func (s *Store) ServiceTopology(
|
||||||
err error
|
err error
|
||||||
fullyTransparent bool
|
fullyTransparent bool
|
||||||
hasTransparent bool
|
hasTransparent bool
|
||||||
|
connectNative bool
|
||||||
)
|
)
|
||||||
switch kind {
|
switch kind {
|
||||||
case structs.ServiceKindIngressGateway:
|
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.
|
// transparent proxy mode. If ANY instance isn't in the right mode then the warming applies.
|
||||||
fullyTransparent = false
|
fullyTransparent = false
|
||||||
}
|
}
|
||||||
|
if proxy.ServiceConnect.Native {
|
||||||
|
connectNative = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -3526,8 +3530,8 @@ func (s *Store) ServiceTopology(
|
||||||
|
|
||||||
upstreamDecisions := make(map[string]structs.IntentionDecisionSummary)
|
upstreamDecisions := make(map[string]structs.IntentionDecisionSummary)
|
||||||
|
|
||||||
// Only transparent proxies have upstreams from intentions
|
// Only transparent proxies / connect native services have upstreams from intentions
|
||||||
if hasTransparent {
|
if hasTransparent || connectNative {
|
||||||
idx, intentionUpstreams, err := s.intentionTopologyTxn(tx, ws, sn, false, defaultAllow)
|
idx, intentionUpstreams, err := s.intentionTopologyTxn(tx, ws, sn, false, defaultAllow)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
|
@ -3607,8 +3611,8 @@ func (s *Store) ServiceTopology(
|
||||||
sn = structs.NewServiceName(upstream.Service.Proxy.DestinationServiceName, &upstream.Service.EnterpriseMeta)
|
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.
|
// Avoid returning upstreams from intentions when none of the proxy instances of the target are in transparent mode or connect native.
|
||||||
if !hasTransparent && upstreamSources[sn.String()] != structs.TopologySourceRegistration {
|
if !hasTransparent && !connectNative && upstreamSources[sn.String()] != structs.TopologySourceRegistration {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
upstreams = append(upstreams, upstream)
|
upstreams = append(upstreams, upstream)
|
||||||
|
@ -3711,6 +3715,7 @@ func (s *Store) ServiceTopology(
|
||||||
}
|
}
|
||||||
|
|
||||||
idx, unfilteredDownstreams, err := s.combinedServiceNodesTxn(tx, ws, downstreamNames)
|
idx, unfilteredDownstreams, err := s.combinedServiceNodesTxn(tx, ws, downstreamNames)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, fmt.Errorf("failed to get downstreams for %q: %v", sn.String(), err)
|
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 {
|
if downstream.Service.Kind == structs.ServiceKindConnectProxy {
|
||||||
sn = structs.NewServiceName(downstream.Service.Proxy.DestinationServiceName, &downstream.Service.EnterpriseMeta)
|
sn = structs.NewServiceName(downstream.Service.Proxy.DestinationServiceName, &downstream.Service.EnterpriseMeta)
|
||||||
}
|
}
|
||||||
if _, ok := tproxyMap[sn]; !ok && downstreamSources[sn.String()] != structs.TopologySourceRegistration {
|
if _, ok := tproxyMap[sn]; !ok && !downstream.Service.Connect.Native && downstreamSources[sn.String()] != structs.TopologySourceRegistration {
|
||||||
// If downstream is not a transparent proxy, remove references
|
// If downstream is not a transparent proxy or connect native, remove references
|
||||||
delete(downstreamSources, sn.String())
|
delete(downstreamSources, sn.String())
|
||||||
delete(downstreamDecisions, sn.String())
|
delete(downstreamDecisions, sn.String())
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -35,6 +35,7 @@ type ServiceSummary struct {
|
||||||
GatewayConfig GatewayConfig
|
GatewayConfig GatewayConfig
|
||||||
TransparentProxy bool
|
TransparentProxy bool
|
||||||
transparentProxySet bool
|
transparentProxySet bool
|
||||||
|
ConnectNative bool
|
||||||
|
|
||||||
structs.EnterpriseMeta
|
structs.EnterpriseMeta
|
||||||
}
|
}
|
||||||
|
@ -422,6 +423,7 @@ func summarizeServices(dump structs.ServiceDump, cfg *config.RuntimeConfig, dc s
|
||||||
sum.Kind = svc.Kind
|
sum.Kind = svc.Kind
|
||||||
sum.Datacenter = csn.Node.Datacenter
|
sum.Datacenter = csn.Node.Datacenter
|
||||||
sum.InstanceCount += 1
|
sum.InstanceCount += 1
|
||||||
|
sum.ConnectNative = svc.Connect.Native
|
||||||
if svc.Kind == structs.ServiceKindConnectProxy {
|
if svc.Kind == structs.ServiceKindConnectProxy {
|
||||||
sn := structs.NewServiceName(svc.Proxy.DestinationServiceName, &svc.EnterpriseMeta)
|
sn := structs.NewServiceName(svc.Proxy.DestinationServiceName, &svc.EnterpriseMeta)
|
||||||
hasProxy[sn] = true
|
hasProxy[sn] = true
|
||||||
|
|
|
@ -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 {
|
for _, args := range registrations {
|
||||||
var out struct{}
|
var out struct{}
|
||||||
|
@ -1292,6 +1428,8 @@ func TestUIServiceTopology(t *testing.T) {
|
||||||
// wildcard deny intention
|
// wildcard deny intention
|
||||||
// api -> web exact intention
|
// api -> web exact intention
|
||||||
// web -> redis exact intention
|
// web -> redis exact intention
|
||||||
|
// cfrontend -> cproxy exact intention
|
||||||
|
// cproxy -> cbackend exact intention
|
||||||
{
|
{
|
||||||
entries := []structs.ConfigEntryRequest{
|
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 {
|
for _, req := range entries {
|
||||||
out := false
|
out := false
|
||||||
|
@ -1620,6 +1784,60 @@ func TestUIServiceTopology(t *testing.T) {
|
||||||
FilteredByACLs: false,
|
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 {
|
for _, tc := range tcs {
|
||||||
|
|
|
@ -92,7 +92,7 @@
|
||||||
@disabled={{disabled}}
|
@disabled={{disabled}}
|
||||||
@oncreate={{action @oncreate item @service}}
|
@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
|
<TopologyMetrics::Popover
|
||||||
@type='not-defined'
|
@type='not-defined'
|
||||||
@service={{@service}}
|
@service={{@service}}
|
||||||
|
|
|
@ -14,6 +14,7 @@ export default class Topology extends Model {
|
||||||
@attr('string') Protocol;
|
@attr('string') Protocol;
|
||||||
@attr('boolean') FilteredByACLs;
|
@attr('boolean') FilteredByACLs;
|
||||||
@attr('boolean') TransparentProxy;
|
@attr('boolean') TransparentProxy;
|
||||||
|
@attr('boolean') ConnectNative;
|
||||||
@attr() Upstreams; // Service[]
|
@attr() Upstreams; // Service[]
|
||||||
@attr() Downstreams; // Service[],
|
@attr() Downstreams; // Service[],
|
||||||
@attr() meta; // {}
|
@attr() meta; // {}
|
||||||
|
@ -25,7 +26,7 @@ export default class Topology extends Model {
|
||||||
undefinedDownstream =
|
undefinedDownstream =
|
||||||
this.Downstreams.filter(
|
this.Downstreams.filter(
|
||||||
item =>
|
item =>
|
||||||
item.Source === 'specific-intention' && !item.TransparentProxy && item.Intention.Allowed
|
item.Source === 'specific-intention' && !item.TransparentProxy && !item.ConnectNative && item.Intention.Allowed
|
||||||
).length !== 0;
|
).length !== 0;
|
||||||
|
|
||||||
return undefinedDownstream;
|
return undefinedDownstream;
|
||||||
|
|
Loading…
Reference in New Issue