ui: Discovery-Chain: Cope with some edge case configs (#7174)
* ui: Discovery-Chain: Cope with redirects that have failovers We found a few stranger configurations for discovery-chain, one of which was redirects that can then failover. We altered the parsing here to include 2 passes, one to organize the nodes into resolvers and children/subsets based on the nodes themselves, which includes adding the failovers to resolvers and subsets. We then do a second pass which can more reliably figure out whether a target is a redirect or a failover (target failovers don't have a corresponding node), this then adds the redirect children to the already exising resolver (from the first pass) and then checks if the redirect also has failovers and adds those if so. * ui: Check to see if we have a user configured default route or not ...if we don't add one so the visualization looks complete
This commit is contained in:
parent
c5f184f61d
commit
85ea64211f
|
@ -56,7 +56,33 @@ export default Component.extend({
|
|||
return getSplitters(get(this, 'chain.Nodes'));
|
||||
}),
|
||||
routes: computed('chain.Nodes', function() {
|
||||
return getRoutes(get(this, 'chain.Nodes'), this.dom.guid);
|
||||
const routes = getRoutes(get(this, 'chain.Nodes'), this.dom.guid);
|
||||
// if we have no routes with a PathPrefix of '/' or one with no definition at all
|
||||
// then add our own 'default catch all'
|
||||
if (
|
||||
!routes.find(item => get(item, 'Definition.Match.HTTP.PathPrefix') === '/') &&
|
||||
!routes.find(item => typeof item.Definition === 'undefined')
|
||||
) {
|
||||
let nextNode = `resolver:${this.chain.ServiceName}.${this.chain.Namespace}.${this.chain.Datacenter}`;
|
||||
const splitterID = `splitter:${this.chain.ServiceName}`;
|
||||
if (typeof this.chain.Nodes[splitterID] !== 'undefined') {
|
||||
nextNode = splitterID;
|
||||
}
|
||||
routes.push({
|
||||
Default: true,
|
||||
ID: `route:${this.chain.ServiceName}`,
|
||||
Name: this.chain.ServiceName,
|
||||
Definition: {
|
||||
Match: {
|
||||
HTTP: {
|
||||
PathPrefix: '/',
|
||||
},
|
||||
},
|
||||
},
|
||||
NextNode: nextNode,
|
||||
});
|
||||
}
|
||||
return routes;
|
||||
}),
|
||||
resolvers: computed('chain.{Nodes,Targets}', function() {
|
||||
return getResolvers(
|
||||
|
|
|
@ -23,7 +23,29 @@
|
|||
{{#each item.Children as |child|}}
|
||||
<li onclick={{onclick}} id={{concat 'resolver:' child.ID}}>
|
||||
<a name="">
|
||||
{{#if child.Failover}}
|
||||
{{#if child.Redirect}}
|
||||
<dl class="redirect">
|
||||
<dt data-tooltip="Redirect">Redirect</dt>
|
||||
<dd>
|
||||
{{child.Name}}
|
||||
</dd>
|
||||
</dl>
|
||||
{{#if child.Failover}}
|
||||
<dl class="failover">
|
||||
<dt data-tooltip={{concat child.Failover.Type ' failover'}}>{{concat child.Failover.Type ' failover'}}</dt>
|
||||
<dd>
|
||||
<ol>
|
||||
{{#each child.Failover.Targets as |target|}}
|
||||
<li>
|
||||
<span>{{target}}</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
</dd>
|
||||
</dl>
|
||||
{{/if}}
|
||||
{{else if child.Failover}}
|
||||
{{child.Name}}
|
||||
<dl class="failover">
|
||||
<dt data-tooltip={{concat child.Failover.Type ' failover'}}>{{concat child.Failover.Type ' failover'}}</dt>
|
||||
<dd>
|
||||
|
@ -36,13 +58,6 @@
|
|||
</ol>
|
||||
</dd>
|
||||
</dl>
|
||||
{{else if child.Redirect}}
|
||||
<dl class="redirect">
|
||||
<dt data-tooltip="Redirect">Redirect</dt>
|
||||
<dd>
|
||||
{{child.Name}}
|
||||
</dd>
|
||||
</dl>
|
||||
{{else}}
|
||||
{{child.Name}}
|
||||
{{/if}}
|
||||
|
|
|
@ -53,45 +53,74 @@ export const getRoutes = function(nodes, uid) {
|
|||
};
|
||||
export const getResolvers = function(dc, nspace = 'default', targets = {}, nodes = {}) {
|
||||
const resolvers = {};
|
||||
Object.values(targets).forEach(target => {
|
||||
const node = nodes[`resolver:${target.ID}`];
|
||||
const resolver = findResolver(resolvers, target.Service, nspace, dc);
|
||||
// We use this to figure out whether this target is a redirect target
|
||||
const alternate = getAlternateServices([target.ID], `service.${nspace}.${dc}`);
|
||||
|
||||
let failovers;
|
||||
// Figure out the failover type
|
||||
if (typeof node.Resolver.Failover !== 'undefined') {
|
||||
failovers = getAlternateServices(node.Resolver.Failover.Targets, target.ID);
|
||||
}
|
||||
switch (true) {
|
||||
// This target is a redirect
|
||||
case alternate.Type !== 'Service':
|
||||
resolver.Children.push({
|
||||
Redirect: true,
|
||||
ID: target.ID,
|
||||
Name: target[alternate.Type],
|
||||
});
|
||||
break;
|
||||
// This target is a Subset
|
||||
case typeof target.ServiceSubset !== 'undefined':
|
||||
resolver.Children.push({
|
||||
// make all our resolver nodes
|
||||
Object.values(nodes)
|
||||
.filter(item => item.Type === 'resolver')
|
||||
.forEach(function(item) {
|
||||
const parts = item.Name.split('.');
|
||||
let subset;
|
||||
// this will leave behind the service.name.nspace.dc even if the service name contains a dot
|
||||
if (parts.length > 3) {
|
||||
subset = parts.shift();
|
||||
}
|
||||
parts.reverse();
|
||||
// slice off from dc.nspace onwards leaving the potentially dot containing service name
|
||||
// const nodeDc =
|
||||
parts.shift();
|
||||
// const nodeNspace =
|
||||
parts.shift();
|
||||
// if it does contain a dot put it back to the correct order
|
||||
parts.reverse();
|
||||
const service = parts.join('.');
|
||||
const resolver = findResolver(resolvers, service, nspace, dc);
|
||||
let failovers;
|
||||
if (typeof item.Resolver.Failover !== 'undefined') {
|
||||
// figure out what type of failover this is
|
||||
failovers = getAlternateServices(item.Resolver.Failover.Targets, item.Name);
|
||||
}
|
||||
if (subset) {
|
||||
const child = {
|
||||
Subset: true,
|
||||
ID: target.ID,
|
||||
Name: target.ServiceSubset,
|
||||
Filter: target.Subset.Filter,
|
||||
...(typeof failovers !== 'undefined'
|
||||
? {
|
||||
Failover: failovers,
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
break;
|
||||
// This target is just normal service that might have failovers
|
||||
default:
|
||||
ID: item.Name,
|
||||
Name: subset,
|
||||
};
|
||||
if (typeof failovers !== 'undefined') {
|
||||
child.Failover = failovers;
|
||||
}
|
||||
resolver.Children.push(child);
|
||||
} else {
|
||||
if (typeof failovers !== 'undefined') {
|
||||
resolver.Failover = failovers;
|
||||
}
|
||||
}
|
||||
});
|
||||
Object.values(targets).forEach(target => {
|
||||
// Failovers don't have a specific node
|
||||
if (typeof nodes[`resolver:${target.ID}`] !== 'undefined') {
|
||||
// We use this to figure out whether this target is a redirect target
|
||||
const alternate = getAlternateServices([target.ID], `service.${nspace}.${dc}`);
|
||||
// as Failovers don't make it here, we know anything that has alternateServices
|
||||
// must be a redirect
|
||||
if (alternate.Type !== 'Service') {
|
||||
// find the already created resolver
|
||||
const resolver = findResolver(resolvers, target.Service, nspace, dc);
|
||||
// and add the redirect as a child, redirects are always children
|
||||
const child = {
|
||||
Redirect: true,
|
||||
ID: target.ID,
|
||||
Name: target[alternate.Type],
|
||||
};
|
||||
// redirects can then also have failovers
|
||||
// so it this one does, figure out what type they are and add them
|
||||
// to the redirect
|
||||
if (typeof nodes[`resolver:${target.ID}`].Resolver.Failover !== 'undefined') {
|
||||
child.Failover = getAlternateServices(
|
||||
nodes[`resolver:${target.ID}`].Resolver.Failover.Targets,
|
||||
target.ID
|
||||
);
|
||||
}
|
||||
resolver.Children.push(child);
|
||||
}
|
||||
}
|
||||
});
|
||||
return Object.values(resolvers);
|
||||
|
|
|
@ -92,4 +92,108 @@ module('Unit | Utility | components/discovery-chain/get-resolvers', function() {
|
|||
})
|
||||
);
|
||||
});
|
||||
test('it finds subsets with failovers correctly', function(assert) {
|
||||
return Promise.resolve({
|
||||
Chain: {
|
||||
ServiceName: 'service-name',
|
||||
Namespace: 'default',
|
||||
Datacenter: 'dc-1',
|
||||
Protocol: 'http',
|
||||
StartNode: '',
|
||||
Nodes: {
|
||||
'resolver:v2.dc-failover.default.dc-1': {
|
||||
Type: 'resolver',
|
||||
Name: 'v2.dc-failover.default.dc-1',
|
||||
Resolver: {
|
||||
Target: 'v2.dc-failover.defauilt.dc-1',
|
||||
Failover: {
|
||||
Targets: ['v2.dc-failover.default.dc-5', 'v2.dc-failover.default.dc-6'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Targets: {
|
||||
'v2.dc-failover.default.dc-1': {
|
||||
ID: 'v2.dc-failover.default.dc-1',
|
||||
Service: 'dc-failover',
|
||||
Namespace: 'default',
|
||||
Datacenter: 'dc-1',
|
||||
Subset: {
|
||||
Filter: '',
|
||||
},
|
||||
},
|
||||
'v2.dc-failover.default.dc-6': {
|
||||
ID: 'v2.dc-failover.default.dc-6',
|
||||
Service: 'dc-failover',
|
||||
Namespace: 'default',
|
||||
Datacenter: 'dc-6',
|
||||
Subset: {
|
||||
Filter: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}).then(function({ Chain }) {
|
||||
const actual = getResolvers(dc, nspace, Chain.Targets, Chain.Nodes);
|
||||
const expected = {
|
||||
ID: 'dc-failover.default.dc-1',
|
||||
Name: 'dc-failover',
|
||||
Children: [
|
||||
{
|
||||
Subset: true,
|
||||
ID: 'v2.dc-failover.default.dc-1',
|
||||
Name: 'v2',
|
||||
Failover: {
|
||||
Type: 'Datacenter',
|
||||
Targets: ['dc-5', 'dc-6'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
assert.deepEqual(actual[0], expected);
|
||||
});
|
||||
});
|
||||
test('it finds services with failovers correctly', function(assert) {
|
||||
return Promise.resolve({
|
||||
Chain: {
|
||||
ServiceName: 'service-name',
|
||||
Namespace: 'default',
|
||||
Datacenter: 'dc-1',
|
||||
Protocol: 'http',
|
||||
StartNode: '',
|
||||
Nodes: {
|
||||
'resolver:dc-failover.default.dc-1': {
|
||||
Type: 'resolver',
|
||||
Name: 'dc-failover.default.dc-1',
|
||||
Resolver: {
|
||||
Target: 'dc-failover.defauilt.dc-1',
|
||||
Failover: {
|
||||
Targets: ['dc-failover.default.dc-5', 'dc-failover.default.dc-6'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Targets: {
|
||||
'dc-failover.default.dc-1': {
|
||||
ID: 'dc-failover.default.dc-1',
|
||||
Service: 'dc-failover',
|
||||
Namespace: 'default',
|
||||
Datacenter: 'dc-1',
|
||||
},
|
||||
},
|
||||
},
|
||||
}).then(function({ Chain }) {
|
||||
const actual = getResolvers(dc, nspace, Chain.Targets, Chain.Nodes);
|
||||
const expected = {
|
||||
ID: 'dc-failover.default.dc-1',
|
||||
Name: 'dc-failover',
|
||||
Children: [],
|
||||
Failover: {
|
||||
Type: 'Datacenter',
|
||||
Targets: ['dc-5', 'dc-6'],
|
||||
},
|
||||
};
|
||||
assert.deepEqual(actual[0], expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue