[1.16.x] Fix topology view when displaying mixed connect-native/normal services. (#18330)
Fix topology view when displaying mixed connect-native/normal services. (#13023) * Fix topoloy intention with mixed connect-native/normal services. If a service is registered twice, once with connect-native and once without, the topology views would prune the existing intentions. This change brings the code more in line with the transparent proxy behavior. * Dedupe nodes in the ServiceTopology ui endpoint (like done with tags). * Consider a service connect-native as soon as one instance is. Co-authored-by: Florian Apolloner <florian@apolloner.eu>
This commit is contained in:
parent
5429e56d1d
commit
8a3a96d075
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
ui: the topology view now properly displays services with mixed connect and non-connect instances.
|
||||||
|
```
|
|
@ -4535,13 +4535,17 @@ func (s *Store) ServiceTopology(
|
||||||
maxIdx = idx
|
maxIdx = idx
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store downstreams with at least one instance in transparent proxy mode.
|
// Store downstreams with at least one instance in transparent proxy or connect native mode.
|
||||||
// This is to avoid returning downstreams from intentions when none of the downstreams are transparent proxies.
|
// This is to avoid returning downstreams from intentions when none of the downstreams are transparent proxies.
|
||||||
tproxyMap := make(map[structs.ServiceName]struct{})
|
proxyMap := make(map[structs.ServiceName]struct{})
|
||||||
for _, downstream := range unfilteredDownstreams {
|
for _, downstream := range unfilteredDownstreams {
|
||||||
if downstream.Service.Proxy.Mode == structs.ProxyModeTransparent {
|
if downstream.Service.Proxy.Mode == structs.ProxyModeTransparent {
|
||||||
sn := structs.NewServiceName(downstream.Service.Proxy.DestinationServiceName, &downstream.Service.EnterpriseMeta)
|
sn := structs.NewServiceName(downstream.Service.Proxy.DestinationServiceName, &downstream.Service.EnterpriseMeta)
|
||||||
tproxyMap[sn] = struct{}{}
|
proxyMap[sn] = struct{}{}
|
||||||
|
}
|
||||||
|
if downstream.Service.Connect.Native {
|
||||||
|
sn := downstream.Service.CompoundServiceName()
|
||||||
|
proxyMap[sn] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4551,7 +4555,7 @@ 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 && !downstream.Service.Connect.Native && downstreamSources[sn.String()] != structs.TopologySourceRegistration {
|
if _, ok := proxyMap[sn]; !ok && downstreamSources[sn.String()] != structs.TopologySourceRegistration {
|
||||||
// If downstream is not a transparent proxy or connect native, 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())
|
||||||
|
@ -4580,6 +4584,7 @@ func (s *Store) combinedServiceNodesTxn(tx ReadTxn, ws memdb.WatchSet, names []s
|
||||||
maxIdx uint64
|
maxIdx uint64
|
||||||
resp structs.CheckServiceNodes
|
resp structs.CheckServiceNodes
|
||||||
)
|
)
|
||||||
|
dedupMap := make(map[string]structs.CheckServiceNode)
|
||||||
for _, u := range names {
|
for _, u := range names {
|
||||||
// Collect typical then connect instances
|
// Collect typical then connect instances
|
||||||
idx, csn, err := checkServiceNodesTxn(tx, ws, u.Name, false, &u.EnterpriseMeta, peerName)
|
idx, csn, err := checkServiceNodesTxn(tx, ws, u.Name, false, &u.EnterpriseMeta, peerName)
|
||||||
|
@ -4589,7 +4594,9 @@ func (s *Store) combinedServiceNodesTxn(tx ReadTxn, ws memdb.WatchSet, names []s
|
||||||
if idx > maxIdx {
|
if idx > maxIdx {
|
||||||
maxIdx = idx
|
maxIdx = idx
|
||||||
}
|
}
|
||||||
resp = append(resp, csn...)
|
for _, item := range csn {
|
||||||
|
dedupMap[item.Node.Node+"/"+item.Service.ID] = item
|
||||||
|
}
|
||||||
|
|
||||||
idx, csn, err = checkServiceNodesTxn(tx, ws, u.Name, true, &u.EnterpriseMeta, peerName)
|
idx, csn, err = checkServiceNodesTxn(tx, ws, u.Name, true, &u.EnterpriseMeta, peerName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -4598,7 +4605,12 @@ func (s *Store) combinedServiceNodesTxn(tx ReadTxn, ws memdb.WatchSet, names []s
|
||||||
if idx > maxIdx {
|
if idx > maxIdx {
|
||||||
maxIdx = idx
|
maxIdx = idx
|
||||||
}
|
}
|
||||||
resp = append(resp, csn...)
|
for _, item := range csn {
|
||||||
|
dedupMap[item.Node.Node+"/"+item.Service.ID] = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, item := range dedupMap {
|
||||||
|
resp = append(resp, item)
|
||||||
}
|
}
|
||||||
return maxIdx, resp, nil
|
return maxIdx, resp, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,11 +564,25 @@ func summarizeServices(dump structs.ServiceDump, cfg *config.RuntimeConfig, dc s
|
||||||
sum := getService(psn)
|
sum := getService(psn)
|
||||||
|
|
||||||
svc := csn.Service
|
svc := csn.Service
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, existing := range sum.Nodes {
|
||||||
|
if existing == csn.Node.Node {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
sum.Nodes = append(sum.Nodes, csn.Node.Node)
|
sum.Nodes = append(sum.Nodes, csn.Node.Node)
|
||||||
|
}
|
||||||
|
|
||||||
sum.Kind = svc.Kind
|
sum.Kind = svc.Kind
|
||||||
sum.Datacenter = csn.Node.Datacenter
|
sum.Datacenter = csn.Node.Datacenter
|
||||||
sum.InstanceCount += 1
|
sum.InstanceCount += 1
|
||||||
|
// Consider a service connect native once at least one instance is
|
||||||
|
if svc.Connect.Native {
|
||||||
sum.ConnectNative = svc.Connect.Native
|
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)
|
||||||
psn := structs.PeeredServiceName{Peer: peerName, ServiceName: sn}
|
psn := structs.PeeredServiceName{Peer: peerName, ServiceName: sn}
|
||||||
|
|
|
@ -1687,19 +1687,43 @@ func TestUIServiceTopology(t *testing.T) {
|
||||||
SkipNodeUpdate: true,
|
SkipNodeUpdate: true,
|
||||||
Service: &structs.NodeService{
|
Service: &structs.NodeService{
|
||||||
Kind: structs.ServiceKindTypical,
|
Kind: structs.ServiceKindTypical,
|
||||||
ID: "cproxy",
|
ID: "cproxy-https",
|
||||||
Service: "cproxy",
|
Service: "cproxy",
|
||||||
Port: 1111,
|
Port: 1111,
|
||||||
Address: "198.18.1.70",
|
Address: "198.18.1.70",
|
||||||
|
Tags: []string{"https"},
|
||||||
Connect: structs.ServiceConnect{Native: true},
|
Connect: structs.ServiceConnect{Native: true},
|
||||||
},
|
},
|
||||||
Checks: structs.HealthChecks{
|
Checks: structs.HealthChecks{
|
||||||
&structs.HealthCheck{
|
&structs.HealthCheck{
|
||||||
Node: "cnative",
|
Node: "cnative",
|
||||||
CheckID: "cnative:cproxy",
|
CheckID: "cnative:cproxy-https",
|
||||||
Name: "cproxy-liveness",
|
Name: "cproxy-liveness",
|
||||||
Status: api.HealthPassing,
|
Status: api.HealthPassing,
|
||||||
ServiceID: "cproxy",
|
ServiceID: "cproxy-https",
|
||||||
|
ServiceName: "cproxy",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Service cproxy/http on cnative": {
|
||||||
|
Datacenter: "dc1",
|
||||||
|
Node: "cnative",
|
||||||
|
SkipNodeUpdate: true,
|
||||||
|
Service: &structs.NodeService{
|
||||||
|
Kind: structs.ServiceKindTypical,
|
||||||
|
ID: "cproxy-http",
|
||||||
|
Service: "cproxy",
|
||||||
|
Port: 1112,
|
||||||
|
Address: "198.18.1.70",
|
||||||
|
Tags: []string{"http"},
|
||||||
|
},
|
||||||
|
Checks: structs.HealthChecks{
|
||||||
|
&structs.HealthCheck{
|
||||||
|
Node: "cnative",
|
||||||
|
CheckID: "cnative:cproxy-http",
|
||||||
|
Name: "cproxy-liveness",
|
||||||
|
Status: api.HealthPassing,
|
||||||
|
ServiceID: "cproxy-http",
|
||||||
ServiceName: "cproxy",
|
ServiceName: "cproxy",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -2125,6 +2149,42 @@ func TestUIServiceTopology(t *testing.T) {
|
||||||
FilteredByACLs: false,
|
FilteredByACLs: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "cbackend",
|
||||||
|
httpReq: func() *http.Request {
|
||||||
|
req, _ := http.NewRequest("GET", "/v1/internal/ui/service-topology/cbackend?kind=", nil)
|
||||||
|
return req
|
||||||
|
}(),
|
||||||
|
want: &ServiceTopology{
|
||||||
|
Protocol: "http",
|
||||||
|
TransparentProxy: false,
|
||||||
|
Upstreams: []*ServiceTopologySummary{},
|
||||||
|
Downstreams: []*ServiceTopologySummary{
|
||||||
|
{
|
||||||
|
ServiceSummary: ServiceSummary{
|
||||||
|
Name: "cproxy",
|
||||||
|
Datacenter: "dc1",
|
||||||
|
Tags: []string{"http", "https"},
|
||||||
|
Nodes: []string{"cnative"},
|
||||||
|
InstanceCount: 2,
|
||||||
|
ChecksPassing: 3,
|
||||||
|
ChecksWarning: 0,
|
||||||
|
ChecksCritical: 0,
|
||||||
|
ConnectNative: true,
|
||||||
|
EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
|
||||||
|
},
|
||||||
|
Intention: structs.IntentionDecisionSummary{
|
||||||
|
DefaultAllow: true,
|
||||||
|
Allowed: true,
|
||||||
|
HasPermissions: false,
|
||||||
|
HasExact: true,
|
||||||
|
},
|
||||||
|
Source: structs.TopologySourceSpecificIntention,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
FilteredByACLs: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tcs {
|
for _, tc := range tcs {
|
||||||
|
|
Loading…
Reference in New Issue