From a6555649afe28f36ee3bb67180aa6bed64a214ea Mon Sep 17 00:00:00 2001 From: Philipp Rudloff Date: Tue, 7 Nov 2023 15:46:02 +0100 Subject: [PATCH 1/2] chore(mocks): update mocks to handle gateway types properly Updates the dataplane mocks to handle gateway types more properly. The list views now generate names indicating which type of gateway is used so we can properly respond to this in other mocks (e.g. for Dataplane policies, we should only show the gateway policy list for builtin gateways). Updates all dataplane mocks to be more self-consistent. Signed-off-by: Philipp Rudloff --- src/test-support/FakeKuma.ts | 107 +++++++++++++++--- src/test-support/fake.ts | 1 + src/test-support/mocks/src/dataplanes.ts | 41 ++++++- .../mocks/src/dataplanes/_overview.ts | 57 +++++++--- .../mocks/src/meshes/_/dataplanes/_.ts | 44 +++---- .../src/meshes/_/dataplanes/_/_overview.ts | 58 +++------- .../src/meshes/_/dataplanes/_/policies.ts | 2 +- .../src/meshes/_/dataplanes/_overview.ts | 83 +++++--------- .../mocks/src/meshes/_/service-insights.ts | 2 +- .../mocks/src/meshes/_/service-insights/_.ts | 10 +- .../mocks/src/service-insights.ts | 2 +- src/types/index.d.ts | 50 ++++---- 12 files changed, 270 insertions(+), 187 deletions(-) diff --git a/src/test-support/FakeKuma.ts b/src/test-support/FakeKuma.ts index 53928a53e..0bbcff125 100644 --- a/src/test-support/FakeKuma.ts +++ b/src/test-support/FakeKuma.ts @@ -1,7 +1,14 @@ import { Faker } from '@faker-js/faker' import deepmerge from 'deepmerge' -import type { DataPlaneProxyStatus, ServiceStatus } from '@/types/index.d' +import type { + DataPlaneNetworking as DataplaneNetworking, + DataPlaneProxyStatus, + DataplaneGateway, + DataplaneInbound, + DataplaneOutbound, + ServiceStatus, +} from '@/types/index.d' export class KumaModule { faker: Faker @@ -45,11 +52,11 @@ export class KumaModule { ) } - serviceName(serviceType: string = 'internal') { - const prefix = `${this.faker.hacker.noun()}_` + serviceName(serviceType: 'internal' | 'external' | 'gateway_builtin' | 'gateway_delegated' = 'internal') { + const prefix = `${this.faker.hacker.noun()}-` if (serviceType === 'gateway_delegated' || serviceType === 'gateway_builtin') { - return prefix + 'gateway' + return prefix + serviceType } else { return prefix + `${this.faker.hacker.noun()}_svc.mesh:80_${serviceType}` } @@ -174,22 +181,94 @@ export class KumaModule { return Object.fromEntries(values) } - inbound(service: string, zone?: string) { + dataplaneNetworking({ type = 'proxy', inbounds = 0, isMultizone = false, service }: { type?: 'gateway_builtin' | 'gateway_delegated' | 'proxy', inbounds?: number, isMultizone?: boolean, service?: string } = {}): DataplaneNetworking { + const address = this.faker.internet.ip() + const serviceName = service || (this.faker.datatype.boolean() ? this.serviceName(type === 'proxy' ? 'internal' : type) : undefined) + const zoneName = isMultizone && this.faker.datatype.boolean() ? this.faker.hacker.noun() : undefined + + return { + address, + ...(type !== 'proxy' + ? { + gateway: this.dataplaneGateway({ + type, + service: serviceName, + zone: zoneName, + }), + } + : {}), + ...(type === 'proxy' + ? { + inbound: Array.from({ length: inbounds }) + .map(() => this.dataplaneInbound({ + service: serviceName, + zone: zoneName, + })), + } + : {}), + outbound: [ + this.dataplaneOutbound({ service: serviceName }), + ], + } + } + + dataplaneGateway({ type = 'gateway_delegated', service, zone }: { type?: 'gateway_builtin' | 'gateway_delegated', service?: string, zone?: string } = {}): DataplaneGateway { + const dataplaneType = type === 'gateway_builtin' ? 'BUILTIN' : type === 'gateway_delegated' ? 'DELEGATED' : undefined + return { - ...(this.faker.datatype.boolean() && { + tags: { + 'kuma.io/service': service ?? this.serviceName(type), + ...(zone && { 'kuma.io/zone': zone }), + }, + ...(dataplaneType && { type: dataplaneType }), + } + } + + dataplaneInbound({ service, zone }: { service?: string, zone?: string } = {}): DataplaneInbound { + const healthObject = this.faker.datatype.boolean() + ? { health: { ready: this.faker.datatype.boolean(), }, - }), - port: this.faker.internet.port(), - servicePort: this.faker.internet.port(), - serviceAddress: this.faker.internet.ip(), + } + : {} + const port = this.faker.internet.port() + const servicePort = this.faker.internet.port() + const serviceAddress = this.faker.internet.ip() + + return { + ...healthObject, + port, + servicePort, + serviceAddress, tags: { + 'kuma.io/service': service ?? this.serviceName('internal'), 'kuma.io/protocol': this.protocol(), - 'kuma.io/service': service, - ...(zone && { - 'kuma.io/zone': zone, - }), + ...(zone && { 'kuma.io/zone': zone }), + }, + } + } + + dataplaneOutbound({ service }: { service?: string }): DataplaneOutbound { + return { + port: this.faker.internet.port(), + tags: { + 'kuma.io/service': service ?? this.serviceName('internal'), + }, + } + } + + dataplaneMtls() { + const issuedBackend = this.faker.hacker.noun() + const supportedBackends = [issuedBackend].concat(this.faker.helpers.multiple(this.faker.hacker.noun)) + + return { + mTLS: { + certificateExpirationTime: this.faker.date.anytime(), + lastCertificateRegeneration: '2023-10-02T12:40:13.956741929Z', + certificateRegenerations: this.faker.number.int(), + issuedBackend, + supportedBackends, }, } } diff --git a/src/test-support/fake.ts b/src/test-support/fake.ts index 74cf8c565..ff4ab048e 100644 --- a/src/test-support/fake.ts +++ b/src/test-support/fake.ts @@ -42,6 +42,7 @@ export type AEnv = Alias export type MockEnvKeys = keyof { FAKE_SEED: string KUMA_DATAPLANE_COUNT: string + KUMA_DATAPLANEINBOUND_COUNT: string KUMA_CIRCUITBREAKER_COUNT: string KUMA_FAULTINJECTION_COUNT: string KUMA_MESHFAULTINJECTION_COUNT: string diff --git a/src/test-support/mocks/src/dataplanes.ts b/src/test-support/mocks/src/dataplanes.ts index 0f6b3265b..0f739a6e7 100644 --- a/src/test-support/mocks/src/dataplanes.ts +++ b/src/test-support/mocks/src/dataplanes.ts @@ -1,11 +1,39 @@ import type { EndpointDependencies, MockResponder } from '@/test-support' export default ({ env, fake, pager }: EndpointDependencies): MockResponder => (req) => { + const query = req.url.searchParams + const _gateway = query.get('gateway') ?? '' + const _name = query.get('name') ?? '' + const _tags = query.get('tag') ?? '' + const { offset, total, next, pageTotal } = pager( env('KUMA_DATAPLANE_COUNT', `${fake.number.int({ min: 1, max: 1000 })}`), req, '/dataplanes', ) + const inbounds = parseInt(env('KUMA_DATAPLANEINBOUND_COUNT', `${fake.number.int({ min: 1, max: 3 })}`)) + + const tags = _tags !== '' + ? Object.fromEntries([_tags] + .filter((tag) => Boolean(tag)) + .map(item => { const [key, ...rest] = item.split(':'); return [key, rest.join(':')] })) + : {} + + let filterType: 'gateway_builtin' | 'gateway_delegated' | 'proxy' | undefined + if (_gateway === 'builtin' || _gateway === 'delegated') { + filterType = `gateway_${_gateway}` + } else if (_gateway === 'false') { + filterType = 'proxy' + } else if (tags['kuma.io/service']) { + if (tags['kuma.io/service'].includes('gateway_builtin')) { + filterType = 'gateway_builtin' + } else if (tags['kuma.io/service'].includes('gateway_delegated')) { + filterType = 'gateway_delegated' + } else { + filterType = 'proxy' + } + } + return { headers: {}, body: { @@ -14,11 +42,18 @@ export default ({ env, fake, pager }: EndpointDependencies): MockResponder => (r items: Array.from({ length: pageTotal }).map((_, i) => { const id = offset + i + const isMultizone = true && fake.datatype.boolean() + + const type = filterType ?? fake.helpers.arrayElement(['gateway_builtin', 'gateway_delegated', 'proxy']) + const mesh = `${fake.hacker.noun()}-${id}` + const name = `${_name || fake.kuma.dataPlaneProxyName()}-${type}-${id}` + const service = tags['kuma.io/service'] + return { type: 'Dataplane', - mesh: `${fake.hacker.noun()}-${id}`, - name: `${fake.kuma.dataPlaneProxyName()}-${id}`, - networking: {}, + mesh, + name, + networking: fake.kuma.dataplaneNetworking({ type, inbounds, isMultizone, service }), } }), }, diff --git a/src/test-support/mocks/src/dataplanes/_overview.ts b/src/test-support/mocks/src/dataplanes/_overview.ts index e454fa270..5f83fbe3c 100644 --- a/src/test-support/mocks/src/dataplanes/_overview.ts +++ b/src/test-support/mocks/src/dataplanes/_overview.ts @@ -1,41 +1,66 @@ import type { EndpointDependencies, MockResponder } from '@/test-support' + export default ({ fake, pager, env }: EndpointDependencies): MockResponder => (req) => { + const query = req.url.searchParams + const _gateway = query.get('gateway') ?? '' + const _name = query.get('name') ?? '' + const _tags = query.get('tag') ?? '' + + const inbounds = parseInt(env('KUMA_DATAPLANEINBOUND_COUNT', `${fake.number.int({ min: 1, max: 3 })}`)) const subscriptionCount = parseInt(env('KUMA_SUBSCRIPTION_COUNT', `${fake.number.int({ min: 1, max: 10 })}`)) + const isMtlsEnabled = env('KUMA_MTLS_ENABLED', fake.helpers.arrayElement(['false', 'true'])) === 'true' const { offset, total, next, pageTotal } = pager( env('KUMA_DATAPLANE_COUNT', `${fake.number.int({ min: 1, max: 1000 })}`), req, '/dataplanes/_overview', ) + const tags = _tags !== '' + ? Object.fromEntries([_tags] + .filter((tag) => Boolean(tag)) + .map(item => { const [key, ...rest] = item.split(':'); return [key, rest.join(':')] })) + : {} + + let filterType: 'gateway_builtin' | 'gateway_delegated' | 'proxy' | undefined + if (_gateway === 'builtin' || _gateway === 'delegated') { + filterType = `gateway_${_gateway}` + } else if (_gateway === 'false') { + filterType = 'proxy' + } else if (tags['kuma.io/service']) { + if (tags['kuma.io/service'].includes('gateway_builtin')) { + filterType = 'gateway_builtin' + } else if (tags['kuma.io/service'].includes('gateway_delegated')) { + filterType = 'gateway_delegated' + } else { + filterType = 'proxy' + } + } + return { headers: {}, body: { total, items: Array.from({ length: pageTotal }).map((_, i) => { const id = offset + i + + const isMultizone = true && fake.datatype.boolean() + + const type = filterType ?? fake.helpers.arrayElement(['gateway_builtin', 'gateway_delegated', 'proxy']) + const mesh = `${fake.hacker.noun()}-${id}` + const name = `${_name || fake.kuma.dataPlaneProxyName()}-${type}-${id}` + const service = tags['kuma.io/service'] + return { type: 'DataplaneOverview', - mesh: `${fake.hacker.noun()}-${id}`, - name: `${fake.kuma.dataPlaneProxyName()}-${id}`, + mesh, + name, creationTime: '2021-02-17T08:33:36.442044+01:00', modificationTime: '2021-02-17T08:33:36.442044+01:00', dataplane: { - networking: { - address: '127.0.0.1', - inbound: [ - fake.kuma.inbound(fake.kuma.serviceName()), - ], - outbound: [ - { - port: 10001, - tags: { - 'kuma.io/service': 'frontend', - }, - }, - ], - }, + networking: fake.kuma.dataplaneNetworking({ type, inbounds, isMultizone, service }), }, dataplaneInsight: { + ...(isMtlsEnabled ? { mTLS: fake.kuma.dataplaneMtls() } : {}), subscriptions: Array.from({ length: subscriptionCount }).map((item, i, arr) => { return { id: '118b4d6f-7a98-4172-96d9-85ffb8b20b16', diff --git a/src/test-support/mocks/src/meshes/_/dataplanes/_.ts b/src/test-support/mocks/src/meshes/_/dataplanes/_.ts index 8ceb485c5..df06e53f9 100644 --- a/src/test-support/mocks/src/meshes/_/dataplanes/_.ts +++ b/src/test-support/mocks/src/meshes/_/dataplanes/_.ts @@ -1,45 +1,27 @@ import type { EndpointDependencies, MockResponder } from '@/test-support' -export default ({ fake }: EndpointDependencies): MockResponder => (req) => { +export default ({ fake, env }: EndpointDependencies): MockResponder => (req) => { const { mesh, name } = req.params - const isGateway = name.includes('-gateway') - const service = fake.kuma.serviceName(isGateway ? 'gateway_builtin' : 'internal') + + const inbounds = parseInt(env('KUMA_DATAPLANEINBOUND_COUNT', `${fake.number.int({ min: 1, max: 3 })}`)) + + let type: 'gateway_builtin' | 'gateway_delegated' | 'proxy' = 'proxy' + if (name.includes('-gateway_builtin')) { + type = 'gateway_builtin' + } else if (name.includes('-gateway_delegated')) { + type = 'gateway_delegated' + } + const isMultizone = true && fake.datatype.boolean() - const zone = fake.hacker.noun() return { - headers: { - }, + headers: {}, body: { type: 'Dataplane', mesh, name, creationTime: '2021-02-17T08:33:36.442044+01:00', modificationTime: '2021-02-17T08:33:36.442044+01:00', - networking: { - address: fake.internet.ip(), - ...(isGateway && { - gateway: { - tags: { - 'kuma.io/service': service, - ...(isMultizone && { - 'kuma.io/zone': zone, - }), - }, - type: 'BUILTIN', - }, - }), - inbound: [ - fake.kuma.inbound(service, isMultizone ? zone : undefined), - ], - outbound: [ - { - port: fake.internet.port(), - tags: { - 'kuma.io/service': fake.kuma.serviceName(), - }, - }, - ], - }, + networking: fake.kuma.dataplaneNetworking({ type, inbounds, isMultizone }), }, } } diff --git a/src/test-support/mocks/src/meshes/_/dataplanes/_/_overview.ts b/src/test-support/mocks/src/meshes/_/dataplanes/_/_overview.ts index 2f726d995..2d8b8c681 100644 --- a/src/test-support/mocks/src/meshes/_/dataplanes/_/_overview.ts +++ b/src/test-support/mocks/src/meshes/_/dataplanes/_/_overview.ts @@ -1,18 +1,22 @@ import type { EndpointDependencies, MockResponder } from '@/test-support' export default ({ env, fake }: EndpointDependencies): MockResponder => (req) => { const { mesh, name } = req.params + + const inbounds = parseInt(env('KUMA_DATAPLANEINBOUND_COUNT', `${fake.number.int({ min: 1, max: 3 })}`)) const subscriptionCount = parseInt(env('KUMA_SUBSCRIPTION_COUNT', `${fake.number.int({ min: 1, max: 10 })}`)) + const isMtlsEnabled = env('KUMA_MTLS_ENABLED', fake.helpers.arrayElement(['false', 'true'])) === 'true' - const service = fake.kuma.serviceName() - const isMultizone = true && fake.datatype.boolean() - const zone = fake.hacker.noun() + let type: 'gateway_builtin' | 'gateway_delegated' | 'proxy' = 'proxy' + if (name.includes('-gateway_builtin')) { + type = 'gateway_builtin' + } else if (name.includes('-gateway_delegated')) { + type = 'gateway_delegated' + } - const issuedBackend = fake.hacker.noun() - const supportedBackends = [issuedBackend].concat(fake.helpers.multiple(fake.hacker.noun)) + const isMultizone = true && fake.datatype.boolean() return { - headers: { - }, + headers: {}, body: { type: 'DataplaneOverview', mesh, @@ -20,33 +24,10 @@ export default ({ env, fake }: EndpointDependencies): MockResponder => (req) => creationTime: '2021-02-17T08:33:36.442044+01:00', modificationTime: '2021-02-17T08:33:36.442044+01:00', dataplane: { - networking: { - address: fake.internet.ip(), - ...(name.includes('-gateway') && { - gateway: { - tags: { - 'kuma.io/service': service, - ...(isMultizone && { - 'kuma.io/zone': zone, - }), - }, - type: 'BUILTIN', - }, - }), - inbound: [ - fake.kuma.inbound(service, isMultizone ? zone : undefined), - ], - outbound: [ - { - port: fake.internet.port(), - tags: { - 'kuma.io/service': fake.kuma.serviceName(), - }, - }, - ], - }, + networking: fake.kuma.dataplaneNetworking({ type, inbounds, isMultizone }), }, dataplaneInsight: { + ...(isMtlsEnabled ? { mTLS: fake.kuma.dataplaneMtls() } : {}), subscriptions: Array.from({ length: subscriptionCount }).map((item, i, arr) => { return { id: '118b4d6f-7a98-4172-96d9-85ffb8b20b16', @@ -91,19 +72,6 @@ export default ({ env, fake }: EndpointDependencies): MockResponder => (req) => }, } }), - ...( - JSON.parse(env('KUMA_MTLS_ENABLED', fake.helpers.arrayElement(['false', 'true']))) - ? { - mTLS: { - certificateExpirationTime: fake.date.anytime(), - lastCertificateRegeneration: '2023-10-02T12:40:13.956741929Z', - certificateRegenerations: fake.number.int(), - issuedBackend, - supportedBackends, - }, - } - : {} - ), }, }, } diff --git a/src/test-support/mocks/src/meshes/_/dataplanes/_/policies.ts b/src/test-support/mocks/src/meshes/_/dataplanes/_/policies.ts index 6666f4af0..d6791717f 100644 --- a/src/test-support/mocks/src/meshes/_/dataplanes/_/policies.ts +++ b/src/test-support/mocks/src/meshes/_/dataplanes/_/policies.ts @@ -1,7 +1,7 @@ import type { EndpointDependencies, MockResponder } from '@/test-support' export default (_deps: EndpointDependencies): MockResponder => (req) => { const params = req.params - if (params.name.includes('-gateway')) { + if (params.name.includes('-gateway_builtin')) { return { headers: {}, body: { diff --git a/src/test-support/mocks/src/meshes/_/dataplanes/_overview.ts b/src/test-support/mocks/src/meshes/_/dataplanes/_overview.ts index 78f58e7f3..c8affaa21 100644 --- a/src/test-support/mocks/src/meshes/_/dataplanes/_overview.ts +++ b/src/test-support/mocks/src/meshes/_/dataplanes/_overview.ts @@ -1,22 +1,41 @@ import type { EndpointDependencies, MockResponder } from '@/test-support' export default ({ fake, pager, env }: EndpointDependencies): MockResponder => (req) => { - const params = req.params + const { mesh } = req.params const query = req.url.searchParams const _gateway = query.get('gateway') ?? '' const _name = query.get('name') ?? '' - const _tags = query.get('tags') ?? '' + const _tags = query.get('tag') ?? '' + const inbounds = parseInt(env('KUMA_DATAPLANEINBOUND_COUNT', `${fake.number.int({ min: 1, max: 3 })}`)) const subscriptionCount = parseInt(env('KUMA_SUBSCRIPTION_COUNT', `${fake.number.int({ min: 1, max: 10 })}`)) + const isMtlsEnabled = env('KUMA_MTLS_ENABLED', fake.helpers.arrayElement(['false', 'true'])) === 'true' const { offset, total, next, pageTotal } = pager( env('KUMA_DATAPLANE_COUNT', `${fake.number.int({ min: 1, max: 1000 })}`), req, - `/meshes/${params.mesh}/dataplanes/_overview`, + `/meshes/${mesh}/dataplanes/_overview`, ) - const hasGateways = _gateway !== 'false' - const hasSidecars = !['true', 'builtin', 'delegated'].includes(_gateway) - const tags = _tags !== '' ? Object.fromEntries([_tags].filter(Boolean).map(item => { const [key, ...rest] = item.split(':'); return [key, rest.join(':')] })) : {} + const tags = _tags !== '' + ? Object.fromEntries([_tags] + .filter((tag) => Boolean(tag)) + .map(item => { const [key, ...rest] = item.split(':'); return [key, rest.join(':')] })) + : {} + + let filterType: 'gateway_builtin' | 'gateway_delegated' | 'proxy' | undefined + if (_gateway === 'builtin' || _gateway === 'delegated') { + filterType = `gateway_${_gateway}` + } else if (_gateway === 'false') { + filterType = 'proxy' + } else if (tags['kuma.io/service']) { + if (tags['kuma.io/service'].includes('gateway_builtin')) { + filterType = 'gateway_builtin' + } else if (tags['kuma.io/service'].includes('gateway_delegated')) { + filterType = 'gateway_delegated' + } else { + filterType = 'proxy' + } + } return { headers: {}, @@ -24,64 +43,24 @@ export default ({ fake, pager, env }: EndpointDependencies): MockResponder => (r total, items: Array.from({ length: pageTotal }).map((_, i) => { const id = offset + i - const isGateway = (!hasSidecars || (hasGateways && fake.datatype.boolean())) const isMultizone = true && fake.datatype.boolean() - const service = tags['kuma.io/service'] ?? fake.kuma.serviceName() - const name = `${_name || fake.kuma.dataPlaneProxyName()}${isGateway ? '-gateway' : '-proxy'}-${id}` - const zone = isMultizone ? `${fake.hacker.noun()}-${id}` : undefined - - const issuedBackend = fake.hacker.noun() - const supportedBackends = [issuedBackend].concat(fake.helpers.multiple(fake.hacker.noun)) + const type = filterType ?? fake.helpers.arrayElement(['gateway_builtin', 'gateway_delegated', 'proxy']) + const name = `${_name || fake.kuma.dataPlaneProxyName()}-${type}-${id}` + const service = tags['kuma.io/service'] return { type: 'DataplaneOverview', - mesh: params.mesh, + mesh, name, creationTime: '2021-02-17T08:33:36.442044+01:00', modificationTime: '2021-02-17T08:33:36.442044+01:00', dataplane: { - networking: { - address: fake.internet.ip(), - ...(isGateway && { - gateway: { - tags: { - 'kuma.io/service': service, - ...(zone && { - 'kuma.io/zone': zone, - }), - }, - type: _gateway === 'true' || _gateway === '' ? fake.helpers.arrayElement(['BUILTIN', 'DELEGATED']) : _gateway.toUpperCase(), - }, - }), - inbound: [ - fake.kuma.inbound(service, zone), - ], - outbound: [ - { - port: fake.internet.port(), - tags: { - 'kuma.io/service': fake.kuma.serviceName(), - }, - }, - ], - }, + networking: fake.kuma.dataplaneNetworking({ type, inbounds, isMultizone, service }), }, dataplaneInsight: { - ...( - JSON.parse(env('KUMA_MTLS_ENABLED', fake.helpers.arrayElement(['false', 'true']))) - ? { - mTLS: { - certificateExpirationTime: fake.date.anytime(), - lastCertificateRegeneration: '2023-10-02T12:40:13.956741929Z', - certificateRegenerations: fake.number.int(), - issuedBackend, - supportedBackends, - }, - } - : {} - ), + ...(isMtlsEnabled ? { mTLS: fake.kuma.dataplaneMtls() } : {}), subscriptions: Array.from({ length: subscriptionCount }).map((item, i, arr) => { return { id: '118b4d6f-7a98-4172-96d9-85ffb8b20b16', diff --git a/src/test-support/mocks/src/meshes/_/service-insights.ts b/src/test-support/mocks/src/meshes/_/service-insights.ts index 886c00dfd..e4ffcc998 100644 --- a/src/test-support/mocks/src/meshes/_/service-insights.ts +++ b/src/test-support/mocks/src/meshes/_/service-insights.ts @@ -16,7 +16,7 @@ export default ({ fake, pager, env }: EndpointDependencies): MockResponder => (r const id = offset + i const serviceType = fake.kuma.serviceType() const mesh = req.params.mesh - const name = `${fake.hacker.noun()}-${id}_${serviceType}` + const name = `${fake.hacker.noun()}-${id}-${serviceType}` const serviceInsight: any = { type: 'ServiceInsight', diff --git a/src/test-support/mocks/src/meshes/_/service-insights/_.ts b/src/test-support/mocks/src/meshes/_/service-insights/_.ts index 432504b26..c2dde970f 100644 --- a/src/test-support/mocks/src/meshes/_/service-insights/_.ts +++ b/src/test-support/mocks/src/meshes/_/service-insights/_.ts @@ -1,7 +1,15 @@ import type { EndpointDependencies, MockResponder } from '@/test-support' export default ({ fake }: EndpointDependencies): MockResponder => (req) => { const { mesh, name } = req.params - const serviceType = (name as string).split('_')[1] ?? fake.kuma.serviceType() + + let serviceType + if (name.includes('gateway_builtin')) { + serviceType = 'gateway_builtin' + } else if (name.includes('gateway_delegated')) { + serviceType = 'gateway_delegated' + } else { + serviceType = fake.kuma.serviceType() + } const serviceInsight: any = { type: 'ServiceInsight', diff --git a/src/test-support/mocks/src/service-insights.ts b/src/test-support/mocks/src/service-insights.ts index 886c00dfd..e4ffcc998 100644 --- a/src/test-support/mocks/src/service-insights.ts +++ b/src/test-support/mocks/src/service-insights.ts @@ -16,7 +16,7 @@ export default ({ fake, pager, env }: EndpointDependencies): MockResponder => (r const id = offset + i const serviceType = fake.kuma.serviceType() const mesh = req.params.mesh - const name = `${fake.hacker.noun()}-${id}_${serviceType}` + const name = `${fake.hacker.noun()}-${id}-${serviceType}` const serviceInsight: any = { type: 'ServiceInsight', diff --git a/src/types/index.d.ts b/src/types/index.d.ts index 4320b48a0..7e997b741 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -191,33 +191,39 @@ export interface DataPlaneInsight { subscriptions: DiscoverySubscription[] } +export type DataplaneGateway = { + tags: Record + /** + * Type of the gateway. **Default**: `'DELEGATED'` + */ + type?: 'BUILTIN' | 'DELEGATED' +} + +export type DataplaneInbound = { + port: number + servicePort: number + serviceAddress: string + tags: Record + health?: { + ready: boolean + } +} + +export type DataplaneOutbound = { + port: number + tags: Record +} + export type DataPlaneNetworking = { address: string - inbound?: { - port: number - servicePort: number - serviceAddress: string - tags: Record - health?: { - ready: boolean - } - }[] - outbound?: { - port: number - tags: Record - }[] + inbound?: DataplaneInbound[] + outbound?: DataplaneOutbound[] /** - * The presence of the `gateway` field means one of two things: + * The presence of the `gateway` field means the resource is a gateway. The `gateway.type` property indicates which type of gateway it is. * - * - The resource represents a builtin gateway (when `gateway.type === 'BUILTIN'`) - * - The resource represents a delegated gateway (when `gateway.type === 'DELEGATED'` or `gateway.type` is omitted) - * - * If the `gateway` field is absent, the resource represents a regular Data Plane Proxy. + * The absence of the `gateway` field means the resource is a regular Data Plane Proxy. */ - gateway?: { - tags: Record - type?: 'BUILTIN' | 'DELEGATED' - } + gateway?: DataplaneGateway } /** From 95f08c5866a7101e5d941db7683bc35cfc8e1446 Mon Sep 17 00:00:00 2001 From: Philipp Rudloff Date: Wed, 8 Nov 2023 14:58:07 +0100 Subject: [PATCH 2/2] feat(dataplanes): integrates Gateways in Data Plane Proxy list view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Integrates the Gateway list view in the Data Plane Proxy list view. Adds a type column and the appropriate type filter to the DPP list view and the service detail view’s DPP list. Removes the protocol and zone columns. Signed-off-by: Philipp Rudloff --- .../application/ListViewNavigation.feature | 10 - features/application/Titles.feature | 3 - .../mesh/dataplanes/DataplanePolicies.feature | 61 ++++++ .../mesh/dataplanes/DetailViewContent.feature | 1 + features/mesh/dataplanes/Index.feature | 163 ++++++++++++---- .../mesh/dataplanes/NoSubscriptions.feature | 13 ++ features/mesh/gateways/GatewaySummary.feature | 59 ------ features/mesh/gateways/Index.feature | 123 ------------ features/mesh/gateways/PageNavigation.feature | 17 -- .../components/BuiltinGatewayPolicies.vue} | 7 +- .../data-planes/components/DataPlaneList.vue | 150 +++++---------- ...List.vue => StandardDataplanePolicies.vue} | 42 ++-- src/app/data-planes/locales/en-us/index.yaml | 5 + src/app/data-planes/sources.ts | 69 ++++--- .../data-planes/views/DataPlaneListView.vue | 34 +++- .../views/DataPlanePoliciesView.vue | 78 ++++++-- .../views/DataPlaneSummaryView.vue | 9 +- src/app/gateways/index.ts | 20 -- src/app/gateways/locales/en-us/index.yaml | 14 -- src/app/gateways/routes.ts | 70 ------- src/app/gateways/sources.ts | 68 ------- src/app/gateways/views/GatewayListView.vue | 157 --------------- .../gateways/views/GatewayPoliciesView.vue | 111 ----------- src/app/meshes/index.ts | 7 - src/app/meshes/locales/en-us/index.yaml | 1 - src/app/meshes/routes.ts | 3 - .../views/ServiceDataPlaneProxiesView.vue | 179 ++++++++---------- src/locales/en-us/index.ts | 2 - 28 files changed, 495 insertions(+), 981 deletions(-) create mode 100644 features/mesh/dataplanes/DataplanePolicies.feature delete mode 100644 features/mesh/gateways/GatewaySummary.feature delete mode 100644 features/mesh/gateways/Index.feature delete mode 100644 features/mesh/gateways/PageNavigation.feature rename src/app/{gateways/components/GatewayDataplanePolicyList.vue => data-planes/components/BuiltinGatewayPolicies.vue} (97%) rename src/app/data-planes/components/{SidecarDataplanePolicyList.vue => StandardDataplanePolicies.vue} (94%) delete mode 100644 src/app/gateways/index.ts delete mode 100644 src/app/gateways/locales/en-us/index.yaml delete mode 100644 src/app/gateways/routes.ts delete mode 100644 src/app/gateways/sources.ts delete mode 100644 src/app/gateways/views/GatewayListView.vue delete mode 100644 src/app/gateways/views/GatewayPoliciesView.vue diff --git a/features/application/ListViewNavigation.feature b/features/application/ListViewNavigation.feature index 3e2a7f245..208648d2a 100644 --- a/features/application/ListViewNavigation.feature +++ b/features/application/ListViewNavigation.feature @@ -21,16 +21,6 @@ Feature: application / ListViewNavigation | /zones/bandwidth-0/egresses | [data-testid='zone-egress-detail-view'] | | /zones/bandwidth-0/ingresses | [data-testid='zone-ingress-detail-view'] | | /meshes | [data-testid='mesh-detail-view'] | - # TODO: Fix this view having the wrong route data-testid. It’s caused by us re-using the DPP-specific view components in gateway routes. - # | /meshes/default/gateways | [data-testid='gateway-detail-view'] | | /meshes/default/data-planes | [data-testid='data-plane-detail-view'] | | /meshes/default/services | [data-testid='service-detail-view'] | | /meshes/default/policies | [data-testid='policy-detail-view'] | - - # TODO: Remove this scenario once it can be handled by the previous one. - Scenario: The /meshes/default/gateways list view has correct detail view link - When I visit the "/meshes/default/gateways" URL - And I click the "$detail-link" element - - Then the URL contains "/meshes/default/gateways" - And the URL contains "/overview" diff --git a/features/application/Titles.feature b/features/application/Titles.feature index 1fad67994..70abdee9d 100644 --- a/features/application/Titles.feature +++ b/features/application/Titles.feature @@ -36,9 +36,6 @@ Feature: application / titles | /meshes/default/services | Services | | /meshes/default/services/service-name/overview | service-name | - | /meshes/default/gateways | Gateways | - | /meshes/default/gateways/gateway-name/overview | gateway-name | - | /meshes/default/data-planes | Data Plane Proxies | | /meshes/default/data-planes/data-plane-name/overview | data-plane-name | diff --git a/features/mesh/dataplanes/DataplanePolicies.feature b/features/mesh/dataplanes/DataplanePolicies.feature new file mode 100644 index 000000000..cf3e2db10 --- /dev/null +++ b/features/mesh/dataplanes/DataplanePolicies.feature @@ -0,0 +1,61 @@ +Feature: Dataplane policies + Background: + Given the CSS selectors + | Alias | Selector | + | builtin-gateway-dataplane-policies | [data-testid='builtin-gateway-dataplane-policies'] | + | standard-dataplane-policies | [data-testid='standard-dataplane-policies'] | + + Scenario: Dataplane policies view shows expected content for standard proxy + Given the URL "/meshes/default/dataplanes/dataplane-1/_overview" responds with + """ + body: + mesh: default + dataplane: + networking: + gateway: !!js/undefined + """ + When I visit the "/meshes/default/data-planes/dataplane-1/policies" URL + + Then the "$standard-dataplane-policies" element exists + + Scenario: Dataplane policies view shows expected content for delegated gateway + Given the URL "/meshes/default/dataplanes/dataplane-1/_overview" responds with + """ + body: + mesh: default + dataplane: + networking: + gateway: + type: DELEGATED + """ + When I visit the "/meshes/default/data-planes/dataplane-1/policies" URL + + Then the "$standard-dataplane-policies" element exists + + Scenario: Dataplane policies view shows expected content for delegated gateway (with omitted type field) + Given the URL "/meshes/default/dataplanes/dataplane-1/_overview" responds with + """ + body: + mesh: default + dataplane: + networking: + gateway: + type: !!js/undefined + """ + When I visit the "/meshes/default/data-planes/dataplane-1/policies" URL + + Then the "$standard-dataplane-policies" element exists + + Scenario: Dataplane policies view shows expected content for built-in gateway + Given the URL "/meshes/default/dataplanes/dataplane-1/_overview" responds with + """ + body: + mesh: default + dataplane: + networking: + gateway: + type: BUILTIN + """ + When I visit the "/meshes/default/data-planes/dataplane-1/policies" URL + + Then the "$builtin-gateway-dataplane-policies" element exists diff --git a/features/mesh/dataplanes/DetailViewContent.feature b/features/mesh/dataplanes/DetailViewContent.feature index f6f2b2f61..c432d1d4c 100644 --- a/features/mesh/dataplanes/DetailViewContent.feature +++ b/features/mesh/dataplanes/DetailViewContent.feature @@ -23,6 +23,7 @@ Feature: Data Plane Proxies: Detail view content And the environment """ KUMA_SUBSCRIPTION_COUNT: 2 + KUMA_DATAPLANEINBOUND_COUNT: 1 KUMA_MODE: global """ And the URL "/meshes/default/dataplanes/dpp-1-name-of-dataplane/_overview" responds with diff --git a/features/mesh/dataplanes/Index.feature b/features/mesh/dataplanes/Index.feature index 78989459a..46fbec1a8 100644 --- a/features/mesh/dataplanes/Index.feature +++ b/features/mesh/dataplanes/Index.feature @@ -1,16 +1,21 @@ Feature: mesh / dataplanes / index Background: Given the CSS selectors - | Alias | Selector | - | table | [data-testid='data-plane-collection'] | - | table-header | $table th | - | item | $table tbody tr | - | service-cell | $item:nth-child(1) td:nth-child(2) | - | protocol-cell | $item:nth-child(1) td:nth-child(3) | - | zone-cell | $item:nth-child(1) td:nth-child(4) | + | Alias | Selector | + | table | [data-testid='data-plane-collection'] | + | table-header | $table th | + | item | $table tbody tr | + | service-cell | $item:nth-child(1) td:nth-child(3) | + | select-type | [data-testid='k-select-input'] | + | select-option | .k-select-item | + | select-standard | [data-testid='k-select-item-standard'] | + | select-builtin | [data-testid='k-select-item-builtin'] | + | select-delegated | [data-testid='k-select-item-delegated'] | And the environment """ + KUMA_MODE: global KUMA_DATAPLANE_COUNT: 9 + KUMA_DATAPLANEINBOUND_COUNT: 1 KUMA_SUBSCRIPTION_COUNT: 2 """ And the URL "/meshes/default/dataplanes/_overview" responds with @@ -21,13 +26,12 @@ Feature: mesh / dataplanes / index mesh: fake-default dataplane: networking: + gateway: !!js/undefined inbound: - health: ready: true tags: kuma.io/service: service-1 - kuma.io/protocol: http - kuma.io/zone: zone-1 dataplaneInsight: mTLS: certificateExpirationTime: 2023-11-03T09:10:17Z @@ -39,39 +43,15 @@ Feature: mesh / dataplanes / index - name: fake-frontend """ - Scenario: The Proxy listing table has the correct columns (mode: global) - Given the environment - """ - KUMA_MODE: global - """ - - When I visit the "/meshes/default/data-planes" URL - - Then the "$table-header" element exists 8 times - And the "$table-header" elements contain - | Value | - | Name | - | Service | - | Protocol | - | Zone | - | Certificate Info | - | Status | - | Warnings | - - Scenario: The Proxy listing table has the correct columns (mode: standalone) - Given the environment - """ - KUMA_MODE: standalone - """ - + Scenario: The Proxy listing table has the correct columns When I visit the "/meshes/default/data-planes" URL Then the "$table-header" element exists 7 times And the "$table-header" elements contain | Value | | Name | + | Type | | Service | - | Protocol | | Certificate Info | | Status | | Warnings | @@ -83,9 +63,8 @@ Feature: mesh / dataplanes / index Then the "$item:nth-child(1)" element contains | Value | | fake-backend | + | Standard proxy | | service-1 | - | http | - | zone-1 | | Nov 3, 2023, 9:10 AM | | online | @@ -103,13 +82,12 @@ Feature: mesh / dataplanes / index mesh: fake-default dataplane: networking: + gateway: !!js/undefined inbound: - health: ready: true tags: kuma.io/service: !!js/undefined - kuma.io/protocol: !!js/undefined - kuma.io/zone: !!js/undefined dataplaneInsight: mTLS: certificateExpirationTime: !!js/undefined @@ -120,11 +98,114 @@ Feature: mesh / dataplanes / index When I visit the "/meshes/default/data-planes" URL Then the "$service-cell" element is empty - Then the "$protocol-cell" element is empty - Then the "$zone-cell" element is empty Then the "$item:nth-child(1)" element contains | Value | | dpp-2 | | No certificate | | offline | + + Rule: The listing can be filtered by type + Scenario: Filtering by "builtin" + Given the environment + """ + KUMA_DATAPLANE_COUNT: 1 + KUMA_DATAPLANEINBOUND_COUNT: 0 + """ + And the URL "/meshes/default/dataplanes/_overview" responds with + """ + body: + items: + - name: fake-transmitter-gateway_builtin-0 + dataplane: + networking: + gateway: + type: 'BUILTIN' + tags: + kuma.io/service: service-1 + """ + + When I visit the "/meshes/default/data-planes" URL + And I click the "$select-type" element + + Then the "$select-option" element exists 4 times + + When I click the "$select-builtin" element + Then the URL "/meshes/default/dataplanes/_overview" was requested with + """ + searchParams: + gateway: builtin + """ + And the "$item" element exists 1 time + And the "$item:nth-child(1)" element contains + | Value | + | fake-transmitter-gateway_builtin-0 | + | Built-in gateway | + + Scenario: Filtering by "delegated" + Given the environment + """ + KUMA_DATAPLANE_COUNT: 1 + KUMA_DATAPLANEINBOUND_COUNT: 0 + """ + And the URL "/meshes/default/dataplanes/_overview" responds with + """ + body: + items: + - name: fake-alarm-gateway_delegated-0 + dataplane: + networking: + gateway: + type: 'DELEGATED' + tags: + kuma.io/service: service-1 + """ + + When I visit the "/meshes/default/data-planes" URL + And I click the "$select-type" element + + Then the "$select-option" element exists 4 times + + When I click the "$select-delegated" element + Then the URL "/meshes/default/dataplanes/_overview" was requested with + """ + searchParams: + gateway: delegated + """ + And the "$item" element exists 1 time + And the "$item:nth-child(1)" element contains + | Value | + | fake-alarm-gateway_delegated-0 | + | Delegated gateway | + + Scenario: Filtering by "standard" + Given the environment + """ + KUMA_DATAPLANE_COUNT: 1 + """ + And the URL "/meshes/default/dataplanes/_overview" responds with + """ + body: + items: + - name: fake-system-proxy-0 + dataplane: + networking: + gateway: !!js/undefined + """ + + When I visit the "/meshes/default/data-planes" URL + And I click the "$select-type" element + + Then the "$select-option" element exists 4 times + + When I click the "$select-standard" element + Then the URL "/meshes/default/dataplanes/_overview" was requested with + """ + searchParams: + gateway: 'false' + """ + And the "$item" element exists 1 time + And the "$item:nth-child(1)" element contains + | Value | + | fake-system-proxy-0 | + | Standard proxy | diff --git a/features/mesh/dataplanes/NoSubscriptions.feature b/features/mesh/dataplanes/NoSubscriptions.feature index 2b21830a1..8980f5e5e 100644 --- a/features/mesh/dataplanes/NoSubscriptions.feature +++ b/features/mesh/dataplanes/NoSubscriptions.feature @@ -7,7 +7,20 @@ Feature: dataplanes / no-subscriptions And the environment """ KUMA_SUBSCRIPTION_COUNT: 0 + KUMA_DATAPLANEINBOUND_COUNT: 1 """ + And the URL "/meshes/default/dataplanes/dpp-1/_overview" responds with + """ + body: + name: fake-backend + mesh: fake-default + dataplane: + networking: + inbound: + - health: + ready: true + """ + When I visit the "/meshes/default/data-planes/dpp-1/overview" URL And the "$detail-view" element contains "dpp-1" And the "$overview-content" element contains "offline" diff --git a/features/mesh/gateways/GatewaySummary.feature b/features/mesh/gateways/GatewaySummary.feature deleted file mode 100644 index df5f662e7..000000000 --- a/features/mesh/gateways/GatewaySummary.feature +++ /dev/null @@ -1,59 +0,0 @@ -Feature: Gateway summary - Background: - Given the CSS selectors - | Alias | Selector | - | item | [data-testid='gateway-collection'] tbody tr | - | summary | [data-testid='summary'] | - | close-summary-button | $summary [data-testid^='close-button-'] | - And the environment - """ - KUMA_SUBSCRIPTION_COUNT: 2 - """ - And the URL "/meshes/default/dataplanes/_overview" responds with - """ - body: - items: - - name: test-gateway-1 - dataplaneInsight: - subscriptions: - - status: - lastUpdateTime: 2021-02-16T08:33:36.442044+01:00 - - status: - lastUpdateTime: 2021-02-18T08:33:36.442044+01:00 - """ - - Scenario: Clicking a row opens the summary - Given the environment - """ - KUMA_DATAPLANE_COUNT: 1 - """ - - When I visit the "/meshes/default/gateways" URL - And I click the "$item:nth-child(1) td:nth-child(2)" element - Then the "$summary" element exists - And the "$summary" element contains "test-gateway-1" - - When I click the "$close-summary-button" element - Then the "$summary" element doesn't exist - - When I navigate "back" - Then the "$summary" element exists - And the "$summary" element contains "test-gateway-1" - - When I navigate "forward" - Then the "$summary" element doesn't exist - - Scenario: Summary has expected content - When I visit the "/meshes/default/gateways" URL - And I click the "$item:nth-child(1) td:nth-child(2)" element - - Then the "$summary" element contains "Feb 18, 2021" - - Scenario: Summary URL goes to page with open dataplane summary - Given the environment - """ - KUMA_DATAPLANE_COUNT: 51 - """ - - When I visit the "/meshes/default/gateways/test-gateway-1?page=2&size=50" URL - Then the "$summary" element exists diff --git a/features/mesh/gateways/Index.feature b/features/mesh/gateways/Index.feature deleted file mode 100644 index d81c5e261..000000000 --- a/features/mesh/gateways/Index.feature +++ /dev/null @@ -1,123 +0,0 @@ -Feature: mesh / gateways / index - Background: - Given the CSS selectors - | Alias | Selector | - | select-type | [data-testid='k-select-input'] | - | select-option | .k-select-item | - | select-builtin | [data-testid='k-select-item-builtin'] | - | select-delegated | [data-testid='k-select-item-delegated'] | - | items | [data-testid='gateway-collection'] | - | item-header | $items th | - | item | $items tbody tr | - - Given the environment - """ - KUMA_DATAPLANE_COUNT: 2 - """ - And the URL "/meshes/default/dataplanes/_overview" responds with - """ - body: - items: - - name: fake-alarm-gateway-0 - dataplane: - networking: - gateway: - type: 'DELEGATED' - - name: fake-transmitter-gateway-0 - dataplane: - networking: - gateway: - type: 'BUILTIN' - """ - When I visit the "/meshes/default/gateways" URL - - Scenario: The Gateway listing table has the correct columns - Then the "$item-header" elements contain - | Value | - | Name | - | Type | - | Service | - | Zone | - | Certificate Info | - | Status | - | Warnings | - - Scenario: The Gateway listing has the expected content and UI elements - When I click the "$select-type" element - Then the "$select-option" element exists 3 times - Then the "$item" element exists 2 times - Then the "$item:nth-child(1)" element contains - | Value | - | fake-alarm-gateway-0 | - | DELEGATED | - Then the "$item:nth-child(2)" element contains - | Value | - | fake-transmitter-gateway-0 | - | BUILTIN | - - - Rule: The Gateway listing can filter gateways by type - - Scenario: No filtering - Then the URL "/meshes/default/dataplanes/_overview" was requested with - """ - searchParams: - gateway: "true" - """ - Scenario: Filtering by "builtin" - Given the environment - """ - KUMA_DATAPLANE_COUNT: 1 - """ - And the URL "/meshes/default/dataplanes/_overview" responds with - """ - body: - items: - - name: fake-transmitter-gateway-0 - dataplane: - networking: - gateway: - type: 'BUILTIN' - """ - When I click the "$select-type" element - Then the "$select-option" element exists 3 times - And I click the "$select-builtin" element - Then the URL "/meshes/default/dataplanes/_overview" was requested with - """ - searchParams: - gateway: builtin - """ - Then the "$item" element exists 1 time - Then the "$item:nth-child(1)" element contains - | Value | - | fake-transmitter-gateway-0 | - | BUILTIN | - - Scenario: Filtering by "delegated" - Given the environment - """ - KUMA_DATAPLANE_COUNT: 1 - """ - And the URL "/meshes/default/dataplanes/_overview" responds with - """ - body: - items: - - name: fake-alarm-gateway-0 - dataplane: - networking: - gateway: - type: 'DELEGATED' - """ - When I click the "$select-type" element - Then the "$select-option" element exists 3 times - And I click the "$select-delegated" element - Then the URL "/meshes/default/dataplanes/_overview" was requested with - """ - searchParams: - gateway: delegated - """ - Then the "$item" element exists 1 time - Then the "$item:nth-child(1)" element contains - | Value | - | fake-alarm-gateway-0 | - | DELEGATED | diff --git a/features/mesh/gateways/PageNavigation.feature b/features/mesh/gateways/PageNavigation.feature deleted file mode 100644 index 7b7f68dc5..000000000 --- a/features/mesh/gateways/PageNavigation.feature +++ /dev/null @@ -1,17 +0,0 @@ -Feature: Gateways: Page navigation - Scenario Outline: Tabs have expected URLs - - When I visit the "/meshes/default/gateways/firewal-gateway-1/overview" URL - - When I click the "[data-testid='-tab']" element - And the URL contains "" - Then the page title contains "" - - Examples: - | RouteName | Path | Title | - | gateway-detail-view | /gateways/firewal-gateway-1/overview | firewal-gateway-1 | - | gateway-policies-view | /gateways/firewal-gateway-1/policies | Policies | - | gateway-xds-config-view | /gateways/firewal-gateway-1/xds-config | XDS Configuration | - | gateway-stats-view | /gateways/firewal-gateway-1/stats | Stats | - | gateway-clusters-view | /gateways/firewal-gateway-1/clusters | Clusters | - | gateway-config-view | /gateways/firewal-gateway-1/config | YAML | diff --git a/src/app/gateways/components/GatewayDataplanePolicyList.vue b/src/app/data-planes/components/BuiltinGatewayPolicies.vue similarity index 97% rename from src/app/gateways/components/GatewayDataplanePolicyList.vue rename to src/app/data-planes/components/BuiltinGatewayPolicies.vue index 3d865b852..1725439b0 100644 --- a/src/app/gateways/components/GatewayDataplanePolicyList.vue +++ b/src/app/data-planes/components/BuiltinGatewayPolicies.vue @@ -1,5 +1,8 @@ <template> - <div class="policies-list"> + <div + class="policies-list" + data-testid="builtin-gateway-dataplane-policies" + > <div class="mesh-gateway-policy-list"> <h3 class="mb-2"> Gateway policies @@ -80,7 +83,7 @@ > {{ policy.type }}: - <router-link :to="policy.route">{{ policy.name }}</router-link> + <RouterLink :to="policy.route">{{ policy.name }}</RouterLink> </li> </ul> </template> diff --git a/src/app/data-planes/components/DataPlaneList.vue b/src/app/data-planes/components/DataPlaneList.vue index f921472b0..784ef8dfd 100644 --- a/src/app/data-planes/components/DataPlaneList.vue +++ b/src/app/data-planes/components/DataPlaneList.vue @@ -1,14 +1,12 @@ <template> <AppCollection - :empty-state-message="t('common.emptyState.message', { type: props.gateways ? 'Gateways' : 'Data Plane Proxies' })" - :empty-state-cta-to="t(`data-planes.href.docs.${props.gateways ? 'gateway' : 'data_plane_proxy'}`)" + :empty-state-message="t('common.emptyState.message', { type: 'Data Plane Proxies' })" + :empty-state-cta-to="t('data-planes.href.docs.data_plane_proxy')" :empty-state-cta-text="t('common.documentation')" :headers="[ { label: 'Name', key: 'name' }, - ...(props.gateways ? [{ label: 'Type', key: 'type' }] : []), - { label: 'Service', key: 'service' }, - ...(!props.gateways ? [{ label: 'Protocol', key: 'protocol' }] : []), - ...(isMultiZoneMode ? [{ label: 'Zone', key: 'zone' }] : []), + { label: 'Type', key: 'type' }, + { label: 'Services', key: 'services' }, { label: 'Certificate Info', key: 'certificate' }, { label: 'Status', key: 'status' }, { label: 'Warnings', key: 'warnings', hideLabel: true }, @@ -22,11 +20,14 @@ :is-selected-row="props.isSelectedRow" @change="emit('change', $event)" > - <template #toolbar> + <template + v-if="$slots.toolbar" + #toolbar + > <slot name="toolbar" /> </template> - <template #name="{ row: item }"> + <template #name="{ row: item }: { row: DataPlaneOverviewTableRow }"> <RouterLink :to="{ name: props.summaryRouteName, @@ -44,42 +45,38 @@ </RouterLink> </template> - <template #service="{ rowValue }"> - <RouterLink - v-if="rowValue.route" - :to="rowValue.route" - > - {{ rowValue.title }} - </RouterLink> - - <template v-else> - {{ rowValue.title }} + <template #services="{ row }: { row: DataPlaneOverviewTableRow }"> + <template v-if="row.services.length > 0"> + <KTruncate> + <KBadge + v-for="(service, index) in row.services" + :key="index" + > + <RouterLink + :to="{ + name: 'service-detail-view', + params: { + mesh: row.mesh, + service: service.value, + }, + }" + > + {{ service.value }} + </RouterLink> + </KBadge> + </KTruncate> </template> - </template> - <template #zone="{ rowValue }"> - <RouterLink - v-if="rowValue.route" - :to="rowValue.route" - > - {{ rowValue.title }} - </RouterLink> <template v-else> - {{ rowValue.title }} + {{ t('common.collection.none') }} </template> </template> - <template #status="{ rowValue }"> - <StatusBadge - v-if="rowValue" - :status="rowValue" - /> - <template v-else> - {{ t('common.collection.none') }} - </template> + <template #status="{ row }: { row: DataPlaneOverviewTableRow }"> + <StatusBadge :status="row.status" /> </template> - <template #warnings="{ row: item }"> + <template #warnings="{ row: item }: { row: DataPlaneOverviewTableRow }"> <KTooltip v-if="Object.values(item.warnings).some((item) => item)" > @@ -109,12 +106,12 @@ </template> </template> - <template #details="{ row }"> + <template #details="{ row }: { row: DataPlaneOverviewTableRow }"> <RouterLink class="details-link" data-testid="details-link" :to="{ - name: row.isGateway ? 'gateway-detail-view' : 'data-plane-detail-view', + name: 'data-plane-detail-view', params: { dataPlane: row.name, }, @@ -135,14 +132,11 @@ <script lang="ts" setup> import { KUI_ICON_SIZE_30 } from '@kong/design-tokens' import { ArrowRightIcon } from '@kong/icons' -import { type RouteLocationNamedRaw } from 'vue-router' -import { useCan } from '@/app/application' import AppCollection from '@/app/application/components/app-collection/AppCollection.vue' import StatusBadge from '@/app/common/StatusBadge.vue' import WarningIcon from '@/app/common/WarningIcon.vue' -import { DataPlaneOverviewParameters } from '@/types/api.d' -import type { DataPlaneOverview, StatusKeyword, Version } from '@/types/index.d' +import type { DataPlaneOverview, LabelValue, StatusKeyword, Version } from '@/types/index.d' import { useI18n } from '@/utilities' import { compatibilityKind, @@ -152,26 +146,17 @@ import { } from '@/utilities/dataplane' const { t, formatIsoDate } = useI18n() -const can = useCan() type DataPlaneOverviewTableRow = { type: string name: string - zone: { - title: string - route?: RouteLocationNamedRaw | undefined - } - service: { - title: string - route?: RouteLocationNamedRaw | undefined - } - protocol: string + mesh: string + services: LabelValue[] status: StatusKeyword warnings: { version_mismatch: boolean cert_expired: boolean } - isGateway: boolean certificate: string } @@ -186,58 +171,27 @@ const props = withDefaults(defineProps<{ pageSize: number items: DataPlaneOverview[] | undefined error: Error | undefined - gateways?: boolean isSelectedRow: ((row: any) => boolean) | null summaryRouteName: string + canUseZones: boolean }>(), { total: 0, - gateways: false, isSelectedRow: null, }) const emit = defineEmits<{ - (event: 'load-data', offset: number, params: DataPlaneOverviewParameters): void - (e: 'change', value: ChangeValue): void + (event: 'change', value: ChangeValue): void }>() -const isMultiZoneMode = can('use zones') - function transformToTableData(dataPlaneOverviews: DataPlaneOverview[]): DataPlaneOverviewTableRow[] { return dataPlaneOverviews.map((dataPlaneOverview) => { - const mesh = dataPlaneOverview.mesh - const name = dataPlaneOverview.name - const type = dataPlaneOverview.dataplane.networking.gateway?.type || 'STANDARD' - - // Handles our tag collections based on the dataplane type. - const importantDataPlaneTagLabels = [ - 'kuma.io/protocol', - 'kuma.io/service', - 'kuma.io/zone', - ] - const tags = dpTags(dataPlaneOverview.dataplane).filter((tag) => importantDataPlaneTagLabels.includes(tag.label)) - const service = tags.find((tag) => tag.label === 'kuma.io/service')?.value - const protocol = tags.find((tag) => tag.label === 'kuma.io/protocol')?.value - const zone = tags.find((tag) => tag.label === 'kuma.io/zone')?.value + const { mesh, name } = dataPlaneOverview + const type = dataPlaneOverview.dataplane.networking.gateway?.type + ? t(`data-planes.type.${dataPlaneOverview.dataplane.networking.gateway.type.toLowerCase()}`) + : t('data-planes.type.standard') - let serviceInsightRoute: RouteLocationNamedRaw | undefined - if (service !== undefined) { - serviceInsightRoute = { - name: 'service-detail-view', - params: { - mesh, - service, - }, - } - } - let zoneRoute: RouteLocationNamedRaw | undefined - if (zone !== undefined) { - zoneRoute = { - name: 'zone-cp-detail-view', - params: { - zone, - }, - } - } + const tags = dpTags(dataPlaneOverview.dataplane) + const services = tags.filter((tag) => tag.label === 'kuma.io/service') const { status } = getStatusAndReason(dataPlaneOverview.dataplane, dataPlaneOverview.dataplaneInsight) const subscriptions = dataPlaneOverview.dataplaneInsight?.subscriptions ?? [] @@ -271,15 +225,13 @@ function transformToTableData(dataPlaneOverviews: DataPlaneOverview[]): DataPlan const item: DataPlaneOverviewTableRow = { name, type, - zone: { title: zone ?? t('common.collection.none'), route: zoneRoute }, - service: { title: service ?? t('common.collection.none'), route: serviceInsightRoute }, - protocol: protocol ?? t('common.collection.none'), + mesh, + services, status, warnings: { version_mismatch: false, cert_expired: false, }, - isGateway: dataPlaneOverview.dataplane?.networking?.gateway !== undefined, certificate, } @@ -291,13 +243,14 @@ function transformToTableData(dataPlaneOverviews: DataPlaneOverview[]): DataPlan } } - if (isMultiZoneMode && summary.dpVersion) { - const zoneTag = tags.find(tag => tag.label === 'kuma.io/zone') + if (props.canUseZones && summary.dpVersion) { + const zoneTag = tags.find((tag) => tag.label === 'kuma.io/zone') if (zoneTag && typeof summary.version?.kumaDp.kumaCpCompatible === 'boolean' && !summary.version.kumaDp.kumaCpCompatible) { item.warnings.version_mismatch = true } } + const time = dataPlaneOverview.dataplaneInsight?.mTLS?.certificateExpirationTime if ( time && @@ -309,7 +262,6 @@ function transformToTableData(dataPlaneOverviews: DataPlaneOverview[]): DataPlan return item }) } - </script> <style lang="scss" scoped> diff --git a/src/app/data-planes/components/SidecarDataplanePolicyList.vue b/src/app/data-planes/components/StandardDataplanePolicies.vue similarity index 94% rename from src/app/data-planes/components/SidecarDataplanePolicyList.vue rename to src/app/data-planes/components/StandardDataplanePolicies.vue index cb21021c2..72a2c15c0 100644 --- a/src/app/data-planes/components/SidecarDataplanePolicyList.vue +++ b/src/app/data-planes/components/StandardDataplanePolicies.vue @@ -1,27 +1,29 @@ <template> - <h2 class="visually-hidden"> - Policies - </h2> - - <PolicyTypeEntryList - id="policies" - :policy-type-entries="policyTypeEntries" - data-testid="policy-list" - /> - - <div - v-if="ruleEntries.length > 0" - class="mt-2" - > - <h2 class="mb-2"> - Rules + <div data-testid="standard-dataplane-policies"> + <h2 class="visually-hidden"> + Policies </h2> - <RuleEntryList - id="rules" - :rule-entries="ruleEntries" - data-testid="rule-list" + <PolicyTypeEntryList + id="policies" + :policy-type-entries="policyTypeEntries" + data-testid="policy-list" /> + + <div + v-if="ruleEntries.length > 0" + class="mt-2" + > + <h2 class="mb-2"> + Rules + </h2> + + <RuleEntryList + id="rules" + :rule-entries="ruleEntries" + data-testid="rule-list" + /> + </div> </div> </template> diff --git a/src/app/data-planes/locales/en-us/index.yaml b/src/app/data-planes/locales/en-us/index.yaml index a17f87c09..059954a9f 100644 --- a/src/app/data-planes/locales/en-us/index.yaml +++ b/src/app/data-planes/locales/en-us/index.yaml @@ -42,3 +42,8 @@ data-planes: docs: data_plane_proxy: '{KUMA_DOCS_URL}/production/dp-config/dpp?{KUMA_UTM_QUERY_PARAMS}' gateway: '{KUMA_DOCS_URL}/explore/gateway?{KUMA_UTM_QUERY_PARAMS}' + type: + all: 'All' + standard: 'Standard proxy' + builtin: 'Built-in gateway' + delegated: 'Delegated gateway' diff --git a/src/app/data-planes/sources.ts b/src/app/data-planes/sources.ts index 5281ee5f0..ed02b7d11 100644 --- a/src/app/data-planes/sources.ts +++ b/src/app/data-planes/sources.ts @@ -1,8 +1,8 @@ import { DataSourceResponse } from '@/app/application/services/data-source/DataSourcePool' import { normalizeFilterFields } from '@/app/common/filter-bar/normalizeFilterFields' import type KumaApi from '@/services/kuma-api/KumaApi' -import type { PaginatedApiListResponse as CollectionResponse, ApiKindListResponse as KindCollectionResponse } from '@/types/api.d' -import type { DataPlane, DataPlaneOverview as DataplaneOverview, DataplaneRule, SidecarDataplane } from '@/types/index.d' +import type { PaginatedApiListResponse as CollectionResponse, DataPlaneOverviewParameters, ApiKindListResponse as KindCollectionResponse } from '@/types/api.d' +import type { DataPlane, DataPlaneOverview as DataplaneOverview, DataplaneRule, MeshGatewayDataplane, SidecarDataplane } from '@/types/index.d' type CollectionParams = { mesh: string @@ -27,7 +27,7 @@ type ServiceParams = { } type DataplaneTypeParams = { - type: 'all' | 'delegated' | 'builtin' + type: 'all' | 'standard' | 'delegated' | 'builtin' } type Closeable = { close: () => void } @@ -42,27 +42,13 @@ export type EnvoyDataSource = DataSourceResponse<object | string> export type SidecarDataplaneCollection = KindCollectionResponse<SidecarDataplane> export type SidecarDataplaneCollectionSource = DataSourceResponse<SidecarDataplaneCollection> +export type MeshGatewayDataplaneSource = DataSourceResponse<MeshGatewayDataplane> + export type DataplaneRulesCollection = CollectionResponse<DataplaneRule> export type DataplaneRulesCollectionSource = DataSourceResponse<DataplaneRulesCollection> export const sources = (api: KumaApi) => { return { - '/meshes/:mesh/dataplanes': async (params: CollectionParams & PaginationParams, source: Closeable) => { - source.close() - - const { mesh, size } = params - const offset = params.size * (params.page - 1) - const gateway = 'false' - const filterParams = Object.fromEntries(normalizeFilterFields(JSON.parse(params.search || '[]'))) - - return api.getAllDataplaneOverviewsFromMesh({ mesh }, { - ...filterParams, - gateway, - offset, - size, - }) - }, - '/meshes/:mesh/dataplanes/:name': (params: DetailParams, source: Closeable) => { source.close() @@ -79,7 +65,7 @@ export const sources = (api: KumaApi) => { return api.getDataplaneData({ mesh, dppName: name, dataPath }) }, - '/meshes/:mesh/dataplanes/:name/sidecar-dataplanes-policies': (params: DetailParams, source: Closeable) => { + '/meshes/:mesh/dataplanes/:name/sidecar-dataplane-policies': (params: DetailParams, source: Closeable) => { source.close() const { mesh, name } = params @@ -95,6 +81,14 @@ export const sources = (api: KumaApi) => { return api.getDataplaneRules({ mesh, name }) }, + '/meshes/:mesh/dataplanes/:name/gateway-dataplane-policies': (params: DetailParams, source: Closeable) => { + source.close() + + const { mesh, name } = params + + return api.getMeshGatewayDataplane({ mesh, name }) + }, + '/meshes/:mesh/dataplane-overviews/:name': (params: DetailParams, source: Closeable) => { source.close() @@ -103,11 +97,34 @@ export const sources = (api: KumaApi) => { return api.getDataplaneOverviewFromMesh({ mesh, name }) }, + '/meshes/:mesh/dataplanes/of/:type': async (params: CollectionParams & DataplaneTypeParams & PaginationParams, source: Closeable) => { + source.close() + + const { mesh, size, type } = params + const offset = size * (params.page - 1) + + const filterParams = Object.fromEntries(normalizeFilterFields(JSON.parse(params.search || '[]'))) + + const gatewayParams: DataPlaneOverviewParameters = {} + if (type === 'standard') { + gatewayParams.gateway = 'false' + } else if (type !== 'all') { + gatewayParams.gateway = type + } + + return api.getAllDataplaneOverviewsFromMesh({ mesh }, { + ...filterParams, + ...gatewayParams, + offset, + size, + }) + }, + '/meshes/:mesh/dataplanes/for/:service/of/:type': async (params: CollectionParams & ServiceParams & PaginationParams & DataplaneTypeParams, source: Closeable) => { source.close() - const { mesh, size } = params - const offset = params.size * (params.page - 1) + const { mesh, size, type } = params + const offset = size * (params.page - 1) // here 'all' means both proxies/sidecars and gateways this currently fits // our usecases but we should probably include `gateway | sidecar` or @@ -118,7 +135,13 @@ export const sources = (api: KumaApi) => { } filterParams.tag = filterParams.tag.filter((item) => !item.startsWith('kuma.io/service:')) filterParams.tag.push(`kuma.io/service:${params.service}`) - const gatewayParams = params.type !== 'all' ? { gateway: params.type } : {} + + const gatewayParams: DataPlaneOverviewParameters = {} + if (type === 'standard') { + gatewayParams.gateway = 'false' + } else if (type !== 'all') { + gatewayParams.gateway = type + } return api.getAllDataplaneOverviewsFromMesh({ mesh }, { ...filterParams, diff --git a/src/app/data-planes/views/DataPlaneListView.vue b/src/app/data-planes/views/DataPlaneListView.vue index 92cda27ac..b888e7cf8 100644 --- a/src/app/data-planes/views/DataPlaneListView.vue +++ b/src/app/data-planes/views/DataPlaneListView.vue @@ -5,12 +5,13 @@ > <RouteView v-if="me" - v-slot="{ route, t }" + v-slot="{ can, route, t }" name="data-plane-list-view" :params="{ page: 1, - size: 50, + size: me.pageSize, query: '', + dataplaneType: 'all', s: '', mesh: '', dataPlane: '', @@ -18,7 +19,7 @@ > <DataSource v-slot="{data, error}: DataPlaneCollectionSource" - :src="`/meshes/${route.params.mesh}/dataplanes?page=${route.params.page}&size=${route.params.size}&search=${route.params.s}`" + :src="`/meshes/${route.params.mesh}/dataplanes/of/${route.params.dataplaneType}?page=${route.params.page}&size=${route.params.size}&search=${route.params.s}`" > <AppView> <template #title> @@ -48,24 +49,42 @@ :error="error" :is-selected-row="(row) => row.name === route.params.dataPlane" summary-route-name="data-plane-summary-view" + :can-use-zones="can('use zones')" @change="route.update" > <template #toolbar> <FilterBar class="data-plane-proxy-filter" - :placeholder="`tag: 'kuma.io/protocol: http'`" + :placeholder="`tag: 'kuma.io/service: backend'`" :query="route.params.query" :fields="{ name: { description: 'filter by name or parts of a name' }, + protocol: { description: 'filter by “kuma.io/protocol” value' }, service: { description: 'filter by “kuma.io/service” value' }, tag: { description: 'filter by tags (e.g. “tag: version:2”)' }, zone: { description: 'filter by “kuma.io/zone” value' }, }" - @fields-change="(val) => route.update({ - query: val.query, - s: val.query.length > 0 ? JSON.stringify(val.fields) : '', + @fields-change="route.update({ + query: $event.query, + s: $event.query.length > 0 ? JSON.stringify($event.fields) : '', })" /> + + <KSelect + label="Type" + :overlay-label="true" + :items="['all', 'standard', 'builtin', 'delegated'].map((value) => ({ + value, + label: t(`data-planes.type.${value}`), + selected: value === route.params.dataplaneType, + }))" + appearance="select" + @selected="route.update({ dataplaneType: String($event.value) })" + > + <template #item-template="{ item: value }"> + {{ value.label }} + </template> + </KSelect> </template> </DataPlaneList> </template> @@ -107,7 +126,6 @@ import ErrorBlock from '@/app/common/ErrorBlock.vue' import FilterBar from '@/app/common/filter-bar/FilterBar.vue' import SummaryView from '@/app/common/SummaryView.vue' import type { MeSource } from '@/app/me/sources' - </script> <style lang="scss" scoped> diff --git a/src/app/data-planes/views/DataPlanePoliciesView.vue b/src/app/data-planes/views/DataPlanePoliciesView.vue index 8ceb01a7b..be38857d6 100644 --- a/src/app/data-planes/views/DataPlanePoliciesView.vue +++ b/src/app/data-planes/views/DataPlanePoliciesView.vue @@ -19,17 +19,14 @@ <KCard> <template #body> - <DataSource - v-slot="{ data: policyTypesData, error: policyTypesError }: PolicyTypeCollectionSource" - :src="`/*/policy-types`" - > + <template v-if="props.data.dataplane?.networking?.gateway?.type === 'BUILTIN'"> <DataSource - v-slot="{ data, error }: SidecarDataplaneCollectionSource" - :src="`/meshes/${route.params.mesh}/dataplanes/${route.params.dataPlane}/sidecar-dataplanes-policies`" + v-slot="{ data: policyTypesData, error: policyTypesError }: PolicyTypeCollectionSource" + :src="`/*/policy-types`" > <DataSource - v-slot="{ data: rulesData, error: rulesError }: DataplaneRulesCollectionSource" - :src="`/meshes/${route.params.mesh}/dataplanes/${route.params.dataPlane}/rules`" + v-slot="{ data: gatewayDataplane, error }: MeshGatewayDataplaneSource" + :src="`/meshes/${route.params.mesh}/dataplanes/${route.params.dataPlane}/gateway-dataplane-policies`" > <ErrorBlock v-if="policyTypesError" @@ -41,22 +38,57 @@ :error="error" /> - <ErrorBlock - v-else-if="rulesError" - :error="rulesError" - /> + <LoadingBlock v-else-if="gatewayDataplane === undefined || policyTypesData === undefined" /> - <LoadingBlock v-else-if="policyTypesData === undefined || data === undefined || rulesData === undefined" /> - - <SidecarDataplanePolicyList + <BuiltinGatewayPolicies v-else :policy-types-by-name="policyTypesData.policies.reduce((obj, policyType) => Object.assign(obj, { [policyType.name]: policyType }), {})" - :sidecar-dataplanes="data.items" - :rules="rulesData.items" + :gateway-dataplane="gatewayDataplane" /> </DataSource> </DataSource> - </DataSource> + </template> + + <template v-else> + <DataSource + v-slot="{ data: policyTypesData, error: policyTypesError }: PolicyTypeCollectionSource" + :src="`/*/policy-types`" + > + <DataSource + v-slot="{ data: sidecarDataplaneData, error }: SidecarDataplaneCollectionSource" + :src="`/meshes/${route.params.mesh}/dataplanes/${route.params.dataPlane}/sidecar-dataplane-policies`" + > + <DataSource + v-slot="{ data: rulesData, error: rulesError }: DataplaneRulesCollectionSource" + :src="`/meshes/${route.params.mesh}/dataplanes/${route.params.dataPlane}/rules`" + > + <ErrorBlock + v-if="policyTypesError" + :error="policyTypesError" + /> + + <ErrorBlock + v-else-if="error" + :error="error" + /> + + <ErrorBlock + v-else-if="rulesError" + :error="rulesError" + /> + + <LoadingBlock v-else-if="policyTypesData === undefined || sidecarDataplaneData === undefined || rulesData === undefined" /> + + <StandardDataplanePolicies + v-else + :policy-types-by-name="policyTypesData.policies.reduce((obj, policyType) => Object.assign(obj, { [policyType.name]: policyType }), {})" + :sidecar-dataplanes="sidecarDataplaneData.items" + :rules="rulesData.items" + /> + </DataSource> + </DataSource> + </DataSource> + </template> </template> </KCard> </AppView> @@ -64,9 +96,15 @@ </template> <script lang="ts" setup> -import SidecarDataplanePolicyList from '../components/SidecarDataplanePolicyList.vue' -import { DataplaneRulesCollectionSource, SidecarDataplaneCollectionSource } from '../sources' +import BuiltinGatewayPolicies from '../components/BuiltinGatewayPolicies.vue' +import StandardDataplanePolicies from '../components/StandardDataplanePolicies.vue' +import { DataplaneRulesCollectionSource, MeshGatewayDataplaneSource, SidecarDataplaneCollectionSource } from '../sources' import ErrorBlock from '@/app/common/ErrorBlock.vue' import LoadingBlock from '@/app/common/LoadingBlock.vue' import { PolicyTypeCollectionSource } from '@/app/policies/sources' +import type { DataPlaneOverview } from '@/types/index.d' + +const props = defineProps<{ + data: DataPlaneOverview +}>() </script> diff --git a/src/app/data-planes/views/DataPlaneSummaryView.vue b/src/app/data-planes/views/DataPlaneSummaryView.vue index a8272ec88..46ef10bcf 100644 --- a/src/app/data-planes/views/DataPlaneSummaryView.vue +++ b/src/app/data-planes/views/DataPlaneSummaryView.vue @@ -11,7 +11,7 @@ <h2 class="summary-title"> <RouterLink :to="{ - name: isGateway ? 'gateway-detail-view' : 'data-plane-detail-view', + name: 'data-plane-detail-view', params: { dataPlane: props.name, }, @@ -27,10 +27,10 @@ </template> <EmptyBlock v-if="props.dataplaneOverview === undefined"> - {{ t('common.collection.summary.empty_title', { type: isGateway ? 'Gateway' : 'Dataplane' }) }} + {{ t('common.collection.summary.empty_title', { type: 'Data Plane Proxy' }) }} <template #message> - <p>{{ t('common.collection.summary.empty_message', { type: isGateway ? 'Gateway' : 'Dataplane' }) }}</p> + <p>{{ t('common.collection.summary.empty_message', { type: 'Data Plane Proxy' }) }}</p> </template> </EmptyBlock> @@ -52,7 +52,6 @@ </template> <script lang="ts" setup> -import { computed } from 'vue' import { useRoute } from 'vue-router' import EmptyBlock from '@/app/common/EmptyBlock.vue' @@ -69,8 +68,6 @@ const props = withDefaults(defineProps<{ }>(), { dataplaneOverview: undefined, }) - -const isGateway = computed(() => props.dataplaneOverview?.dataplane?.networking?.gateway !== undefined) </script> <style lang="scss" scoped> diff --git a/src/app/gateways/index.ts b/src/app/gateways/index.ts deleted file mode 100644 index 5281dcae0..000000000 --- a/src/app/gateways/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { sources } from './sources' -import type { ServiceDefinition } from '@/services/utils' -import { token } from '@/services/utils' -export * from './routes' - -type Token = ReturnType<typeof token> - -export const services = (app: Record<string, Token>): ServiceDefinition[] => { - return [ - [token('gateway.sources'), { - service: sources, - arguments: [ - app.api, - ], - labels: [ - app.sources, - ], - }], - ] -} diff --git a/src/app/gateways/locales/en-us/index.yaml b/src/app/gateways/locales/en-us/index.yaml deleted file mode 100644 index b9f5e8aec..000000000 --- a/src/app/gateways/locales/en-us/index.yaml +++ /dev/null @@ -1,14 +0,0 @@ -gateways: - routes: - item: - title: "{name}" - breadcrumbs: Gateways - navigation: - gateway-detail-view: 'Overview' - gateway-policies-view: 'Policies' - gateway-xds-config-view: 'XDS Configuration' - gateway-stats-view: 'Stats' - gateway-clusters-view: 'Clusters' - gateway-config-view: 'YAML' - items: - title: Gateways diff --git a/src/app/gateways/routes.ts b/src/app/gateways/routes.ts deleted file mode 100644 index 9150b1927..000000000 --- a/src/app/gateways/routes.ts +++ /dev/null @@ -1,70 +0,0 @@ -import type { RouteRecordRaw } from 'vue-router' -export const routes = () => { - const item = (): RouteRecordRaw[] => { - return [ - { - path: 'gateways/:dataPlane', - name: 'gateway-detail-tabs-view', - component: () => import('@/app/data-planes/views/DataPlaneDetailTabsView.vue'), - props: { - isGatewayView: true, - }, - children: [ - { - path: 'overview', - name: 'gateway-detail-view', - component: () => import('@/app/data-planes/views/DataPlaneDetailView.vue'), - }, - { - path: 'policies', - name: 'gateway-policies-view', - component: () => import('@/app/gateways/views/GatewayPoliciesView.vue'), - }, - { - path: 'xds-config', - name: 'gateway-xds-config-view', - component: () => import('@/app/data-planes/views/DataPlaneXdsConfigView.vue'), - }, - { - path: 'stats', - name: 'gateway-stats-view', - component: () => import('@/app/data-planes/views/DataPlaneStatsView.vue'), - }, - { - path: 'clusters', - name: 'gateway-clusters-view', - component: () => import('@/app/data-planes/views/DataPlaneClustersView.vue'), - }, - { - path: 'config', - name: 'gateway-config-view', - component: () => import('@/app/data-planes/views/DataPlaneConfigView.vue'), - }, - ], - }, - ] - } - - return { - items: (): RouteRecordRaw[] => { - return [ - { - path: 'gateways', - name: 'gateway-list-view', - meta: { - module: 'gateways', - }, - component: () => import('@/app/gateways/views/GatewayListView.vue'), - children: [ - { - path: ':dataPlane', - name: 'gateway-summary-view', - component: () => import('@/app/data-planes/views/DataPlaneSummaryView.vue'), - }, - ], - }, - ] - }, - item, - } -} diff --git a/src/app/gateways/sources.ts b/src/app/gateways/sources.ts deleted file mode 100644 index 6593bbae9..000000000 --- a/src/app/gateways/sources.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { DataSourceResponse } from '@/app/application/services/data-source/DataSourcePool' -import { normalizeFilterFields } from '@/app/common/filter-bar/normalizeFilterFields' -import type KumaApi from '@/services/kuma-api/KumaApi' -import type { - PaginatedApiListResponse as CollectionResponse, -} from '@/types/api.d' -import type { - DataPlaneOverview as DataplaneOverview, - MeshGatewayDataplane, -} from '@/types/index.d' -type DataplaneTypeParams = { - type: 'all' | 'delegated' | 'builtin' -} - -type Closeable = { close: () => void } -type PaginationParams = { - size: number - page: number - search: string -} -type CollectionParams = { - mesh: string -} - -type DetailParams = CollectionParams & { - name: string -} - -export type GatewayCollection = CollectionResponse<DataplaneOverview> -export type GatewayCollectionSource = DataSourceResponse<GatewayCollection> - -export type MeshGatewayDataplaneSource = DataSourceResponse<MeshGatewayDataplane> - -export const sources = (api: KumaApi) => { - return { - '/meshes/:mesh/gateways': async (params: CollectionParams & PaginationParams, source: Closeable) => { - source.close() - const offset = params.size * (params.page - 1) - return api.getAllDataplaneOverviewsFromMesh({ - mesh: params.mesh, - }, { - gateway: 'true', - offset, - size: params.size, - }) - }, - '/meshes/:mesh/gateways/of/:type': async (params: CollectionParams & DataplaneTypeParams & PaginationParams, source: Closeable) => { - source.close() - const offset = params.size * (params.page - 1) - return api.getAllDataplaneOverviewsFromMesh({ - mesh: params.mesh, - }, { - ...Object.fromEntries(normalizeFilterFields(JSON.parse(params.search || '[]'))), - gateway: params.type === 'all' ? 'true' : params.type, - offset, - size: params.size, - }) - }, - - '/meshes/:mesh/gateways/:name/policies': (params: DetailParams, source: Closeable) => { - source.close() - - const { mesh, name } = params - - return api.getMeshGatewayDataplane({ mesh, name }) - }, - } -} diff --git a/src/app/gateways/views/GatewayListView.vue b/src/app/gateways/views/GatewayListView.vue deleted file mode 100644 index 5b3f7170c..000000000 --- a/src/app/gateways/views/GatewayListView.vue +++ /dev/null @@ -1,157 +0,0 @@ -<template> - <DataSource - v-slot="{ data: me }: MeSource" - src="/me" - > - <RouteView - v-if="me" - v-slot="{ route, can, t }" - name="gateway-list-view" - :params="{ - page: 1, - size: me.pageSize, - gatewayType: 'all', - query: '', - s: '', - mesh: '', - dataPlane: '', - }" - > - <DataSource - v-slot="{data, error}: GatewayCollectionSource" - :src="`/meshes/${route.params.mesh}/gateways/of/${route.params.gatewayType}?page=${route.params.page}&size=${route.params.size}&search=${route.params.s}`" - > - <AppView> - <template #title> - <h2> - <RouteTitle - :title="t('gateways.routes.items.title')" - :render="true" - /> - </h2> - </template> - - <KCard> - <template #body> - <ErrorBlock - v-if="error !== undefined" - :error="error" - /> - - <DataPlaneList - v-else - data-testid="gateway-collection" - class="gateway-collection" - :page-number="parseInt(route.params.page)" - :page-size="parseInt(route.params.size)" - :total="data?.total" - :items="data?.items" - :error="error" - :gateways="true" - :is-selected-row="(row) => row.name === route.params.dataPlane" - summary-route-name="gateway-summary-view" - @change="({page, size}) => { - route.update({ - page: String(page), - size: String(size), - }) - }" - > - <template #toolbar> - <FilterBar - class="data-plane-proxy-filter" - :placeholder="`tag: 'kuma.io/protocol: http'`" - :query="route.params.query" - :fields="{ - name: { description: 'filter by name or parts of a name' }, - service: { description: 'filter by “kuma.io/service” value' }, - tag: { description: 'filter by tags (e.g. “tag: version:2”)' }, - ...( can('use zones') ? { - zone: { description: 'filter by “kuma.io/zone” value' }, - } : {}), - }" - @fields-change="(val) => route.update({ - query: val.query, - s: val.query.length > 0 ? JSON.stringify(val.fields) : '', - })" - /> - <KSelect - label="Type" - :overlay-label="true" - :items="[ - { - label: 'All', - value: 'all', - }, - { - label: 'Builtin', - value: 'builtin', - }, - { - label: 'Delegated', - value: 'delegated', - }, - ].map(item => ({ - ...item, - selected: item.value === route.params.gatewayType, - }))" - appearance="select" - @selected="(item: SelectItem) => route.update({ - gatewayType: String(item.value), - })" - > - <template #item-template="{ item }"> - {{ item.label }} - </template> - </KSelect> - </template> - </DataPlaneList> - </template> - </KCard> - - <RouterView - v-if="route.params.dataPlane" - v-slot="child" - > - <SummaryView - @close="route.replace({ - name: 'gateway-list-view', - params: { - mesh: route.params.mesh, - }, - query: { - page: route.params.page, - size: route.params.size, - }, - })" - > - <component - :is="child.Component" - :name="route.params.dataPlane" - :dataplane-overview="data?.items.find((item) => item.name === route.params.dataPlane)" - /> - </SummaryView> - </RouterView> - </AppView> - </DataSource> - </RouteView> - </DataSource> -</template> - -<script lang="ts" setup> -import type { GatewayCollectionSource } from '../sources' -import ErrorBlock from '@/app/common/ErrorBlock.vue' -import FilterBar from '@/app/common/filter-bar/FilterBar.vue' -import SummaryView from '@/app/common/SummaryView.vue' -import DataPlaneList from '@/app/data-planes/components/DataPlaneList.vue' -import type { MeSource } from '@/app/me/sources' -import type { SelectItem } from '@kong/kongponents' - -</script> - -<style lang="scss" scoped> -.data-plane-proxy-filter { - flex-basis: 350px; - flex-grow: 1; -} -</style> diff --git a/src/app/gateways/views/GatewayPoliciesView.vue b/src/app/gateways/views/GatewayPoliciesView.vue deleted file mode 100644 index 63235a56f..000000000 --- a/src/app/gateways/views/GatewayPoliciesView.vue +++ /dev/null @@ -1,111 +0,0 @@ -<template> - <RouteView - v-slot="{ route, t }" - name="gateway-policies-view" - :params="{ - mesh: '', - dataPlane: '', - }" - > - <AppView> - <template #title> - <h2> - <RouteTitle - :title="t('gateways.routes.item.navigation.gateway-policies-view')" - :render="true" - /> - </h2> - </template> - - <KCard> - <template #body> - <template v-if="props.data.dataplane?.networking?.gateway?.type === 'BUILTIN'"> - <DataSource - v-slot="{ data: policyTypesData, error: policyTypesError }: PolicyTypeCollectionSource" - :src="`/*/policy-types`" - > - <DataSource - v-slot="{ data: gatewayDataplane, error }: MeshGatewayDataplaneSource" - :src="`/meshes/${route.params.mesh}/gateways/${route.params.dataPlane}/policies`" - > - <ErrorBlock - v-if="policyTypesError" - :error="policyTypesError" - /> - - <ErrorBlock - v-else-if="error" - :error="error" - /> - - <LoadingBlock v-else-if="gatewayDataplane === undefined || policyTypesData === undefined" /> - - <GatewayDataplanePolicyList - v-else - :policy-types-by-name="policyTypesData.policies.reduce((obj, policyType) => Object.assign(obj, { [policyType.name]: policyType }), {})" - :gateway-dataplane="gatewayDataplane" - /> - </DataSource> - </DataSource> - </template> - - <template v-else> - <DataSource - v-slot="{ data: policyTypesData, error: policyTypesError }: PolicyTypeCollectionSource" - :src="`/*/policy-types`" - > - <DataSource - v-slot="{ data: sidecarDataplane, error }: SidecarDataplaneCollectionSource" - :src="`/meshes/${route.params.mesh}/dataplanes/${route.params.dataPlane}/sidecar-dataplanes-policies`" - > - <DataSource - v-slot="{ data: rulesData, error: rulesError }: DataplaneRulesCollectionSource" - :src="`/meshes/${route.params.mesh}/dataplanes/${route.params.dataPlane}/rules`" - > - <ErrorBlock - v-if="policyTypesError" - :error="policyTypesError" - /> - - <ErrorBlock - v-else-if="error" - :error="error" - /> - - <ErrorBlock - v-else-if="rulesError" - :error="rulesError" - /> - - <LoadingBlock v-else-if="policyTypesData === undefined || sidecarDataplane === undefined || rulesData === undefined" /> - - <SidecarDataplanePolicyList - v-else - :policy-types-by-name="policyTypesData.policies.reduce((obj, policyType) => Object.assign(obj, { [policyType.name]: policyType }), {})" - :sidecar-dataplanes="sidecarDataplane.items" - :rules="rulesData.items" - /> - </DataSource> - </DataSource> - </DataSource> - </template> - </template> - </KCard> - </AppView> - </RouteView> -</template> - -<script lang="ts" setup> -import GatewayDataplanePolicyList from '../components/GatewayDataplanePolicyList.vue' -import { MeshGatewayDataplaneSource } from '../sources' -import ErrorBlock from '@/app/common/ErrorBlock.vue' -import LoadingBlock from '@/app/common/LoadingBlock.vue' -import SidecarDataplanePolicyList from '@/app/data-planes/components/SidecarDataplanePolicyList.vue' -import { DataplaneRulesCollectionSource, SidecarDataplaneCollectionSource } from '@/app/data-planes/sources' -import { PolicyTypeCollectionSource } from '@/app/policies/sources' -import type { DataPlaneOverview } from '@/types/index.d' - -const props = defineProps<{ - data: DataPlaneOverview -}>() -</script> diff --git a/src/app/meshes/index.ts b/src/app/meshes/index.ts index 3a22ec7b8..991434942 100644 --- a/src/app/meshes/index.ts +++ b/src/app/meshes/index.ts @@ -2,7 +2,6 @@ import { routes } from './routes' import type { SplitRouteRecordRaw } from './routes' import { sources } from './sources' import { routes as dataplaneRoutes, services as dataplanes } from '@/app/data-planes' -import { routes as gatewayRoutes, services as gateways } from '@/app/gateways' import { routes as policyRoutes, services as policies } from '@/app/policies' import { routes as serviceRoutes, services as servicesModule } from '@/app/services' import type { ServiceDefinition } from '@/services/utils' @@ -12,7 +11,6 @@ type Token = ReturnType<typeof token> const $ = { dataplaneRoutes: token<SplitRouteRecordRaw[]>('kuma.dataplane.routes'), - gatewayRoutes: token<SplitRouteRecordRaw[]>('kuma.gateway.routes'), serviceRoutes: token<SplitRouteRecordRaw[]>('kuma.service.routes'), policyRoutes: token<SplitRouteRecordRaw[]>('kuma.policy.routes'), } @@ -22,7 +20,6 @@ export const services = (app: Record<string, Token>): ServiceDefinition[] => { service: routes, arguments: [ $.serviceRoutes, - $.gatewayRoutes, $.dataplaneRoutes, $.policyRoutes, ], @@ -33,9 +30,6 @@ export const services = (app: Record<string, Token>): ServiceDefinition[] => { [$.dataplaneRoutes, { service: dataplaneRoutes, }], - [$.gatewayRoutes, { - service: gatewayRoutes, - }], [$.serviceRoutes, { service: serviceRoutes, }], @@ -54,7 +48,6 @@ export const services = (app: Record<string, Token>): ServiceDefinition[] => { }], ...servicesModule(app), ...dataplanes(app), - ...gateways(app), ...policies(app), ] } diff --git a/src/app/meshes/locales/en-us/index.yaml b/src/app/meshes/locales/en-us/index.yaml index dd8ae09a7..7123df60a 100644 --- a/src/app/meshes/locales/en-us/index.yaml +++ b/src/app/meshes/locales/en-us/index.yaml @@ -21,7 +21,6 @@ meshes: mesh-config-view: YAML service-list-view: Services data-plane-list-view: Data Plane Proxies - gateway-list-view: Gateways policy-list-index-view: Policies overview: 'Overview' items: diff --git a/src/app/meshes/routes.ts b/src/app/meshes/routes.ts index 61430fd79..c0b3356b8 100644 --- a/src/app/meshes/routes.ts +++ b/src/app/meshes/routes.ts @@ -7,7 +7,6 @@ export type SplitRouteRecordRaw = { export const routes = ( services: SplitRouteRecordRaw, - gateways: SplitRouteRecordRaw, dataplanes: SplitRouteRecordRaw, policies: SplitRouteRecordRaw, ): RouteRecordRaw[] => { @@ -55,13 +54,11 @@ export const routes = ( component: () => import('@/app/meshes/views/MeshConfigView.vue'), }, ...services.items(), - ...gateways.items(), ...dataplanes.items(), ...policies.items(), ], }, ...services.item(), - ...gateways.item(), ...dataplanes.item(), ...policies.item(), ], diff --git a/src/app/services/views/ServiceDataPlaneProxiesView.vue b/src/app/services/views/ServiceDataPlaneProxiesView.vue index 744844287..e57f150ec 100644 --- a/src/app/services/views/ServiceDataPlaneProxiesView.vue +++ b/src/app/services/views/ServiceDataPlaneProxiesView.vue @@ -5,16 +5,16 @@ > <RouteView v-if="me" - v-slot="{ route, t }" + v-slot="{ can, route, t }" name="service-data-plane-proxies-view" :params="{ page: 1, size: me.pageSize, query: '', + dataplaneType: 'all', s: '', mesh: '', service: '', - gatewayType: '', dataPlane: '', }" > @@ -30,103 +30,89 @@ <DataSource v-slot="{ data: dataplanesData, error: dataplanesError }: DataPlaneCollectionSource" - :src="`/meshes/${route.params.mesh}/dataplanes/for/${route.params.service}/of/${'all'}?page=${route.params.page}&size=${route.params.size}&search=${route.params.s}`" + :src="`/meshes/${route.params.mesh}/dataplanes/for/${route.params.service}/of/${route.params.dataplaneType}?page=${route.params.page}&size=${route.params.size}&search=${route.params.s}`" > - <template - v-for="gateways in [dataplanesData?.items?.[0]?.dataplane?.networking?.gateway !== undefined]" - :key="gateways" - > - <KCard> - <template #body> - <DataPlaneList - data-testid="data-plane-collection" - class="data-plane-collection" - :page-number="parseInt(route.params.page)" - :page-size="parseInt(route.params.size)" - :total="dataplanesData?.total" - :items="dataplanesData?.items" - :error="dataplanesError" - :gateways="gateways" - :is-selected-row="(row) => row.name === route.params.dataPlane" - summary-route-name="service-data-plane-summary-view" - @change="route.update" - > - <template #toolbar> - <FilterBar - class="data-plane-proxy-filter" - :placeholder="`tag: 'kuma.io/protocol: http'`" - :query="route.params.query" - :fields="{ - name: { description: 'filter by name or parts of a name' }, - protocol: { description: 'filter by “kuma.io/protocol” value' }, - tag: { description: 'filter by tags (e.g. “tag: version:2”)' }, - zone: { description: 'filter by “kuma.io/zone” value' }, - }" - @fields-change="(val) => route.update({ - query: val.query, - s: val.query.length > 0 ? JSON.stringify(val.fields) : '', - })" - /> + <KCard> + <template #body> + <ErrorBlock + v-if="dataplanesError !== undefined" + :error="dataplanesError" + /> + + <DataPlaneList + v-else + data-testid="data-plane-collection" + class="data-plane-collection" + :page-number="parseInt(route.params.page)" + :page-size="parseInt(route.params.size)" + :total="dataplanesData?.total" + :items="dataplanesData?.items" + :error="dataplanesError" + :is-selected-row="(row) => row.name === route.params.dataPlane" + summary-route-name="service-data-plane-summary-view" + :can-use-zones="can('use zones')" + @change="route.update" + > + <template #toolbar> + <FilterBar + class="data-plane-proxy-filter" + :placeholder="`tag: 'kuma.io/protocol: http'`" + :query="route.params.query" + :fields="{ + name: { description: 'filter by name or parts of a name' }, + protocol: { description: 'filter by “kuma.io/protocol” value' }, + tag: { description: 'filter by tags (e.g. “tag: version:2”)' }, + zone: { description: 'filter by “kuma.io/zone” value' }, + }" + @fields-change="route.update({ + query: $event.query, + s: $event.query.length > 0 ? JSON.stringify($event.fields) : '', + })" + /> - <KSelect - v-if="gateways" - label="Type" - :overlay-label="true" - :items="[ - { - label: 'All', - value: 'all', - }, - { - label: 'Builtin', - value: 'builtin', - }, - { - label: 'Delegated', - value: 'delegated', - }, - ].map(item => ({ - ...item, - selected: item.value === route.params.gatewayType, - }))" - appearance="select" - @selected="(item: SelectItem) => route.update({ - gatewayType: String(item.value), - })" - > - <template #item-template="{ item: value }"> - {{ value.label }} - </template> - </KSelect> - </template> - </DataPlaneList> - </template> - </KCard> + <KSelect + label="Type" + :overlay-label="true" + :items="['all', 'standard', 'builtin', 'delegated'].map((value) => ({ + value, + label: t(`data-planes.type.${value}`), + selected: value === route.params.dataplaneType, + }))" + appearance="select" + @selected="route.update({ dataplaneType: String($event.value) })" + > + <template #item-template="{ item: value }"> + {{ value.label }} + </template> + </KSelect> + </template> + </DataPlaneList> + </template> + </KCard> - <RouterView - v-if="route.params.dataPlane" - v-slot="child" + <RouterView + v-if="route.params.dataPlane" + v-slot="child" + > + <SummaryView + @close="route.replace({ + name: 'service-data-plane-proxies-view', + params: { + mesh: route.params.mesh, + }, + query: { + page: route.params.page, + size: route.params.size, + }, + })" > - <SummaryView - @close="route.replace({ - name: 'service-data-plane-proxies-view', - params: { - mesh: route.params.mesh, - }, - query: { - page: route.params.page, - size: route.params.size, - }, - })" - > - <component - :is="child.Component" - :name="route.params.dataPlane" - :dataplane-overview="dataplanesData?.items.find((item) => item.name === route.params.dataPlane)" - /> - </SummaryView> - </RouterView> - </template> + <component + :is="child.Component" + :name="route.params.dataPlane" + :dataplane-overview="dataplanesData?.items.find((item) => item.name === route.params.dataPlane)" + /> + </SummaryView> + </RouterView> </DataSource> </AppView> </RouteView> @@ -134,13 +120,12 @@ </template> <script lang="ts" setup> +import ErrorBlock from '@/app/common/ErrorBlock.vue' import FilterBar from '@/app/common/filter-bar/FilterBar.vue' import SummaryView from '@/app/common/SummaryView.vue' import DataPlaneList from '@/app/data-planes/components/DataPlaneList.vue' import { DataPlaneCollectionSource } from '@/app/data-planes/sources' import type { MeSource } from '@/app/me/sources' -import type { SelectItem } from '@kong/kongponents' - </script> <style lang="scss" scoped> diff --git a/src/locales/en-us/index.ts b/src/locales/en-us/index.ts index 5b1981c99..2972d1015 100644 --- a/src/locales/en-us/index.ts +++ b/src/locales/en-us/index.ts @@ -4,7 +4,6 @@ import http from './http/index.yaml' import controlplanes from '@/app/control-planes/locales/en-us/index.yaml' import dataplanes from '@/app/data-planes/locales/en-us/index.yaml' import diagnostics from '@/app/diagnostics/locales/en-us/index.yaml' -import gateways from '@/app/gateways/locales/en-us/index.yaml' import meshes from '@/app/meshes/locales/en-us/index.yaml' import onboarding from '@/app/onboarding/locales/en-us/index.yaml' import policies from '@/app/policies/locales/en-us/index.yaml' @@ -24,7 +23,6 @@ export default { ...services, ...policies, ...dataplanes, - ...gateways, ...zones, ...zoneIngresses, ...zoneEgresses,