From d558f41d3a0a75b71d0be3879feeee55a294c668 Mon Sep 17 00:00:00 2001 From: Ankit Luthra Date: Thu, 29 Sep 2022 14:32:50 +0530 Subject: [PATCH 1/9] feat: enhance catalog-plugin table with label column Signed-off-by: Ankit Luthra --- .changeset/fifty-berries-learn.md | 40 ++++++++++++++++++ plugins/catalog/api-report.md | 9 ++++ .../CatalogTable/CatalogTable.test.tsx | 38 +++++++++++++++++ .../src/components/CatalogTable/columns.tsx | 41 +++++++++++++++++++ 4 files changed, 128 insertions(+) create mode 100644 .changeset/fifty-berries-learn.md diff --git a/.changeset/fifty-berries-learn.md b/.changeset/fifty-berries-learn.md new file mode 100644 index 0000000000..eb95d1febd --- /dev/null +++ b/.changeset/fifty-berries-learn.md @@ -0,0 +1,40 @@ +--- +'@backstage/plugin-catalog': patch +--- + +--- + +This change would allow consumers of plugin catalog to make use of labels just like tags from metadata. + +The intent of change is to show customised columns based on selected label with which entity is associated. +For example: In example below category and visibility are type of labels associated with API entity. +YAML for API entity + +```yaml +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + name: sample-api + description: API for sample + links: + - url: http://localhost:8080/swagger-ui.html + title: Swagger UI + tags: + - http + labels: + category: legacy + visibility: protected +``` + +Consumers can customise columns to include label column and show in api-docs list + +```typescript +const columns = [ + CatalogTable.columns.createNameColumn({ defaultKind: 'API' }), + CatalogTable.columns.createLabelColumn('category', { title: 'Category' }), + CatalogTable.columns.createLabelColumn('visibility', { + title: 'Visibility', + defaultValue: 'public', + }), +]; +``` diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md index 377b62a1e6..71006b53b5 100644 --- a/plugins/catalog/api-report.md +++ b/plugins/catalog/api-report.md @@ -148,6 +148,15 @@ export const CatalogTable: { } | undefined, ): TableColumn; + createLabelColumn( + key: string, + options?: + | { + title?: string | undefined; + defaultValue?: string | undefined; + } + | undefined, + ): TableColumn; }>; }; diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx index bd78cecaf0..bb2f302a37 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx @@ -331,4 +331,42 @@ describe('CatalogTable component', () => { expect(getByText('Should be rendered')).toBeInTheDocument(); }); + + it('should render the label column with customised title and value as specified', async () => { + const columns = [ + CatalogTable.columns.createNameColumn({ defaultKind: 'API' }), + CatalogTable.columns.createLabelColumn('category', { title: 'Category' }), + ]; + const entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'API', + metadata: { + name: 'APIWithLabel', + labels: { category: 'generic' }, + }, + }; + const expectedColumns = ['Name', 'Category', 'Actions']; + + const { getAllByRole, getByText } = await renderInTestApp( + + + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, + ); + + const columnHeader = getAllByRole('button').filter( + c => c.tagName === 'SPAN', + ); + const columnHeaderLabels = columnHeader.map(c => c.textContent); + expect(columnHeaderLabels).toEqual(expectedColumns); + + const labelCellValue = getByText('generic'); + expect(labelCellValue).toBeInTheDocument(); + }); }); diff --git a/plugins/catalog/src/components/CatalogTable/columns.tsx b/plugins/catalog/src/components/CatalogTable/columns.tsx index 19d160c0e2..68491f2e07 100644 --- a/plugins/catalog/src/components/CatalogTable/columns.tsx +++ b/plugins/catalog/src/components/CatalogTable/columns.tsx @@ -25,6 +25,31 @@ import { OverflowTooltip, TableColumn } from '@backstage/core-components'; import { Entity } from '@backstage/catalog-model'; import { JsonArray } from '@backstage/types'; +/** + * Renders label if exists in entities or default + * @param labels - Key value pairs of labels specified in entity metadata + * @param key - specified label key to be extracted from all labels + * @param defaultValue - shows default in case label key does not exist + * @returns Chip style component when label value exists undefined otherwise. + */ +const renderLabel = ( + labels: Record | undefined, + key: string, + defaultValue?: string, +) => { + const specifiedLabelValue = (labels && labels[key]) || defaultValue; + return ( + specifiedLabelValue && ( + + ) + ); +}; + // The columnFactories symbol is not directly exported, but through the // CatalogTable.columns field. /** @public */ @@ -162,4 +187,20 @@ export const columnFactories = Object.freeze({ searchable: true, }; }, + createLabelColumn( + key: string, + options?: { title?: string; defaultValue?: string }, + ): TableColumn { + return { + title: options?.title || 'Label', + field: 'entity.metadata.labels', + cellStyle: { + padding: '0px 16px 0px 20px', + }, + render: ({ entity }: { entity: Entity }) => ( + <>{renderLabel(entity.metadata?.labels, key, options?.defaultValue)} + ), + width: 'auto', + }; + }, }); From 273009ba390933c83487ca6597ae7fc96ebaf003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 29 Sep 2022 19:33:40 +0200 Subject: [PATCH 2/9] One more integration test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../catalog-backend/src/integration.test.ts | 144 +++++++++++++++++- .../DefaultCatalogProcessingEngine.ts | 4 +- 2 files changed, 142 insertions(+), 6 deletions(-) diff --git a/plugins/catalog-backend/src/integration.test.ts b/plugins/catalog-backend/src/integration.test.ts index 67b63428db..dcfe9a1489 100644 --- a/plugins/catalog-backend/src/integration.test.ts +++ b/plugins/catalog-backend/src/integration.test.ts @@ -34,7 +34,10 @@ import { ScmIntegrations } from '@backstage/integration'; import { DefaultCatalogRulesEnforcer } from './ingestion/CatalogRules'; import { Stitcher } from './stitching/Stitcher'; import { DefaultEntitiesCatalog } from './service/DefaultEntitiesCatalog'; -import { DefaultCatalogProcessingEngine } from './processing/DefaultCatalogProcessingEngine'; +import { + DefaultCatalogProcessingEngine, + ProgressTracker, +} from './processing/DefaultCatalogProcessingEngine'; import { createHash } from 'crypto'; import { DefaultRefreshService } from './service/DefaultRefreshService'; import { connectEntityProviders } from './processing/connectEntityProviders'; @@ -69,10 +72,6 @@ class TestProvider implements EntityProvider { } } -type ProgressTracker = NonNullable< - ConstructorParameters[7] ->; - class ProxyProgressTracker implements ProgressTracker { #inner: ProgressTracker; @@ -540,4 +539,139 @@ describe('Catalog Backend Integration', () => { await expect(harness.getOutputEntities()).resolves.toEqual(outputEntities); }); + + // NOTE(freben): This test documents existing behavior, but it would be more correct to mark the cycle as orphans + it('leaves behind orphaned cycles without orphan markers', async () => { + function mkEntity(name: string) { + return { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name, + annotations: { + 'backstage.io/managed-by-location': 'url:.', + 'backstage.io/managed-by-origin-location': 'url:.', + }, + }, + }; + } + + const harness = await TestHarness.create({ + async processEntity( + entity: Entity, + location: LocationSpec, + emit: CatalogProcessorEmit, + ) { + if (entity.spec?.noEmit) { + return entity; + } + switch (entity.metadata.name) { + case 'a': + emit(processingResult.entity(location, mkEntity('b'))); + break; + case 'b': + emit(processingResult.entity(location, mkEntity('c'))); + break; + case 'c': + emit(processingResult.entity(location, mkEntity('d'))); + break; + case 'd': + emit(processingResult.entity(location, mkEntity('b'))); + break; + default: + } + return entity; + }, + }); + + await harness.setInputEntities([ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'a', + annotations: { + 'backstage.io/managed-by-location': 'url:.', + 'backstage.io/managed-by-origin-location': 'url:.', + }, + }, + }, + ]); + + await expect(harness.getOutputEntities()).resolves.toEqual({}); + await expect(harness.process()).resolves.toEqual({}); + + await expect(harness.getOutputEntities()).resolves.toEqual({ + 'component:default/a': expect.objectContaining({ + metadata: expect.objectContaining({ name: 'a' }), + }), + 'component:default/b': expect.objectContaining({ + metadata: expect.objectContaining({ name: 'b' }), + }), + 'component:default/c': expect.objectContaining({ + metadata: expect.objectContaining({ name: 'c' }), + }), + 'component:default/d': expect.objectContaining({ + metadata: expect.objectContaining({ name: 'd' }), + }), + }); + // NOTE(freben): Avoid .toHaveProperty here, since it treats dots as path separators + expect( + (await harness.getOutputEntities())['component:default/b'].metadata + .annotations!['backstage.io/orphan'], + ).toBeUndefined(); + expect( + (await harness.getOutputEntities())['component:default/c'].metadata + .annotations!['backstage.io/orphan'], + ).toBeUndefined(); + expect( + (await harness.getOutputEntities())['component:default/d'].metadata + .annotations!['backstage.io/orphan'], + ).toBeUndefined(); + + await harness.setInputEntities([ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'a', + annotations: { + 'backstage.io/managed-by-location': 'url:.', + 'backstage.io/managed-by-origin-location': 'url:.', + }, + }, + spec: { noEmit: true }, + }, + ]); + + await expect(harness.process()).resolves.toEqual({}); + + await expect(harness.getOutputEntities()).resolves.toEqual({ + 'component:default/a': expect.objectContaining({ + metadata: expect.objectContaining({ name: 'a' }), + }), + 'component:default/b': expect.objectContaining({ + metadata: expect.objectContaining({ name: 'b' }), + }), + 'component:default/c': expect.objectContaining({ + metadata: expect.objectContaining({ name: 'c' }), + }), + 'component:default/d': expect.objectContaining({ + metadata: expect.objectContaining({ name: 'd' }), + }), + }); + // TODO(freben): Ideally these should be orphaned now + expect( + (await harness.getOutputEntities())['component:default/b'].metadata + .annotations!['backstage.io/orphan'], + ).toBeUndefined(); + expect( + (await harness.getOutputEntities())['component:default/c'].metadata + .annotations!['backstage.io/orphan'], + ).toBeUndefined(); + expect( + (await harness.getOutputEntities())['component:default/d'].metadata + .annotations!['backstage.io/orphan'], + ).toBeUndefined(); + }); }); diff --git a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts index 4693ffc559..2cc0e2dc40 100644 --- a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts @@ -31,6 +31,8 @@ import { startTaskPipeline } from './TaskPipeline'; const CACHE_TTL = 5; +export type ProgressTracker = ReturnType; + export class DefaultCatalogProcessingEngine implements CatalogProcessingEngine { private stopFunc?: () => void; @@ -45,7 +47,7 @@ export class DefaultCatalogProcessingEngine implements CatalogProcessingEngine { unprocessedEntity: Entity; errors: Error[]; }) => Promise | void, - private readonly tracker = progressTracker(), + private readonly tracker: ProgressTracker = progressTracker(), ) {} async start() { From c74b8e2807a78524c334ae0ac7875b8ecdddca23 Mon Sep 17 00:00:00 2001 From: Ankit Luthra Date: Fri, 30 Sep 2022 18:13:53 +0530 Subject: [PATCH 3/9] chore: rephrase changeset and inline render logic for feat: enhance catalog-plugin with label column Signed-off-by: Ankit Luthra --- .changeset/fifty-berries-learn.md | 9 ++-- .../src/components/CatalogTable/columns.tsx | 46 ++++++++----------- 2 files changed, 21 insertions(+), 34 deletions(-) diff --git a/.changeset/fifty-berries-learn.md b/.changeset/fifty-berries-learn.md index eb95d1febd..a05d5f6078 100644 --- a/.changeset/fifty-berries-learn.md +++ b/.changeset/fifty-berries-learn.md @@ -2,13 +2,10 @@ '@backstage/plugin-catalog': patch --- ---- +Added new column `Label` to `CatalogTable.columns`, this new column allows you make use of labels from metadata. +For example: category and visibility are type of labels associated with API entity illustrated below. -This change would allow consumers of plugin catalog to make use of labels just like tags from metadata. - -The intent of change is to show customised columns based on selected label with which entity is associated. -For example: In example below category and visibility are type of labels associated with API entity. -YAML for API entity +YAML code snippet for API entity ```yaml apiVersion: backstage.io/v1alpha1 diff --git a/plugins/catalog/src/components/CatalogTable/columns.tsx b/plugins/catalog/src/components/CatalogTable/columns.tsx index 68491f2e07..7f3cd5ef82 100644 --- a/plugins/catalog/src/components/CatalogTable/columns.tsx +++ b/plugins/catalog/src/components/CatalogTable/columns.tsx @@ -25,31 +25,6 @@ import { OverflowTooltip, TableColumn } from '@backstage/core-components'; import { Entity } from '@backstage/catalog-model'; import { JsonArray } from '@backstage/types'; -/** - * Renders label if exists in entities or default - * @param labels - Key value pairs of labels specified in entity metadata - * @param key - specified label key to be extracted from all labels - * @param defaultValue - shows default in case label key does not exist - * @returns Chip style component when label value exists undefined otherwise. - */ -const renderLabel = ( - labels: Record | undefined, - key: string, - defaultValue?: string, -) => { - const specifiedLabelValue = (labels && labels[key]) || defaultValue; - return ( - specifiedLabelValue && ( - - ) - ); -}; - // The columnFactories symbol is not directly exported, but through the // CatalogTable.columns field. /** @public */ @@ -197,9 +172,24 @@ export const columnFactories = Object.freeze({ cellStyle: { padding: '0px 16px 0px 20px', }, - render: ({ entity }: { entity: Entity }) => ( - <>{renderLabel(entity.metadata?.labels, key, options?.defaultValue)} - ), + render: ({ entity }: { entity: Entity }) => { + const labels: Record | undefined = + entity.metadata?.labels; + const specifiedLabelValue = + (labels && labels[key]) || options?.defaultValue; + return ( + <> + {specifiedLabelValue && ( + + )} + + ); + }, width: 'auto', }; }, From b36d4a5189066dd28b4a234b366d3b4eed54795a Mon Sep 17 00:00:00 2001 From: Pascal Wallenius Date: Fri, 30 Sep 2022 11:59:23 +0200 Subject: [PATCH 4/9] add link to questions tagged 'backstage' in Stack Overflow Signed-off-by: Pascal Wallenius --- docs/overview/support.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/overview/support.md b/docs/overview/support.md index 5122a1c9ff..662a37c6ae 100644 --- a/docs/overview/support.md +++ b/docs/overview/support.md @@ -6,6 +6,7 @@ description: Support and Community Details and Links - [Discord chatroom](https://discord.gg/MUpMjP2) - Get support or discuss the project. +- [Stack Overflow](https://stackoverflow.com/questions/tagged/backstage) - Browse or ask questions on Stack Overflow. - [Good First Issues](https://github.com/backstage/backstage/contribute) - Start here if you want to contribute. - [RFCs](https://github.com/backstage/backstage/labels/rfc) - Help shape the From d76de4acf5b5a68352ab55505e8006dfc83a650c Mon Sep 17 00:00:00 2001 From: andreizimin <75641133+andreizimin@users.noreply.github.com> Date: Fri, 30 Sep 2022 11:50:59 -0700 Subject: [PATCH 5/9] Update ADOPTERS.md Updated key contacts for VMware Signed-off-by: andreizimin <75641133+andreizimin@users.noreply.github.com> --- ADOPTERS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADOPTERS.md b/ADOPTERS.md index 4378eb14f9..e7e0e7784c 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -91,7 +91,7 @@ _You can do this by using the [Adopter form](https://form.typeform.com/to/zcOaKi | [HBO Max](https://hbomax.com) | [@mdb](https://github.com/mdb), [@nesta219](https://github.com/nesta219), [@nmische](https://github.com/nmische), [@hbomark](https://github.com/hbomark) | Developer portal hosting service catalog and API documentation, as well as cloud infrastructure details, operational visibility tools, and a custom plugin for browsing notable platform change events, such as deployments and configuration updates. | | [RCHLO](https://www.riachuelo.com.br) & [MIDWAY](https://www.midway.com.br) | [@marcosborges](https://github.com/marcosborges), [@defaultbr](https://github.com/defaultbr) | Self-Service Platform | | [HP Inc](https://www.hp.com) | [Damon Kaswell](https://github.com/dekoding) | DevEx engagement hub (dev portal: docs, standards, Q&A) and extensive assets catalog (APIs, services, code, data, etc.) for the pan-HP internal developer community. | -| [VMware](https://www.vmware.com) | [@mpriamo](https://github.com/mpriamo), [@krisapplegate](https://github.com/krisapplegate) | Part of [Tanzu Application Platform](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/index.html) offering; internal developer portal | +| [VMware](https://www.vmware.com) | [Waldir Montoya](https://github.com/waldirmontoya25), [Kris Applegate](https://github.com/krisapplegate), [Jamie Klassen](https://github.com/jamieklassen) | Part of [Tanzu Application Platform](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/index.html) offering; internal developer portal | | [Ualá](https://www.uala.com.ar/) | [Santiago Bernal](https://github.com/sabernal) | Initial work being done to centralize documentation for all our microservices and APIs, as well as scaffolding new services and tracking code quality | | [IKEA IT AB](https://www.ingka.com) | [@bjornramberg](https://github.com/bjornramberg), [@supriyachitale](https://github.com/supriyachitale) | Supporting engineers at scale with self serve access and connecting the dots of our engineering platform and services, enabling product teams to move faster and go further, and unleashing innovation, reuse and co-creation across the organisation. | | [Invitae](https://www.invitae.com/en) | [@ryan-hanchett](https://github.com/ryan-hanchett), [@gmandler42](https://github.com/gmandler42) | Centralized Developer Experience portal, putting all of our tooling behind a single pane of glass and creating a living service catalog. | From d4a8c683bebcdc2914c94eaf59ebab10faff3007 Mon Sep 17 00:00:00 2001 From: Matthew Clarke Date: Sat, 1 Oct 2022 12:53:11 -0400 Subject: [PATCH 6/9] feat: Add request details to k8s service locator (#13871) * feat: Add request details to k8s service locator BREAKING_CHANGE: Service locator now takes additional args Signed-off-by: Matthew Clarke * changeset && api report Signed-off-by: Matthew Clarke * Update .changeset/silent-bees-repeat.md Co-authored-by: Patrik Oldsberg Signed-off-by: Matthew Clarke Signed-off-by: Matthew Clarke * update changeset Signed-off-by: Matthew Clarke * Update silent-bees-repeat.md Signed-off-by: Matthew Clarke Signed-off-by: Matthew Clarke Signed-off-by: Matthew Clarke Co-authored-by: Patrik Oldsberg --- .changeset/silent-bees-repeat.md | 5 +++++ plugins/kubernetes-backend/api-report.md | 13 ++++++++++++- .../MultiTenantServiceLocator.test.ts | 16 +++++++++++++--- .../service-locator/MultiTenantServiceLocator.ts | 2 ++ .../src/service/KubernetesFanOutHandler.ts | 9 +++++++-- plugins/kubernetes-backend/src/types/types.ts | 13 ++++++++++++- 6 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 .changeset/silent-bees-repeat.md diff --git a/.changeset/silent-bees-repeat.md b/.changeset/silent-bees-repeat.md new file mode 100644 index 0000000000..4592d9a703 --- /dev/null +++ b/.changeset/silent-bees-repeat.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes-backend': patch +--- + +kubernetes service locator now take request context parameters diff --git a/plugins/kubernetes-backend/api-report.md b/plugins/kubernetes-backend/api-report.md index 4219387819..bf2c9e8082 100644 --- a/plugins/kubernetes-backend/api-report.md +++ b/plugins/kubernetes-backend/api-report.md @@ -326,7 +326,10 @@ export type KubernetesObjectTypes = // @alpha export interface KubernetesServiceLocator { // (undocumented) - getClustersByEntity(entity: Entity): Promise<{ + getClustersByEntity( + entity: Entity, + requestContext: ServiceLocatorRequestContext, + ): Promise<{ clusters: ClusterDetails[]; }>; } @@ -403,6 +406,14 @@ export interface ServiceAccountClusterDetails extends ClusterDetails {} // @alpha (undocumented) export type ServiceLocatorMethod = 'multiTenant' | 'http'; +// @alpha (undocumented) +export interface ServiceLocatorRequestContext { + // (undocumented) + customResources: CustomResourceMatcher[]; + // (undocumented) + objectTypesToFetch: Set; +} + // @alpha (undocumented) export type SigningCreds = { accessKeyId: string | undefined; diff --git a/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.test.ts b/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.test.ts index 2ead460848..b40503efc9 100644 --- a/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.test.ts +++ b/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.test.ts @@ -16,6 +16,7 @@ import '@backstage/backend-common'; import { Entity } from '@backstage/catalog-model'; +import { ServiceLocatorRequestContext } from '../types/types'; import { MultiTenantServiceLocator } from './MultiTenantServiceLocator'; describe('MultiTenantConfigClusterLocator', () => { @@ -24,7 +25,10 @@ describe('MultiTenantConfigClusterLocator', () => { getClusters: async () => [], }); - const result = await sut.getClustersByEntity({} as Entity); + const result = await sut.getClustersByEntity( + {} as Entity, + {} as ServiceLocatorRequestContext, + ); expect(result).toStrictEqual({ clusters: [] }); }); @@ -43,7 +47,10 @@ describe('MultiTenantConfigClusterLocator', () => { }, }); - const result = await sut.getClustersByEntity({} as Entity); + const result = await sut.getClustersByEntity( + {} as Entity, + {} as ServiceLocatorRequestContext, + ); expect(result).toStrictEqual({ clusters: [ @@ -76,7 +83,10 @@ describe('MultiTenantConfigClusterLocator', () => { }, }); - const result = await sut.getClustersByEntity({} as Entity); + const result = await sut.getClustersByEntity( + {} as Entity, + {} as ServiceLocatorRequestContext, + ); expect(result).toStrictEqual({ clusters: [ diff --git a/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.ts b/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.ts index 7bafbb10f3..ed902b194a 100644 --- a/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.ts +++ b/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.ts @@ -19,6 +19,7 @@ import { ClusterDetails, KubernetesClustersSupplier, KubernetesServiceLocator, + ServiceLocatorRequestContext, } from '../types/types'; // This locator assumes that every service is located on every cluster @@ -33,6 +34,7 @@ export class MultiTenantServiceLocator implements KubernetesServiceLocator { // As this implementation always returns all clusters serviceId is ignored here getClustersByEntity( _entity: Entity, + _requestContext: ServiceLocatorRequestContext, ): Promise<{ clusters: ClusterDetails[] }> { return this.clusterSupplier.getClusters().then(clusters => ({ clusters })); } diff --git a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts index 23163d1004..64bbec6051 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts @@ -27,6 +27,7 @@ import { CustomResource, CustomResourcesByEntity, KubernetesObjectsByEntity, + ServiceLocatorRequestContext, } from '../types/types'; import { KubernetesAuthTranslator } from '../kubernetes-auth-translator/types'; import { KubernetesAuthTranslatorGenerator } from '../kubernetes-auth-translator/KubernetesAuthTranslatorGenerator'; @@ -231,7 +232,10 @@ export class KubernetesFanOutHandler { entity.metadata?.name; const clusterDetailsDecoratedForAuth: ClusterDetails[] = - await this.decorateClusterDetailsWithAuth(entity, auth); + await this.decorateClusterDetailsWithAuth(entity, auth, { + objectTypesToFetch: objectTypesToFetch, + customResources: customResources ?? [], + }); this.logger.info( `entity.metadata.name=${entityName} clusterDetails=[${clusterDetailsDecoratedForAuth @@ -274,9 +278,10 @@ export class KubernetesFanOutHandler { private async decorateClusterDetailsWithAuth( entity: Entity, auth: KubernetesRequestAuth, + requestContext: ServiceLocatorRequestContext, ) { const clusterDetails: ClusterDetails[] = await ( - await this.serviceLocator.getClustersByEntity(entity) + await this.serviceLocator.getClustersByEntity(entity, requestContext) ).clusters; // Execute all of these async actions simultaneously/without blocking sequentially as no common object is modified by them diff --git a/plugins/kubernetes-backend/src/types/types.ts b/plugins/kubernetes-backend/src/types/types.ts index 5ddfe7367d..a90f94c491 100644 --- a/plugins/kubernetes-backend/src/types/types.ts +++ b/plugins/kubernetes-backend/src/types/types.ts @@ -120,12 +120,23 @@ export interface KubernetesClustersSupplier { getClusters(): Promise; } +/** + * @alpha + */ +export interface ServiceLocatorRequestContext { + objectTypesToFetch: Set; + customResources: CustomResourceMatcher[]; +} + /** * Used to locate which cluster(s) a service is running on * @alpha */ export interface KubernetesServiceLocator { - getClustersByEntity(entity: Entity): Promise<{ clusters: ClusterDetails[] }>; + getClustersByEntity( + entity: Entity, + requestContext: ServiceLocatorRequestContext, + ): Promise<{ clusters: ClusterDetails[] }>; } /** From 94dbaf364590ce6c9b804ee1fdbf13fb65ffb951 Mon Sep 17 00:00:00 2001 From: Bradley Grainger Date: Sat, 1 Oct 2022 20:33:06 -0700 Subject: [PATCH 7/9] Document host attribute for github catalog provider. This was added in https://github.com/backstage/backstage/pull/13400 but isn't documented yet. Signed-off-by: Bradley Grainger --- docs/integrations/github/discovery.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/integrations/github/discovery.md b/docs/integrations/github/discovery.md index 6966753d68..6acb87ac37 100644 --- a/docs/integrations/github/discovery.md +++ b/docs/integrations/github/discovery.md @@ -96,6 +96,10 @@ catalog: topic: include: ['backstage-include'] # optional array of strings exclude: ['experiments'] # optional array of strings + enterpriseProviderId: + host: ghe.example.net + organization: 'backstage' # string + catalogPath: '/catalog-info.yaml' # string ``` This provider supports multiple organizations via unique provider IDs. @@ -125,6 +129,8 @@ This provider supports multiple organizations via unique provider IDs. - **organization**: Name of your organization account/workspace. If you want to add multiple organizations, you need to add one provider config each. +- **host** _(optional)_: + The hostname of your GitHub Enterprise instance. It must match a host defined in [integrations.github](locations.md). ## GitHub API Rate Limits From a948b6af07d45b304eb39af6e81582a5f965e848 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Mon, 3 Oct 2022 10:14:05 +0200 Subject: [PATCH 8/9] ci: skip publishing upgrade-helper diff for next releases Signed-off-by: Vincenzo Scamporlino --- .github/workflows/sync_release-manifest.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sync_release-manifest.yml b/.github/workflows/sync_release-manifest.yml index 8cf6991aaf..c6d00ecd28 100644 --- a/.github/workflows/sync_release-manifest.yml +++ b/.github/workflows/sync_release-manifest.yml @@ -53,6 +53,10 @@ jobs: github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} # TODO(Rugvip): Remove the create-app dispatch once we've been on the release version for a while script: | + const releaseVersion = require('./backstage/package.json').version; + if(releaseVersion.includes('next')) { + return; + } console.log('Dispatching upgrade helper sync'); await github.rest.actions.createWorkflowDispatch({ owner: 'backstage', @@ -61,6 +65,6 @@ jobs: ref: 'master', inputs: { version: require('./backstage/packages/create-app/package.json').version, - releaseVersion: require('./backstage/package.json').version + releaseVersion }, }); From 3f3ac6222624952bbafce0647610261ae96ed400 Mon Sep 17 00:00:00 2001 From: Ankit Luthra Date: Mon, 3 Oct 2022 14:39:19 +0530 Subject: [PATCH 9/9] chore: change upgrade version as minor in changeset for feat: enhance catalog-plugin with label column Signed-off-by: Ankit Luthra --- .changeset/fifty-berries-learn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/fifty-berries-learn.md b/.changeset/fifty-berries-learn.md index a05d5f6078..647246a482 100644 --- a/.changeset/fifty-berries-learn.md +++ b/.changeset/fifty-berries-learn.md @@ -1,5 +1,5 @@ --- -'@backstage/plugin-catalog': patch +'@backstage/plugin-catalog': minor --- Added new column `Label` to `CatalogTable.columns`, this new column allows you make use of labels from metadata.