diff --git a/.changelog/11679.txt b/.changelog/11679.txt new file mode 100644 index 0000000000..daf39eb849 --- /dev/null +++ b/.changelog/11679.txt @@ -0,0 +1,3 @@ +```release-note:feature +ui: Adds support for partitions to the Routing visualization. +``` diff --git a/ui/packages/consul-ui/app/components/consul/discovery-chain/README.mdx b/ui/packages/consul-ui/app/components/consul/discovery-chain/README.mdx new file mode 100644 index 0000000000..0a25754cbb --- /dev/null +++ b/ui/packages/consul-ui/app/components/consul/discovery-chain/README.mdx @@ -0,0 +1,18 @@ +--- +type: ember +state: needs-love +--- +# Consul::DiscoveryChain + +Mainly presentational component to visualize a discovery-chain. + +```hbs preview-template + +{{#if source.data}} + +{{/if}} + +``` + diff --git a/ui/packages/consul-ui/app/components/consul/discovery-chain/index.js b/ui/packages/consul-ui/app/components/consul/discovery-chain/index.js index 31842c7dea..8b16e5b5c1 100644 --- a/ui/packages/consul-ui/app/components/consul/discovery-chain/index.js +++ b/ui/packages/consul-ui/app/components/consul/discovery-chain/index.js @@ -44,8 +44,8 @@ export default Component.extend({ !routes.find(item => typeof item.Definition === 'undefined') ) { let nextNode; - const resolverID = `resolver:${this.chain.ServiceName}.${this.chain.Namespace}.${this.chain.Datacenter}`; - const splitterID = `splitter:${this.chain.ServiceName}.${this.chain.Namespace}`; + const resolverID = `resolver:${this.chain.ServiceName}.${this.chain.Namespace}.${this.chain.Partition}.${this.chain.Datacenter}`; + const splitterID = `splitter:${this.chain.ServiceName}.${this.chain.Namespace}.${this.chain.Partition}`; // The default router should look for a splitter first, // if there isn't one try the default resolver if (typeof this.chain.Nodes[splitterID] !== 'undefined') { @@ -106,6 +106,7 @@ export default Component.extend({ resolvers: computed('chain.{Nodes,Targets}', function() { return getResolvers( this.chain.Datacenter, + this.chain.Partition, this.chain.Namespace, get(this, 'chain.Targets'), get(this, 'chain.Nodes') diff --git a/ui/packages/consul-ui/app/components/consul/discovery-chain/utils.js b/ui/packages/consul-ui/app/components/consul/discovery-chain/utils.js index 4dd42d1ff5..e7698caa9d 100644 --- a/ui/packages/consul-ui/app/components/consul/discovery-chain/utils.js +++ b/ui/packages/consul-ui/app/components/consul/discovery-chain/utils.js @@ -1,10 +1,10 @@ const getNodesByType = function(nodes = {}, type) { return Object.values(nodes).filter(item => item.Type === type); }; -const findResolver = function(resolvers, service, nspace = 'default', dc) { +const findResolver = function(resolvers, service, nspace = 'default', partition = 'default', dc) { if (typeof resolvers[service] === 'undefined') { resolvers[service] = { - ID: `${service}.${nspace}.${dc}`, + ID: `${service}.${nspace}.${partition}.${dc}`, Name: service, Children: [], }; @@ -19,7 +19,7 @@ export const getAlternateServices = function(targets, a) { // we might have more data from the endpoint so we don't have to guess // right now the backend also doesn't support dots in service names const [aRev, bRev] = [a, b].map(item => item.split('.').reverse()); - const types = ['Datacenter', 'Namespace', 'Service', 'Subset']; + const types = ['Datacenter', 'Partition', 'Namespace', 'Service', 'Subset']; return bRev.find(function(item, i) { const res = item !== aRev[i]; if (res) { @@ -61,7 +61,13 @@ export const getRoutes = function(nodes, uid) { ); }, []); }; -export const getResolvers = function(dc, nspace = 'default', targets = {}, nodes = {}) { +export const getResolvers = function( + dc, + partition = 'default', + nspace = 'default', + targets = {}, + nodes = {} +) { const resolvers = {}; // make all our resolver nodes Object.values(nodes) @@ -70,7 +76,7 @@ export const getResolvers = function(dc, nspace = 'default', targets = {}, nodes 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) { + if (parts.length > 4) { subset = parts.shift(); } parts.reverse(); @@ -79,10 +85,12 @@ export const getResolvers = function(dc, nspace = 'default', targets = {}, nodes parts.shift(); // const nodeNspace = parts.shift(); + // const nodePartition = + 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); + const resolver = findResolver(resolvers, service, nspace, partition, dc); let failovers; if (typeof item.Resolver.Failover !== 'undefined') { // figure out what type of failover this is @@ -108,12 +116,12 @@ export const getResolvers = function(dc, nspace = 'default', targets = {}, nodes // 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}`); + const alternate = getAlternateServices([target.ID], `service.${nspace}.${partition}.${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); + const resolver = findResolver(resolvers, target.Service, nspace, partition, dc); // and add the redirect as a child, redirects are always children const child = { Redirect: true, diff --git a/ui/packages/consul-ui/mock-api/v1/discovery-chain/_ b/ui/packages/consul-ui/mock-api/v1/discovery-chain/_ index f5f5d0109e..fe51860d2a 100644 --- a/ui/packages/consul-ui/mock-api/v1/discovery-chain/_ +++ b/ui/packages/consul-ui/mock-api/v1/discovery-chain/_ @@ -2,7 +2,9 @@ ${ [1].map(() => { const namespaces = ['default']; + const partitions = ['default']; const ns = location.search.ns || 'default'; + const partition = location.search.partition || 'default'; const dc = location.search.dc; const service = location.pathname.get(2); @@ -75,7 +77,7 @@ ${ const service = fake.hacker.noun().split(' ').join('-'); return { ServiceName: service, - Name: `${service}.${ns}.${dc}`, + Name: `${service}.${ns}.${partition}.${dc}`, Subsets: range( env( 'CONSUL_SUBSET_COUNT', @@ -96,7 +98,7 @@ ${ const service = resolvers[i].ServiceName; return { ServiceName: service, - Name: `${service}.${ns}.redirect-${dc}`, + Name: `${service}.${ns}.${partition}.redirect-${dc}`, Subsets: [] }; }); @@ -129,7 +131,7 @@ ${ const splitters = range( splitterCount ).map(() => ({ - Name: `${service}-${fake.hacker.noun()}.${ns}`, + Name: `${service}-${fake.hacker.noun()}.${ns}.${partition}`, Splits: range( splitCount ).map((item, i, arr) => ({ @@ -151,6 +153,7 @@ ${ "Chain": { "ServiceName": "${service}", "Namespace": "${ns}", + "Partition": "${partition}", "Datacenter": "${dc}", "Protocol": "http", "StartNode": "router:${service}", @@ -247,7 +250,8 @@ ${resolvers.map((resolver) => { ); const failover = ({ Datacenter: `${resolver.Name.replace(`.${dc}`, `.fail-${dc}`).replace(`.redirect-${dc}`, `.fail-${dc}`)}`, - Namespace: `${resolver.Name.replace(`.${ns}.`, `.fail-${ns}.`).replace(`.redirect-${ns}.`, `.fail-${ns}.`)}`, + Partition: `${resolver.Name.replace(`${ns}.${partition}.`, `${ns}.fail-${partition}.`).replace(`${ns}.redirect-${partition}.`, `${ns}.fail-${partition}.`)}`, + Namespace: `${resolver.Name.replace(`.${ns}.${partition}`, `.fail-${ns}.${partition}`).replace(`.redirect-${ns}.${partition}`, `.fail-${ns}.${partition}`)}`, })[env('CONSUL_FAILOVER_TYPE', 'Datacenter')]; return ` @@ -272,7 +276,8 @@ ${resolver.Subsets.map((subset) => { const id = `${subset}.${resolver.Name}`; const failover = ({ Datacenter: `${subset}.${resolver.Name.replace(`.${dc}`, `.fail-${dc}`)}`, - Namespace: `${subset}.${resolver.Name.replace(`.${ns}.`, `.fail-${ns}.`)}`, + Partition: `${subset}.${resolver.Name.replace(`${ns}.${partition}.`, `${ns}.fail-${partition}.`)}`, + Namespace: `${subset}.${resolver.Name.replace(`.${ns}.${partition}`, `.fail-${ns}.${partition}`)}`, })[env('CONSUL_FAILOVER_TYPE', 'Datacenter')]; return ` @@ -321,6 +326,7 @@ ${resolvers.map(item => { "ID": "${item.Name}", "Service": "${item.ServiceName}", "Namespace": "${ns}", + "Partition": "${partition}", "Datacenter": "${dc}", "MeshGateway": {}, "SNI": "${name}", @@ -335,6 +341,7 @@ ${item.Subsets.map(ktem => { "Service": "${item.ServiceName}", "ServiceSubset": "${ktem}", "Namespace": "${ns}", + "Partition": "${partition}", "Datacenter": "${dc}", "MeshGateway": { }, diff --git a/ui/packages/consul-ui/mock-api/v1/internal/ui/services b/ui/packages/consul-ui/mock-api/v1/internal/ui/services index 66b7d9fd79..592e4f4f69 100644 --- a/ui/packages/consul-ui/mock-api/v1/internal/ui/services +++ b/ui/packages/consul-ui/mock-api/v1/internal/ui/services @@ -45,6 +45,9 @@ ${[0].map( "Name":"${name}", ${typeof location.search.ns !== 'undefined' ? ` "Namespace": "${location.search.ns}", +` : ``} +${typeof location.search.partition !== 'undefined' ? ` + "Partition": "${location.search.ns}", ` : ``} "Tags": [ ${ diff --git a/ui/packages/consul-ui/tests/unit/components/consul/discovery-chain/get-alternate-services-test.js b/ui/packages/consul-ui/tests/unit/components/consul/discovery-chain/get-alternate-services-test.js index 53e986cd32..f33af1b893 100644 --- a/ui/packages/consul-ui/tests/unit/components/consul/discovery-chain/get-alternate-services-test.js +++ b/ui/packages/consul-ui/tests/unit/components/consul/discovery-chain/get-alternate-services-test.js @@ -8,8 +8,8 @@ module('Unit | Component | consul/discovery-chain/get-alternative-services', fun Targets: ['different-ns', 'different-ns2'], }; const actual = getAlternateServices( - ['service.different-ns.dc', 'service.different-ns2.dc'], - 'service.namespace.dc' + ['service.different-ns.partition.dc', 'service.different-ns2.partition.dc'], + 'service.namespace.partition.dc' ); assert.equal(actual.Type, expected.Type); assert.deepEqual(actual.Targets, expected.Targets); @@ -20,8 +20,8 @@ module('Unit | Component | consul/discovery-chain/get-alternative-services', fun Targets: ['dc1', 'dc2'], }; const actual = getAlternateServices( - ['service.namespace.dc1', 'service.namespace.dc2'], - 'service.namespace.dc' + ['service.namespace.partition.dc1', 'service.namespace.partition.dc2'], + 'service.namespace.partition.dc' ); assert.equal(actual.Type, expected.Type); assert.deepEqual(actual.Targets, expected.Targets); @@ -32,8 +32,8 @@ module('Unit | Component | consul/discovery-chain/get-alternative-services', fun Targets: ['service-2', 'service-3'], }; const actual = getAlternateServices( - ['service-2.namespace.dc', 'service-3.namespace.dc'], - 'service.namespace.dc' + ['service-2.namespace.partition.dc', 'service-3.namespace.partition.dc'], + 'service.namespace.partition.dc' ); assert.equal(actual.Type, expected.Type); assert.deepEqual(actual.Targets, expected.Targets); @@ -44,8 +44,8 @@ module('Unit | Component | consul/discovery-chain/get-alternative-services', fun Targets: ['v3', 'v2'], }; const actual = getAlternateServices( - ['v3.service.namespace.dc', 'v2.service.namespace.dc'], - 'v1.service.namespace.dc' + ['v3.service.namespace.partition.dc', 'v2.service.namespace.partition.dc'], + 'v1.service.namespace.partition.dc' ); assert.equal(actual.Type, expected.Type); assert.deepEqual(actual.Targets, expected.Targets); diff --git a/ui/packages/consul-ui/tests/unit/components/consul/discovery-chain/get-resolvers-test.js b/ui/packages/consul-ui/tests/unit/components/consul/discovery-chain/get-resolvers-test.js index 69dc9634bb..e298984092 100644 --- a/ui/packages/consul-ui/tests/unit/components/consul/discovery-chain/get-resolvers-test.js +++ b/ui/packages/consul-ui/tests/unit/components/consul/discovery-chain/get-resolvers-test.js @@ -4,6 +4,7 @@ import { get } from 'consul-ui/tests/helpers/api'; const dc = 'dc-1'; const nspace = 'default'; +const partition = 'default'; const request = { url: `/v1/discovery-chain/service-name?dc=${dc}`, }; @@ -19,7 +20,7 @@ module('Unit | Component | consul/discovery-chain/get-resolvers', function() { }, }, }).then(function({ Chain }) { - const actual = getResolvers(dc, nspace, Chain.Targets, Chain.Nodes); + const actual = getResolvers(dc, partition, nspace, Chain.Targets, Chain.Nodes); const childId = Object.keys(Chain.Targets)[1]; const target = Chain.Targets[`${childId}`]; const firstChild = actual[0].Children[0]; @@ -39,7 +40,7 @@ module('Unit | Component | consul/discovery-chain/get-resolvers', function() { }, }, }).then(function({ Chain }) { - const actual = getResolvers(dc, nspace, Chain.Targets, Chain.Nodes); + const actual = getResolvers(dc, partition, nspace, Chain.Targets, Chain.Nodes); const childId = Object.keys(Chain.Targets)[1]; const target = Chain.Targets[`${childId}`]; const firstChild = actual[0].Children[0]; @@ -61,7 +62,7 @@ module('Unit | Component | consul/discovery-chain/get-resolvers', function() { }, }, }).then(function({ Chain }) { - const actual = getResolvers(dc, nspace, Chain.Targets, Chain.Nodes); + const actual = getResolvers(dc, partition, nspace, Chain.Targets, Chain.Nodes); const actualSubset = actual[0].Children[0]; assert.equal(actualSubset.Subset, true); assert.equal(actualSubset.Failover.Type, failoverType); @@ -71,7 +72,7 @@ module('Unit | Component | consul/discovery-chain/get-resolvers', function() { }); test('it assigns Failovers correctly', function(assert) { return Promise.all( - ['Datacenter', 'Namespace'].map(function(failoverType, i) { + ['Datacenter', 'Partition', 'Namespace'].map(function(failoverType, i) { return get(request.url, { headers: { cookie: { @@ -83,7 +84,7 @@ module('Unit | Component | consul/discovery-chain/get-resolvers', function() { }, }, }).then(function({ Chain }) { - const actual = getResolvers(dc, nspace, Chain.Targets, Chain.Nodes); + const actual = getResolvers(dc, partition, nspace, Chain.Targets, Chain.Nodes); const node = Chain.Nodes[`resolver:${Object.keys(Chain.Targets)[0]}`]; const expected = node.Resolver.Failover.Targets.map(item => item.split('.').reverse()[i]); assert.equal(actual[0].Failover.Type, failoverType); @@ -101,31 +102,36 @@ module('Unit | Component | consul/discovery-chain/get-resolvers', function() { Protocol: 'http', StartNode: '', Nodes: { - 'resolver:v2.dc-failover.default.dc-1': { + 'resolver:v2.dc-failover.default.default.dc-1': { Type: 'resolver', - Name: 'v2.dc-failover.default.dc-1', + Name: 'v2.dc-failover.default.default.dc-1', Resolver: { - Target: 'v2.dc-failover.default.dc-1', + Target: 'v2.dc-failover.default.default.dc-1', Failover: { - Targets: ['v2.dc-failover.default.dc-5', 'v2.dc-failover.default.dc-6'], + Targets: [ + 'v2.dc-failover.default.default.dc-5', + 'v2.dc-failover.default.default.dc-6', + ], }, }, }, }, Targets: { - 'v2.dc-failover.default.dc-1': { - ID: 'v2.dc-failover.default.dc-1', + 'v2.dc-failover.default.default.dc-1': { + ID: 'v2.dc-failover.default.default.dc-1', Service: 'dc-failover', Namespace: 'default', + Partition: 'default', Datacenter: 'dc-1', Subset: { Filter: '', }, }, - 'v2.dc-failover.default.dc-6': { - ID: 'v2.dc-failover.default.dc-6', + 'v2.dc-failover.default.default.dc-6': { + ID: 'v2.dc-failover.default.default.dc-6', Service: 'dc-failover', Namespace: 'default', + Partition: 'default', Datacenter: 'dc-6', Subset: { Filter: '', @@ -134,14 +140,14 @@ module('Unit | Component | consul/discovery-chain/get-resolvers', function() { }, }, }).then(function({ Chain }) { - const actual = getResolvers(dc, nspace, Chain.Targets, Chain.Nodes); + const actual = getResolvers(dc, partition, nspace, Chain.Targets, Chain.Nodes); const expected = { - ID: 'dc-failover.default.dc-1', + ID: 'dc-failover.default.default.dc-1', Name: 'dc-failover', Children: [ { Subset: true, - ID: 'v2.dc-failover.default.dc-1', + ID: 'v2.dc-failover.default.default.dc-1', Name: 'v2', Failover: { Type: 'Datacenter', @@ -162,30 +168,31 @@ module('Unit | Component | consul/discovery-chain/get-resolvers', function() { Protocol: 'http', StartNode: '', Nodes: { - 'resolver:dc-failover.default.dc-1': { + 'resolver:dc-failover.default.default.dc-1': { Type: 'resolver', - Name: 'dc-failover.default.dc-1', + Name: 'dc-failover.default.default.dc-1', Resolver: { - Target: 'dc-failover.default.dc-1', + Target: 'dc-failover.default.default.dc-1', Failover: { - Targets: ['dc-failover.default.dc-5', 'dc-failover.default.dc-6'], + Targets: ['dc-failover.default.default.dc-5', 'dc-failover.default.default.dc-6'], }, }, }, }, Targets: { - 'dc-failover.default.dc-1': { - ID: 'dc-failover.default.dc-1', + 'dc-failover.default.default.dc-1': { + ID: 'dc-failover.default.default.dc-1', Service: 'dc-failover', Namespace: 'default', + Partition: 'default', Datacenter: 'dc-1', }, }, }, }).then(function({ Chain }) { - const actual = getResolvers(dc, nspace, Chain.Targets, Chain.Nodes); + const actual = getResolvers(dc, partition, nspace, Chain.Targets, Chain.Nodes); const expected = { - ID: 'dc-failover.default.dc-1', + ID: 'dc-failover.default.default.dc-1', Name: 'dc-failover', Children: [], Failover: { @@ -201,37 +208,42 @@ module('Unit | Component | consul/discovery-chain/get-resolvers', function() { Chain: { ServiceName: 'service-name', Namespace: 'default', + Partition: 'default', Datacenter: 'dc-1', Protocol: 'http', StartNode: '', Nodes: { - 'resolver:dc-failover.default.redirect-dc-1': { + 'resolver:dc-failover.default.default.redirect-dc-1': { Type: 'resolver', - Name: 'dc-failover.default.redirect-dc-1', + Name: 'dc-failover.default.default.redirect-dc-1', Resolver: { - Target: 'dc-failover.default.redirect-dc-1', + Target: 'dc-failover.default.default.redirect-dc-1', Failover: { - Targets: ['dc-failover.default.redirect-dc-5', 'dc-failover.default.redirect-dc-6'], + Targets: [ + 'dc-failover.default.default.redirect-dc-5', + 'dc-failover.default.default.redirect-dc-6', + ], }, }, }, }, Targets: { 'dc-failover.default.redirect-dc-1': { - ID: 'dc-failover.default.redirect-dc-1', + ID: 'dc-failover.default.default.redirect-dc-1', Service: 'dc-failover', Namespace: 'default', + Partition: 'default', Datacenter: 'redirect-dc-1', }, }, }, }).then(function({ Chain }) { - const actual = getResolvers(dc, nspace, Chain.Targets, Chain.Nodes); + const actual = getResolvers(dc, partition, nspace, Chain.Targets, Chain.Nodes); // Both the parent and the child should have a Failover property // as in order for a redirect to have failovers it must redirect to a // service that already has failovers const expected = { - ID: 'dc-failover.default.dc-1', + ID: 'dc-failover.default.default.dc-1', Name: 'dc-failover', Failover: { Targets: ['redirect-dc-5', 'redirect-dc-6'], @@ -243,7 +255,7 @@ module('Unit | Component | consul/discovery-chain/get-resolvers', function() { Targets: ['redirect-dc-5', 'redirect-dc-6'], Type: 'Datacenter', }, - ID: 'dc-failover.default.redirect-dc-1', + ID: 'dc-failover.default.default.redirect-dc-1', Name: 'redirect-dc-1', Redirect: true, },