diff --git a/.changeset/eighty-comics-divide.md b/.changeset/eighty-comics-divide.md new file mode 100644 index 0000000000..c486c1fa33 --- /dev/null +++ b/.changeset/eighty-comics-divide.md @@ -0,0 +1,5 @@ +--- +'@backstage/catalog-model': patch +--- + +Add shared annotations for Kubernetes clusters diff --git a/.changeset/itchy-stingrays-greet.md b/.changeset/itchy-stingrays-greet.md new file mode 100644 index 0000000000..ed409abbe3 --- /dev/null +++ b/.changeset/itchy-stingrays-greet.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-aws': patch +--- + +Add processor for ingesting EKS clusters into the catalog diff --git a/.changeset/plenty-books-peel.md b/.changeset/plenty-books-peel.md new file mode 100644 index 0000000000..423c6f8bd4 --- /dev/null +++ b/.changeset/plenty-books-peel.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes-backend': patch +--- + +Add support for Kubernetes clusters in the catalog. diff --git a/docs/features/kubernetes/configuration.md b/docs/features/kubernetes/configuration.md index 633584bdb5..dce4e60a87 100644 --- a/docs/features/kubernetes/configuration.md +++ b/docs/features/kubernetes/configuration.md @@ -57,11 +57,16 @@ This is an array used to determine where to retrieve cluster configuration from. Valid cluster locator methods are: +- [`catalog`](#catalog) - [`localKubectlProxy`](#localKubectlProxy) - [`config`](#config) - [`gke`](#gke) - [custom `KubernetesClustersSupplier`](#custom-kubernetesclusterssupplier) +#### `catalog` + +This cluster locator method will read cluster information from the catalog. + #### `localKubectlProxy` This cluster locator method will assume a locally running [`kubectl proxy`](https://kubernetes.io/docs/tasks/extend-kubernetes/http-proxy-access-api/#using-kubectl-to-start-a-proxy-server) process using the default port (8001). diff --git a/packages/catalog-model/api-report.md b/packages/catalog-model/api-report.md index 9b9136fc3c..b7456bb2f8 100644 --- a/packages/catalog-model/api-report.md +++ b/packages/catalog-model/api-report.md @@ -14,6 +14,17 @@ export interface AlphaEntity extends Entity { // @public export const ANNOTATION_EDIT_URL = 'backstage.io/edit-url'; +// @public +export const ANNOTATION_KUBERNETES_API_SERVER = 'kubernetes.io/api-server'; + +// @public +export const ANNOTATION_KUBERNETES_API_SERVER_CA = + 'kubernetes.io/api-server-certificate-authority'; + +// @public +export const ANNOTATION_KUBERNETES_AUTH_PROVIDER = + 'kubernetes.io/auth-provider'; + // @public export const ANNOTATION_LOCATION = 'backstage.io/managed-by-location'; diff --git a/packages/catalog-model/src/entity/constants.ts b/packages/catalog-model/src/entity/constants.ts index 5dc21683bd..ac9e813394 100644 --- a/packages/catalog-model/src/entity/constants.ts +++ b/packages/catalog-model/src/entity/constants.ts @@ -34,3 +34,26 @@ export const ANNOTATION_VIEW_URL = 'backstage.io/view-url'; * @public */ export const ANNOTATION_EDIT_URL = 'backstage.io/edit-url'; + +/** + * Annotation for specifying the API server of a Kubernetes cluster + * + * @public + */ +export const ANNOTATION_KUBERNETES_API_SERVER = 'kubernetes.io/api-server'; + +/** + * Annotation for specifying the Certificate Authority of an API server for a Kubernetes cluster + * + * @public + */ +export const ANNOTATION_KUBERNETES_API_SERVER_CA = + 'kubernetes.io/api-server-certificate-authority'; + +/** + * Annotation for specifying the auth provider for a Kubernetes cluster + * + * @public + */ +export const ANNOTATION_KUBERNETES_AUTH_PROVIDER = + 'kubernetes.io/auth-provider'; diff --git a/packages/catalog-model/src/entity/index.ts b/packages/catalog-model/src/entity/index.ts index 039529bf3e..a97476ba11 100644 --- a/packages/catalog-model/src/entity/index.ts +++ b/packages/catalog-model/src/entity/index.ts @@ -19,6 +19,9 @@ export { DEFAULT_NAMESPACE, ANNOTATION_EDIT_URL, ANNOTATION_VIEW_URL, + ANNOTATION_KUBERNETES_API_SERVER, + ANNOTATION_KUBERNETES_API_SERVER_CA, + ANNOTATION_KUBERNETES_AUTH_PROVIDER, } from './constants'; export type { AlphaEntity, diff --git a/plugins/catalog-backend-module-aws/api-report.md b/plugins/catalog-backend-module-aws/api-report.md index c2c18431b9..4e8db41fac 100644 --- a/plugins/catalog-backend-module-aws/api-report.md +++ b/plugins/catalog-backend-module-aws/api-report.md @@ -7,6 +7,7 @@ import { CatalogProcessor } from '@backstage/plugin-catalog-backend'; import { CatalogProcessorEmit } from '@backstage/plugin-catalog-backend'; import { CatalogProcessorParser } from '@backstage/plugin-catalog-backend'; import { Config } from '@backstage/config'; +import { Credentials } from 'aws-sdk'; import { EntityProvider } from '@backstage/plugin-catalog-backend'; import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; import { LocationSpec } from '@backstage/plugin-catalog-backend'; @@ -14,6 +15,26 @@ import { Logger } from 'winston'; import { TaskRunner } from '@backstage/backend-tasks'; import { UrlReader } from '@backstage/backend-common'; +// @public +export type AWSCredentialFactory = ( + awsAccountId: string, +) => Promise; + +// @public +export class AwsEKSClusterProcessor implements CatalogProcessor { + constructor(options: { credentialsFactory?: AWSCredentialFactory }); + // (undocumented) + getProcessorName(): string; + // (undocumented) + normalizeName(name: string): string; + // (undocumented) + readLocation( + location: LocationSpec, + _optional: boolean, + emit: CatalogProcessorEmit, + ): Promise; +} + // @public export class AwsOrganizationCloudAccountProcessor implements CatalogProcessor { // (undocumented) diff --git a/plugins/catalog-backend-module-aws/src/index.ts b/plugins/catalog-backend-module-aws/src/index.ts index 1d8e844895..212308edaa 100644 --- a/plugins/catalog-backend-module-aws/src/index.ts +++ b/plugins/catalog-backend-module-aws/src/index.ts @@ -22,3 +22,4 @@ export * from './processors'; export * from './providers'; +export * from './types'; diff --git a/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.test.ts b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.test.ts new file mode 100644 index 0000000000..3e33ba740c --- /dev/null +++ b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.test.ts @@ -0,0 +1,74 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AwsEKSClusterProcessor } from './AwsEKSClusterProcessor'; +import AWSMock from 'aws-sdk-mock'; +import aws from 'aws-sdk'; + +describe('AwsEKSClusterProcessor', () => { + AWSMock.setSDKInstance(aws); + describe('readLocation', () => { + const processor = new (AwsEKSClusterProcessor as any)({}); + const location = { type: 'aws-eks', target: '957140518395/us-west-2' }; + const emit = jest.fn(); + it('generates cluster correctly', async () => { + const clusters: aws.EKS.Types.ListClustersResponse = { + clusters: ['backstage-test'], + nextToken: undefined, + }; + + const cluster: aws.EKS.Types.DescribeClusterResponse = { + cluster: { + name: 'backstage-test', + arn: 'arn:aws:1', + endpoint: 'https://backstage.io/kubernetes-api-server', + certificateAuthority: { + data: 'cert', + }, + }, + }; + AWSMock.mock('EKS', 'listClusters', clusters); + + AWSMock.mock('EKS', 'describeCluster', cluster); + + await processor.readLocation(location, false, emit); + expect(emit).toBeCalledWith({ + type: 'entity', + location, + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Resource', + metadata: { + annotations: { + 'amazonaws.com/account-id': '957140518395', + 'amazonaws.com/arn': cluster.cluster?.arn, + 'kubernetes.io/api-server': cluster.cluster?.endpoint, + 'kubernetes.io/api-server-certificate-authority': + cluster.cluster?.certificateAuthority?.data, + 'kubernetes.io/auth-provider': 'aws', + }, + name: 'backstage-test', + namespace: 'default', + }, + spec: { + type: 'kubernetes-cluster', + owner: 'unknown', + }, + }, + }); + }); + }); +}); diff --git a/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts new file mode 100644 index 0000000000..c34b247f4d --- /dev/null +++ b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts @@ -0,0 +1,123 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + CatalogProcessor, + CatalogProcessorEmit, + LocationSpec, +} from '@backstage/plugin-catalog-backend'; +import { + ANNOTATION_KUBERNETES_API_SERVER, + ANNOTATION_KUBERNETES_API_SERVER_CA, + ANNOTATION_KUBERNETES_AUTH_PROVIDER, +} from '@backstage/catalog-model'; +import { Credentials, EKS } from 'aws-sdk'; +import { AWSCredentialFactory } from '../types'; + +const ACCOUNTID_ANNOTATION: string = 'amazonaws.com/account-id'; +const ARN_ANNOTATION: string = 'amazonaws.com/arn'; + +/** + * A processor for automatic discovery of resources from EKS clusters. Handles the + * `aws-eks` location type, and target accounts/regions of the form + * `/`. + * + * @public + */ +export class AwsEKSClusterProcessor implements CatalogProcessor { + private credentialsFactory?: AWSCredentialFactory; + + constructor(options: { credentialsFactory?: AWSCredentialFactory }) { + this.credentialsFactory = options.credentialsFactory; + } + + getProcessorName(): string { + return 'aws-eks'; + } + + normalizeName(name: string): string { + return name + .trim() + .toLocaleLowerCase('en-US') + .replace(/[^a-zA-Z0-9\-]/g, '-'); + } + + async readLocation( + location: LocationSpec, + _optional: boolean, + emit: CatalogProcessorEmit, + ): Promise { + if (location.type !== 'aws-eks') { + return false; + } + + // location target is of format "account-id/region" + const [accountId, region] = location.target.split('/'); + + if (!accountId || !region) { + throw new Error( + 'AWS EKS location specified without account or region information', + ); + } + + let credentials: Credentials | undefined; + + if (this.credentialsFactory) { + credentials = await this.credentialsFactory(accountId); + } + + const eksClient = new EKS({ credentials, region }); + const clusters = await eksClient.listClusters({}).promise(); + if (clusters.clusters === undefined) { + return true; + } + + const results = clusters.clusters + .map(cluster => eksClient.describeCluster({ name: cluster }).promise()) + .map(async describedClusterPromise => { + const describedCluster = await describedClusterPromise; + if (describedCluster.cluster) { + const entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Resource', + metadata: { + annotations: { + [ACCOUNTID_ANNOTATION]: accountId, + [ARN_ANNOTATION]: describedCluster.cluster.arn || '', + [ANNOTATION_KUBERNETES_API_SERVER]: + describedCluster.cluster.endpoint || '', + [ANNOTATION_KUBERNETES_API_SERVER_CA]: + describedCluster.cluster.certificateAuthority?.data || '', + [ANNOTATION_KUBERNETES_AUTH_PROVIDER]: 'aws', + }, + name: this.normalizeName(describedCluster.cluster.name as string), + namespace: 'default', + }, + spec: { + type: 'kubernetes-cluster', + owner: 'unknown', + }, + }; + emit({ + type: 'entity', + entity, + location, + }); + } + }); + await Promise.all(results); + return true; + } +} diff --git a/plugins/catalog-backend-module-aws/src/processors/index.ts b/plugins/catalog-backend-module-aws/src/processors/index.ts index 8718e77a6f..63b8a47139 100644 --- a/plugins/catalog-backend-module-aws/src/processors/index.ts +++ b/plugins/catalog-backend-module-aws/src/processors/index.ts @@ -14,5 +14,6 @@ * limitations under the License. */ +export { AwsEKSClusterProcessor } from './AwsEKSClusterProcessor'; export { AwsOrganizationCloudAccountProcessor } from './AwsOrganizationCloudAccountProcessor'; export { AwsS3DiscoveryProcessor } from './AwsS3DiscoveryProcessor'; diff --git a/plugins/catalog-backend-module-aws/src/types.ts b/plugins/catalog-backend-module-aws/src/types.ts new file mode 100644 index 0000000000..3d9df38181 --- /dev/null +++ b/plugins/catalog-backend-module-aws/src/types.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Credentials } from 'aws-sdk'; + +/** + * A factory for providing user-specified AWS credentials for a given AWS account. + * + * @public + */ +export type AWSCredentialFactory = ( + awsAccountId: string, +) => Promise; diff --git a/plugins/kubernetes-backend/api-report.md b/plugins/kubernetes-backend/api-report.md index 363af1b02f..15b1cef853 100644 --- a/plugins/kubernetes-backend/api-report.md +++ b/plugins/kubernetes-backend/api-report.md @@ -15,6 +15,7 @@ import type { KubernetesRequestAuth } from '@backstage/plugin-kubernetes-common' import type { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; import { Logger } from 'winston'; import type { ObjectsByEntityResponse } from '@backstage/plugin-kubernetes-common'; +import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { PodStatus } from '@kubernetes/client-node/dist/top'; // @alpha (undocumented) @@ -279,6 +280,8 @@ export interface RouterOptions { // (undocumented) config: Config; // (undocumented) + discovery: PluginEndpointDiscovery; + // (undocumented) logger: Logger; } diff --git a/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.test.ts b/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.test.ts new file mode 100644 index 0000000000..2a2432f858 --- /dev/null +++ b/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.test.ts @@ -0,0 +1,56 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import '@backstage/backend-common'; +import { CatalogClusterLocator } from './CatalogClusterLocator'; +import { CatalogApi } from '@backstage/catalog-client'; + +describe('CatalogClusterLocator', () => { + it('empty clusters returns empty cluster details', async () => { + const mockCatalogApi = { + getEntityByRef: jest.fn(), + getEntities: async () => ({ + items: [ + { + apiVersion: 'version', + kind: 'User', + metadata: { + annotations: { + 'kubernetes.io/api-server': 'https://apiserver.com', + 'kubernetes.io/api-server-certificate-authority': 'caData', + 'kubernetes.io/auth-provider': 'aws', + }, + name: 'owned', + namespace: 'default', + }, + }, + ], + }), + } as Partial as CatalogApi; + + const sut = CatalogClusterLocator.fromConfig(mockCatalogApi); + + const result = await sut.getClusters(); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + name: 'owned', + url: 'https://apiserver.com', + caData: 'caData', + authProvider: 'aws', + }); + }); +}); diff --git a/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.ts b/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.ts new file mode 100644 index 0000000000..7183b71831 --- /dev/null +++ b/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.ts @@ -0,0 +1,65 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ClusterDetails, KubernetesClustersSupplier } from '../types/types'; +import { CATALOG_FILTER_EXISTS, CatalogApi } from '@backstage/catalog-client'; +import { + ANNOTATION_KUBERNETES_API_SERVER, + ANNOTATION_KUBERNETES_API_SERVER_CA, + ANNOTATION_KUBERNETES_AUTH_PROVIDER, +} from '@backstage/catalog-model'; + +export class CatalogClusterLocator implements KubernetesClustersSupplier { + private catalogClient: CatalogApi; + + constructor(catalogClient: CatalogApi) { + this.catalogClient = catalogClient; + } + + static fromConfig(catalogApi: CatalogApi): CatalogClusterLocator { + return new CatalogClusterLocator(catalogApi); + } + + async getClusters(): Promise { + const apiServerKey = `metadata.annotations.${ANNOTATION_KUBERNETES_API_SERVER}`; + const apiServerCaKey = `metadata.annotations.${ANNOTATION_KUBERNETES_API_SERVER_CA}`; + const authProviderKey = `metadata.annotations.${ANNOTATION_KUBERNETES_AUTH_PROVIDER}`; + + const filter: Record = { + kind: 'Resource', + 'spec.type': 'kubernetes-cluster', + [apiServerKey]: CATALOG_FILTER_EXISTS, + [apiServerCaKey]: CATALOG_FILTER_EXISTS, + [authProviderKey]: CATALOG_FILTER_EXISTS, + }; + + const clusters = await this.catalogClient.getEntities({ + filter: [filter], + }); + return clusters.items.map(entity => { + const clusterDetails: ClusterDetails = { + name: entity.metadata.name, + url: entity.metadata.annotations![ANNOTATION_KUBERNETES_API_SERVER]!, + caData: + entity.metadata.annotations![ANNOTATION_KUBERNETES_API_SERVER_CA]!, + authProvider: + entity.metadata.annotations![ANNOTATION_KUBERNETES_AUTH_PROVIDER]!, + }; + + return clusterDetails; + }); + } +} diff --git a/plugins/kubernetes-backend/src/cluster-locator/index.test.ts b/plugins/kubernetes-backend/src/cluster-locator/index.test.ts index cc7e7d07d6..f494fbbf2b 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/index.test.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/index.test.ts @@ -16,8 +16,11 @@ import { Config, ConfigReader } from '@backstage/config'; import { getCombinedClusterSupplier } from './index'; +import { CatalogApi } from '@backstage/catalog-client'; describe('getCombinedClusterSupplier', () => { + let catalogApi: CatalogApi; + it('should retrieve cluster details from config', async () => { const config: Config = new ConfigReader( { @@ -45,7 +48,7 @@ describe('getCombinedClusterSupplier', () => { 'ctx', ); - const clusterSupplier = getCombinedClusterSupplier(config); + const clusterSupplier = getCombinedClusterSupplier(config, catalogApi); const result = await clusterSupplier.getClusters(); expect(result).toStrictEqual([ @@ -100,7 +103,7 @@ describe('getCombinedClusterSupplier', () => { 'ctx', ); - expect(() => getCombinedClusterSupplier(config)).toThrowError( + expect(() => getCombinedClusterSupplier(config, catalogApi)).toThrowError( new Error('Unsupported kubernetes.clusterLocatorMethods: "magic"'), ); }); diff --git a/plugins/kubernetes-backend/src/cluster-locator/index.ts b/plugins/kubernetes-backend/src/cluster-locator/index.ts index d32f7ffee8..62dfe15720 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/index.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/index.ts @@ -19,6 +19,8 @@ import { Duration } from 'luxon'; import { ClusterDetails, KubernetesClustersSupplier } from '../types/types'; import { ConfigClusterLocator } from './ConfigClusterLocator'; import { GkeClusterLocator } from './GkeClusterLocator'; +import { CatalogClusterLocator } from './CatalogClusterLocator'; +import { CatalogApi } from '@backstage/catalog-client'; import { LocalKubectlProxyClusterLocator } from './LocalKubectlProxyLocator'; class CombinedClustersSupplier implements KubernetesClustersSupplier { @@ -39,6 +41,7 @@ class CombinedClustersSupplier implements KubernetesClustersSupplier { export const getCombinedClusterSupplier = ( rootConfig: Config, + catalogClient: CatalogApi, refreshInterval: Duration | undefined = undefined, ): KubernetesClustersSupplier => { const clusterSuppliers = rootConfig @@ -46,6 +49,8 @@ export const getCombinedClusterSupplier = ( .map(clusterLocatorMethod => { const type = clusterLocatorMethod.getString('type'); switch (type) { + case 'catalog': + return CatalogClusterLocator.fromConfig(catalogClient); case 'localKubectlProxy': return new LocalKubectlProxyClusterLocator(); case 'config': diff --git a/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts b/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts index 309141ec0e..ed56c2da8a 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts @@ -71,8 +71,6 @@ describe('KubernetesBuilder', () => { getKubernetesObjectsByEntity: jest.fn(), } as any; - catalogApi = {} as CatalogApi; - const { router } = await KubernetesBuilder.createBuilder({ config, logger, diff --git a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts index 59bac4f30a..304885f3e2 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts @@ -186,7 +186,11 @@ export class KubernetesBuilder { refreshInterval: Duration, ): KubernetesClustersSupplier { const config = this.env.config; - return getCombinedClusterSupplier(config, refreshInterval); + return getCombinedClusterSupplier( + config, + this.env.catalogApi, + refreshInterval, + ); } protected buildObjectsProvider( diff --git a/plugins/kubernetes-backend/src/service/router.ts b/plugins/kubernetes-backend/src/service/router.ts index 6c26175058..ab27a75a18 100644 --- a/plugins/kubernetes-backend/src/service/router.ts +++ b/plugins/kubernetes-backend/src/service/router.ts @@ -19,6 +19,7 @@ import { Logger } from 'winston'; import { KubernetesClustersSupplier } from '../types/types'; import express from 'express'; import { KubernetesBuilder } from './KubernetesBuilder'; +import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { CatalogApi } from '@backstage/catalog-client'; /** @@ -30,6 +31,7 @@ export interface RouterOptions { config: Config; catalogApi: CatalogApi; clusterSupplier?: KubernetesClustersSupplier; + discovery: PluginEndpointDiscovery; } /** diff --git a/plugins/kubernetes-backend/src/service/standaloneApplication.ts b/plugins/kubernetes-backend/src/service/standaloneApplication.ts index c12d7be2bb..8234f89d48 100644 --- a/plugins/kubernetes-backend/src/service/standaloneApplication.ts +++ b/plugins/kubernetes-backend/src/service/standaloneApplication.ts @@ -39,6 +39,8 @@ export async function createStandaloneApplication( ): Promise { const { enableCors, logger } = options; const config = new ConfigReader({}); + const discovery = SingleHostDiscovery.fromConfig(config); + const app = express(); const catalogApi = new CatalogClient({ @@ -52,7 +54,7 @@ export async function createStandaloneApplication( app.use(compression()); app.use(express.json()); app.use(requestLoggingHandler()); - app.use('/', await createRouter({ logger, config, catalogApi })); + app.use('/', await createRouter({ logger, config, discovery, catalogApi })); app.use(notFoundHandler()); app.use(errorHandler());