From 22c1fc0f6d25edcac284381e3566a753b5b28988 Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Wed, 1 Jun 2022 20:06:59 -0700 Subject: [PATCH 01/24] feat: initial port of EKS Cluster Processor and CatalogClusterLocator into upstream Signed-off-by: Jonah Back --- .../src/processors/AwsEKSClusterProcessor.ts | 109 ++++++++++++++++++ .../catalog-backend-module-aws/src/types.ts | 20 ++++ plugins/kubernetes-backend/package.json | 2 + .../cluster-locator/CatalogClusterLocator.ts | 71 ++++++++++++ 4 files changed, 202 insertions(+) create mode 100644 plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts create mode 100644 plugins/catalog-backend-module-aws/src/types.ts create mode 100644 plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.ts 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..d74462693f --- /dev/null +++ b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts @@ -0,0 +1,109 @@ +/* + * 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 { Credentials, EKS } from 'aws-sdk'; +import { AWSCredentialFactory } from '../types'; + +const ACCOUNTID_ANNOTATION: string = 'amazonaws.com/account-id'; +const ARN_ANNOTATION: string = 'amazonaws.com/arn'; +const KUBERNETES_API_SERVER_ANNOTATION = 'kubernetes.io/api-server'; +const KUBERNETES_API_SERVER_CA_ANNOTATION = + 'kubernetes.io/api-server-certificate-authority'; +const KUBERNETES_AUTH_METHOD_ANNOTATION = 'kubernetes.io/auth-provider'; + +export class AwsEKSClusterProcessor implements CatalogProcessor { + private credentialsFactory?: AWSCredentialFactory; + + constructor(credentialsFactory?: AWSCredentialFactory) { + this.credentialsFactory = credentialsFactory; + } + + getProcessorName(): string { + return 'aws-eks'; + } + + normalizeName(name: string): string { + return name + .trim() + .toLocaleLowerCase() + .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('/'); + + 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 || '', + [KUBERNETES_API_SERVER_ANNOTATION]: + describedCluster.cluster.endpoint || '', + [KUBERNETES_API_SERVER_CA_ANNOTATION]: + describedCluster.cluster.certificateAuthority?.data || '', + [KUBERNETES_AUTH_METHOD_ANNOTATION]: '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/types.ts b/plugins/catalog-backend-module-aws/src/types.ts new file mode 100644 index 0000000000..88fdc067eb --- /dev/null +++ b/plugins/catalog-backend-module-aws/src/types.ts @@ -0,0 +1,20 @@ +/* + * 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'; + +export type AWSCredentialFactory = ( + awsAccountId: string, +) => Promise; diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index db732b8634..1926448635 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -37,8 +37,10 @@ "dependencies": { "@azure/identity": "^2.0.4", "@backstage/backend-common": "^0.13.6-next.1", + "@backstage/catalog-client": "^1.0.3-next.0", "@backstage/catalog-model": "^1.0.3-next.0", "@backstage/config": "^1.0.1", + "@backstage/core-plugin-api": "^1.0.3-next.0", "@backstage/errors": "^1.0.0", "@backstage/plugin-kubernetes-common": "^0.3.0-next.1", "@google-cloud/container": "^3.0.0", 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..1e474f637a --- /dev/null +++ b/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.ts @@ -0,0 +1,71 @@ +/* + * 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 { ClusterDetails, KubernetesClustersSupplier } from '../types/types'; +import { + CATALOG_FILTER_EXISTS, + CatalogClient, +} from '@backstage/catalog-client'; +import { DiscoveryApi } from '@backstage/core-plugin-api'; + +export class CatalogClusterLocator implements KubernetesClustersSupplier { + private catalogClient: CatalogClient; + + constructor(catalogClient: CatalogClient) { + this.catalogClient = catalogClient; + } + + static fromConfig(discoveryApi: DiscoveryApi): CatalogClusterLocator { + return new CatalogClusterLocator(new CatalogClient({ discoveryApi })); + } + + async getClusters(): Promise { + const clusters = await this.catalogClient.getEntities({ + filter: [ + { 'spec.type': 'kubernetes-cluster' }, + { + 'metadata.annotations.kubernetes.io/api-server': + CATALOG_FILTER_EXISTS, + }, + { + 'metadata.annotations.kubernetes.io/api-server-certificate-authority': + CATALOG_FILTER_EXISTS, + }, + { + 'metadata.annotations.kubernetes.io/auth-provider': + CATALOG_FILTER_EXISTS, + }, + ], + }); + return clusters.items.map(entity => { + const clusterDetails: ClusterDetails = { + name: entity.metadata.name, + // @ts-ignore filtered out by catalog-client query. + url: entity.metadata.annotations['kubernetes.io/api-server'], + // @ts-ignore filtered out by catalog-client query. + caData: + entity.metadata.annotations[ + 'kubernetes.io/api-server-certificate-authority' + ], + // @ts-ignore filtered out by catalog-client query. + authProvider: + entity.metadata.annotations['kubernetes.io/auth-provider'], + }; + + return clusterDetails; + }); + } +} From cb86da6467a5395511b5df4cff8e97a2c8889ae1 Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Mon, 20 Jun 2022 13:11:36 -0700 Subject: [PATCH 02/24] feat: add test and basic docs for catalog cluster locator Signed-off-by: Jonah Back --- docs/features/kubernetes/configuration.md | 5 ++ packages/backend/src/plugins/kubernetes.ts | 1 + .../CatalogClusterLocator.test.ts | 56 +++++++++++++++++++ .../cluster-locator/CatalogClusterLocator.ts | 20 +++---- .../src/cluster-locator/index.ts | 7 +++ .../src/service/KubernetesBuilder.ts | 8 ++- 6 files changed, 84 insertions(+), 13 deletions(-) create mode 100644 plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.test.ts diff --git a/docs/features/kubernetes/configuration.md b/docs/features/kubernetes/configuration.md index b78021c8f1..3009813961 100644 --- a/docs/features/kubernetes/configuration.md +++ b/docs/features/kubernetes/configuration.md @@ -57,12 +57,17 @@ This is an array used to determine where to retrieve cluster configuration from. Valid cluster locator methods are: +- [`catalog`](#catalog) - [`config`](#config) - [`gke`](#gke) - [custom `KubernetesClustersSupplier`](#custom-kubernetesclusterssupplier) #### `config` +This cluster locator method will read cluster information from the catalog. + +#### `config` + This cluster locator method will read cluster information from your app-config (see below). diff --git a/packages/backend/src/plugins/kubernetes.ts b/packages/backend/src/plugins/kubernetes.ts index 8023d549ff..376569a959 100644 --- a/packages/backend/src/plugins/kubernetes.ts +++ b/packages/backend/src/plugins/kubernetes.ts @@ -24,6 +24,7 @@ export default async function createPlugin( const { router } = await KubernetesBuilder.createBuilder({ logger: env.logger, config: env.config, + discovery: env.discovery, }).build(); return router; } 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 index 1e474f637a..c762dc6fb6 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Backstage Authors + * 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. @@ -15,21 +15,17 @@ */ import { ClusterDetails, KubernetesClustersSupplier } from '../types/types'; -import { - CATALOG_FILTER_EXISTS, - CatalogClient, -} from '@backstage/catalog-client'; -import { DiscoveryApi } from '@backstage/core-plugin-api'; +import { CATALOG_FILTER_EXISTS, CatalogApi } from '@backstage/catalog-client'; export class CatalogClusterLocator implements KubernetesClustersSupplier { - private catalogClient: CatalogClient; + private catalogClient: CatalogApi; - constructor(catalogClient: CatalogClient) { + constructor(catalogClient: CatalogApi) { this.catalogClient = catalogClient; } - static fromConfig(discoveryApi: DiscoveryApi): CatalogClusterLocator { - return new CatalogClusterLocator(new CatalogClient({ discoveryApi })); + static fromConfig(catalogApi: CatalogApi): CatalogClusterLocator { + return new CatalogClusterLocator(catalogApi); } async getClusters(): Promise { @@ -55,13 +51,13 @@ export class CatalogClusterLocator implements KubernetesClustersSupplier { name: entity.metadata.name, // @ts-ignore filtered out by catalog-client query. url: entity.metadata.annotations['kubernetes.io/api-server'], - // @ts-ignore filtered out by catalog-client query. caData: + // @ts-ignore filtered out by catalog-client query. entity.metadata.annotations[ 'kubernetes.io/api-server-certificate-authority' ], - // @ts-ignore filtered out by catalog-client query. authProvider: + // @ts-ignore filtered out by catalog-client query. entity.metadata.annotations['kubernetes.io/auth-provider'], }; diff --git a/plugins/kubernetes-backend/src/cluster-locator/index.ts b/plugins/kubernetes-backend/src/cluster-locator/index.ts index 53aeb44f8b..46f24f780b 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/index.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/index.ts @@ -19,6 +19,9 @@ import { Duration } from 'luxon'; import { ClusterDetails, KubernetesClustersSupplier } from '../types/types'; import { ConfigClusterLocator } from './ConfigClusterLocator'; import { GkeClusterLocator } from './GkeClusterLocator'; +import { CatalogClusterLocator } from './CatalogClusterLocator'; +import { PluginEndpointDiscovery } from '@backstage/backend-common/dist'; +import { CatalogClient } from '@backstage/catalog-client'; class CombinedClustersSupplier implements KubernetesClustersSupplier { constructor(readonly clusterSuppliers: KubernetesClustersSupplier[]) {} @@ -38,13 +41,17 @@ class CombinedClustersSupplier implements KubernetesClustersSupplier { export const getCombinedClusterSupplier = ( rootConfig: Config, + discovery: PluginEndpointDiscovery, refreshInterval: Duration | undefined = undefined, ): KubernetesClustersSupplier => { + const catalogClient = new CatalogClient({ discoveryApi: discovery }); const clusterSuppliers = rootConfig .getConfigArray('kubernetes.clusterLocatorMethods') .map(clusterLocatorMethod => { const type = clusterLocatorMethod.getString('type'); switch (type) { + case 'catalog': + return CatalogClusterLocator.fromConfig(catalogClient); case 'config': return ConfigClusterLocator.fromConfig(clusterLocatorMethod); case 'gke': diff --git a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts index 18c3fdc931..bd585c3f62 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts @@ -37,10 +37,12 @@ import { KubernetesFanOutHandler, } from './KubernetesFanOutHandler'; import { KubernetesClientBasedFetcher } from './KubernetesFetcher'; +import { PluginEndpointDiscovery } from '@backstage/backend-common/dist'; export interface KubernetesEnvironment { logger: Logger; config: Config; + discovery: PluginEndpointDiscovery; } /** @@ -171,7 +173,11 @@ export class KubernetesBuilder { refreshInterval: Duration, ): KubernetesClustersSupplier { const config = this.env.config; - return getCombinedClusterSupplier(config, refreshInterval); + return getCombinedClusterSupplier( + config, + this.env.discovery, + refreshInterval, + ); } protected buildObjectsProvider( From 1d2f6316306f64a4ab485c60076b3ca599a43ae4 Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Mon, 20 Jun 2022 14:34:48 -0700 Subject: [PATCH 03/24] feat: add test and basic docs for catalog cluster locator Signed-off-by: Jonah Back --- .../processors/AwsEKSClusterProcessor.test.ts | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.test.ts 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..19730a674e --- /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('AwsOrganizationCloudAccountProcessor', () => { + 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', + }, + }, + }); + }); + }); +}); From 151aeea845610e243656a374fb12697ccf1893cb Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Mon, 20 Jun 2022 14:51:37 -0700 Subject: [PATCH 04/24] feat: add test and basic docs for catalog cluster locator Signed-off-by: Jonah Back --- .../src/cluster-locator/index.test.ts | 13 ++++++++++-- .../src/service/KubernetesBuilder.test.ts | 20 +++++++++++++++++-- .../kubernetes-backend/src/service/router.ts | 2 ++ .../src/service/standaloneApplication.ts | 5 ++++- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/plugins/kubernetes-backend/src/cluster-locator/index.test.ts b/plugins/kubernetes-backend/src/cluster-locator/index.test.ts index cc7e7d07d6..475e59e503 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/index.test.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/index.test.ts @@ -16,6 +16,7 @@ import { Config, ConfigReader } from '@backstage/config'; import { getCombinedClusterSupplier } from './index'; +import { PluginEndpointDiscovery } from '@backstage/backend-common/dist'; describe('getCombinedClusterSupplier', () => { it('should retrieve cluster details from config', async () => { @@ -44,8 +45,12 @@ describe('getCombinedClusterSupplier', () => { }, 'ctx', ); + const discovery: PluginEndpointDiscovery = { + getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'), + getExternalBaseUrl: jest.fn(), + }; - const clusterSupplier = getCombinedClusterSupplier(config); + const clusterSupplier = getCombinedClusterSupplier(config, discovery); const result = await clusterSupplier.getClusters(); expect(result).toStrictEqual([ @@ -99,8 +104,12 @@ describe('getCombinedClusterSupplier', () => { }, 'ctx', ); + const discovery: PluginEndpointDiscovery = { + getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'), + getExternalBaseUrl: jest.fn(), + }; - expect(() => getCombinedClusterSupplier(config)).toThrowError( + expect(() => getCombinedClusterSupplier(config, discovery)).toThrowError( new Error('Unsupported kubernetes.clusterLocatorMethods: "magic"'), ); }); diff --git a/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts b/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts index 37dad8c3e4..fd130a30bf 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts @@ -14,7 +14,10 @@ * limitations under the License. */ -import { getVoidLogger } from '@backstage/backend-common'; +import { + getVoidLogger, + PluginEndpointDiscovery, +} from '@backstage/backend-common'; import { Config, ConfigReader } from '@backstage/config'; import { ObjectsByEntityResponse } from '@backstage/plugin-kubernetes-common'; import express from 'express'; @@ -44,6 +47,10 @@ describe('KubernetesBuilder', () => { clusterLocatorMethods: [{ type: 'config', clusters: [] }], }, }); + const discovery: PluginEndpointDiscovery = { + getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'), + getExternalBaseUrl: jest.fn(), + }; const clusters: ClusterDetails[] = [ { @@ -68,7 +75,11 @@ describe('KubernetesBuilder', () => { getKubernetesObjectsByEntity: jest.fn(), } as any; - const { router } = await KubernetesBuilder.createBuilder({ config, logger }) + const { router } = await KubernetesBuilder.createBuilder({ + config, + logger, + discovery, + }) .setObjectsProvider(kubernetesFanOutHandler) .setClusterSupplier(clusterSupplier) .build(); @@ -164,6 +175,10 @@ describe('KubernetesBuilder', () => { it('custom service locator', async () => { const logger = getVoidLogger(); + const discovery: PluginEndpointDiscovery = { + getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'), + getExternalBaseUrl: jest.fn(), + }; const someCluster: ClusterDetails = { name: 'some-cluster', authProvider: 'serviceAccount', @@ -237,6 +252,7 @@ describe('KubernetesBuilder', () => { const { router } = await KubernetesBuilder.createBuilder({ logger, config, + discovery, }) .setClusterSupplier(clusterSupplier) .setServiceLocator(serviceLocator) diff --git a/plugins/kubernetes-backend/src/service/router.ts b/plugins/kubernetes-backend/src/service/router.ts index 61082267be..d00dc5d034 100644 --- a/plugins/kubernetes-backend/src/service/router.ts +++ b/plugins/kubernetes-backend/src/service/router.ts @@ -19,11 +19,13 @@ import { Logger } from 'winston'; import { KubernetesClustersSupplier } from '../types/types'; import express from 'express'; import { KubernetesBuilder } from './KubernetesBuilder'; +import { PluginEndpointDiscovery } from '@backstage/backend-common/dist'; export interface RouterOptions { logger: Logger; config: Config; clusterSupplier?: KubernetesClustersSupplier; + discovery: PluginEndpointDiscovery; } /** diff --git a/plugins/kubernetes-backend/src/service/standaloneApplication.ts b/plugins/kubernetes-backend/src/service/standaloneApplication.ts index 29262744e5..5c14c22b1d 100644 --- a/plugins/kubernetes-backend/src/service/standaloneApplication.ts +++ b/plugins/kubernetes-backend/src/service/standaloneApplication.ts @@ -18,6 +18,7 @@ import { errorHandler, notFoundHandler, requestLoggingHandler, + SingleHostDiscovery, } from '@backstage/backend-common'; import compression from 'compression'; import cors from 'cors'; @@ -37,6 +38,8 @@ export async function createStandaloneApplication( ): Promise { const { enableCors, logger } = options; const config = new ConfigReader({}); + const discovery = SingleHostDiscovery.fromConfig(config); + const app = express(); app.use(helmet()); @@ -46,7 +49,7 @@ export async function createStandaloneApplication( app.use(compression()); app.use(express.json()); app.use(requestLoggingHandler()); - app.use('/', await createRouter({ logger, config })); + app.use('/', await createRouter({ logger, config, discovery })); app.use(notFoundHandler()); app.use(errorHandler()); From 215bd5a6ff31da22ae6b0d1c0a49d97f6a03d21f Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Mon, 20 Jun 2022 15:06:33 -0700 Subject: [PATCH 05/24] feat: add test and basic docs for catalog cluster locator Signed-off-by: Jonah Back --- plugins/kubernetes-backend/src/service/router.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/kubernetes-backend/src/service/router.ts b/plugins/kubernetes-backend/src/service/router.ts index cbf3ee3b68..95814a79a3 100644 --- a/plugins/kubernetes-backend/src/service/router.ts +++ b/plugins/kubernetes-backend/src/service/router.ts @@ -19,7 +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/dist'; +import { PluginEndpointDiscovery } from '@backstage/backend-common'; /** * From f1017c3bc482e1c79882df1890dff71bce4729c5 Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Mon, 20 Jun 2022 15:07:10 -0700 Subject: [PATCH 06/24] feat: add test and basic docs for catalog cluster locator Signed-off-by: Jonah Back --- plugins/kubernetes-backend/src/cluster-locator/index.test.ts | 2 +- plugins/kubernetes-backend/src/cluster-locator/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/kubernetes-backend/src/cluster-locator/index.test.ts b/plugins/kubernetes-backend/src/cluster-locator/index.test.ts index 475e59e503..1ba533ccd4 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/index.test.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/index.test.ts @@ -16,7 +16,7 @@ import { Config, ConfigReader } from '@backstage/config'; import { getCombinedClusterSupplier } from './index'; -import { PluginEndpointDiscovery } from '@backstage/backend-common/dist'; +import { PluginEndpointDiscovery } from '@backstage/backend-common'; describe('getCombinedClusterSupplier', () => { it('should retrieve cluster details from config', async () => { diff --git a/plugins/kubernetes-backend/src/cluster-locator/index.ts b/plugins/kubernetes-backend/src/cluster-locator/index.ts index 46f24f780b..200d064514 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/index.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/index.ts @@ -20,7 +20,7 @@ import { ClusterDetails, KubernetesClustersSupplier } from '../types/types'; import { ConfigClusterLocator } from './ConfigClusterLocator'; import { GkeClusterLocator } from './GkeClusterLocator'; import { CatalogClusterLocator } from './CatalogClusterLocator'; -import { PluginEndpointDiscovery } from '@backstage/backend-common/dist'; +import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { CatalogClient } from '@backstage/catalog-client'; class CombinedClustersSupplier implements KubernetesClustersSupplier { From 78baf6f9eb9408a60a78f91494e59cb566735137 Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Mon, 20 Jun 2022 15:13:08 -0700 Subject: [PATCH 07/24] feat: add test and basic docs for catalog cluster locator Signed-off-by: Jonah Back --- plugins/kubernetes-backend/src/service/KubernetesBuilder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts index bf216a8a35..50ecfab4bc 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts @@ -37,7 +37,7 @@ import { KubernetesFanOutHandler, } from './KubernetesFanOutHandler'; import { KubernetesClientBasedFetcher } from './KubernetesFetcher'; -import { PluginEndpointDiscovery } from '@backstage/backend-common/dist'; +import { PluginEndpointDiscovery } from '@backstage/backend-common'; /** * From 746ec700ea4d23393a6441979883bfd5536afc98 Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Mon, 20 Jun 2022 15:14:41 -0700 Subject: [PATCH 08/24] feat: add test and basic docs for catalog cluster locator Signed-off-by: Jonah Back --- .changeset/plenty-books-peel.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/plenty-books-peel.md diff --git a/.changeset/plenty-books-peel.md b/.changeset/plenty-books-peel.md new file mode 100644 index 0000000000..6efd32bc36 --- /dev/null +++ b/.changeset/plenty-books-peel.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-catalog-backend-module-aws': minor +'@backstage/plugin-kubernetes-backend': minor +--- + +Add support for Kubernetes clusters in the catalog. From 9462a2ce8daeff316bea1dbd8a668f1501342154 Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Mon, 20 Jun 2022 15:28:48 -0700 Subject: [PATCH 09/24] feat: add test and basic docs for catalog cluster locator Signed-off-by: Jonah Back --- plugins/kubernetes-backend/api-report.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/kubernetes-backend/api-report.md b/plugins/kubernetes-backend/api-report.md index 41748f240b..9aa1827a71 100644 --- a/plugins/kubernetes-backend/api-report.md +++ b/plugins/kubernetes-backend/api-report.md @@ -14,6 +14,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) @@ -158,6 +159,8 @@ export interface KubernetesEnvironment { // (undocumented) config: Config; // (undocumented) + discovery: PluginEndpointDiscovery; + // (undocumented) logger: Logger; } @@ -272,6 +275,8 @@ export interface RouterOptions { // (undocumented) config: Config; // (undocumented) + discovery: PluginEndpointDiscovery; + // (undocumented) logger: Logger; } From c8764b7fd7212541dc3cb1513b94750ad771fc88 Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Tue, 21 Jun 2022 15:40:04 -0700 Subject: [PATCH 10/24] address a few code review comments Signed-off-by: Jonah Back --- .changeset/plenty-books-peel.md | 12 +++++++++++- .../src/processors/AwsEKSClusterProcessor.test.ts | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.changeset/plenty-books-peel.md b/.changeset/plenty-books-peel.md index 6efd32bc36..3f8e25c943 100644 --- a/.changeset/plenty-books-peel.md +++ b/.changeset/plenty-books-peel.md @@ -1,6 +1,16 @@ --- -'@backstage/plugin-catalog-backend-module-aws': minor '@backstage/plugin-kubernetes-backend': minor --- Add support for Kubernetes clusters in the catalog. + +The KubernetesBuilder.createBuilder method now requires an additional field, +discovery. To update your backend, you will want to do something like the following: + +```javascript +KubernetesBuilder.createBuilder({ + config: env.config, + logger: env.config, + discovery: env.discovery, + }) +``` diff --git a/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.test.ts b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.test.ts index 19730a674e..fc44f5f912 100644 --- a/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.test.ts +++ b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.test.ts @@ -18,7 +18,7 @@ import { AwsEKSClusterProcessor } from './AwsEKSClusterProcessor'; import AWSMock from 'aws-sdk-mock'; import aws from 'aws-sdk'; -describe('AwsOrganizationCloudAccountProcessor', () => { +describe('AwsEKSClusterProcessor', () => { AWSMock.setSDKInstance(aws); describe('readLocation', () => { const processor = new (AwsEKSClusterProcessor as any)(); From 6a4c15fb59439356638f608a701efaad75a03f21 Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Sun, 26 Jun 2022 13:18:53 -0700 Subject: [PATCH 11/24] address review comments Signed-off-by: Jonah Back --- .../catalog-model/src/entity/constants.ts | 23 +++++++++++++++++++ packages/catalog-model/src/entity/index.ts | 3 +++ .../src/processors/AwsEKSClusterProcessor.ts | 15 ++++++------ .../cluster-locator/CatalogClusterLocator.ts | 23 ++++++++----------- 4 files changed, 44 insertions(+), 20 deletions(-) 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 45ee673167..7aa5424e5b 100644 --- a/packages/catalog-model/src/entity/index.ts +++ b/packages/catalog-model/src/entity/index.ts @@ -18,6 +18,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/src/processors/AwsEKSClusterProcessor.ts b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts index d74462693f..ca45b28f5d 100644 --- a/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts +++ b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts @@ -18,15 +18,16 @@ import { 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'; -const KUBERNETES_API_SERVER_ANNOTATION = 'kubernetes.io/api-server'; -const KUBERNETES_API_SERVER_CA_ANNOTATION = - 'kubernetes.io/api-server-certificate-authority'; -const KUBERNETES_AUTH_METHOD_ANNOTATION = 'kubernetes.io/auth-provider'; export class AwsEKSClusterProcessor implements CatalogProcessor { private credentialsFactory?: AWSCredentialFactory; @@ -82,11 +83,11 @@ export class AwsEKSClusterProcessor implements CatalogProcessor { annotations: { [ACCOUNTID_ANNOTATION]: accountId, [ARN_ANNOTATION]: describedCluster.cluster.arn || '', - [KUBERNETES_API_SERVER_ANNOTATION]: + [ANNOTATION_KUBERNETES_API_SERVER]: describedCluster.cluster.endpoint || '', - [KUBERNETES_API_SERVER_CA_ANNOTATION]: + [ANNOTATION_KUBERNETES_API_SERVER_CA]: describedCluster.cluster.certificateAuthority?.data || '', - [KUBERNETES_AUTH_METHOD_ANNOTATION]: 'aws', + [ANNOTATION_KUBERNETES_AUTH_PROVIDER]: 'aws', }, name: this.normalizeName(describedCluster.cluster.name as string), namespace: 'default', diff --git a/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.ts b/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.ts index c762dc6fb6..124a53a72a 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.ts @@ -16,6 +16,11 @@ 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; @@ -31,16 +36,13 @@ export class CatalogClusterLocator implements KubernetesClustersSupplier { async getClusters(): Promise { const clusters = await this.catalogClient.getEntities({ filter: [ - { 'spec.type': 'kubernetes-cluster' }, { + kind: 'Resource', + 'spec.type': 'kubernetes-cluster', 'metadata.annotations.kubernetes.io/api-server': CATALOG_FILTER_EXISTS, - }, - { 'metadata.annotations.kubernetes.io/api-server-certificate-authority': CATALOG_FILTER_EXISTS, - }, - { 'metadata.annotations.kubernetes.io/auth-provider': CATALOG_FILTER_EXISTS, }, @@ -49,16 +51,11 @@ export class CatalogClusterLocator implements KubernetesClustersSupplier { return clusters.items.map(entity => { const clusterDetails: ClusterDetails = { name: entity.metadata.name, - // @ts-ignore filtered out by catalog-client query. - url: entity.metadata.annotations['kubernetes.io/api-server'], + url: entity.metadata.annotations![ANNOTATION_KUBERNETES_API_SERVER]!, caData: - // @ts-ignore filtered out by catalog-client query. - entity.metadata.annotations[ - 'kubernetes.io/api-server-certificate-authority' - ], + entity.metadata.annotations![ANNOTATION_KUBERNETES_API_SERVER_CA]!, authProvider: - // @ts-ignore filtered out by catalog-client query. - entity.metadata.annotations['kubernetes.io/auth-provider'], + entity.metadata.annotations![ANNOTATION_KUBERNETES_AUTH_PROVIDER]!, }; return clusterDetails; From 7365cccad264a831e6088e4e3c90e3ad51bcc432 Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Sun, 26 Jun 2022 13:22:53 -0700 Subject: [PATCH 12/24] address review comments Signed-off-by: Jonah Back --- packages/catalog-model/api-report.md | 11 +++++++++++ .../src/processors/AwsEKSClusterProcessor.ts | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/catalog-model/api-report.md b/packages/catalog-model/api-report.md index 8427ef4112..6cfc49532f 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/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts index ca45b28f5d..c85531e944 100644 --- a/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts +++ b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts @@ -32,8 +32,8 @@ const ARN_ANNOTATION: string = 'amazonaws.com/arn'; export class AwsEKSClusterProcessor implements CatalogProcessor { private credentialsFactory?: AWSCredentialFactory; - constructor(credentialsFactory?: AWSCredentialFactory) { - this.credentialsFactory = credentialsFactory; + constructor(options: { credentialsFactory?: AWSCredentialFactory }) { + this.credentialsFactory = options.credentialsFactory; } getProcessorName(): string { From 5151daee5e652b7b8a4706def62ccf648d1811dc Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Sun, 26 Jun 2022 13:35:03 -0700 Subject: [PATCH 13/24] address review comments Signed-off-by: Jonah Back --- .../catalog-backend-module-aws/api-report.md | 21 +++++++++++++++++++ .../catalog-backend-module-aws/src/index.ts | 1 + .../src/processors/AwsEKSClusterProcessor.ts | 7 +++++++ .../src/processors/index.ts | 1 + .../catalog-backend-module-aws/src/types.ts | 5 +++++ 5 files changed, 35 insertions(+) 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.ts b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts index c85531e944..620f3951df 100644 --- a/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts +++ b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts @@ -29,6 +29,13 @@ 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; 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 index 88fdc067eb..3d9df38181 100644 --- a/plugins/catalog-backend-module-aws/src/types.ts +++ b/plugins/catalog-backend-module-aws/src/types.ts @@ -15,6 +15,11 @@ */ 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; From 09591303cd3fcd36156201c7a572fd68635c6726 Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Sun, 26 Jun 2022 13:35:53 -0700 Subject: [PATCH 14/24] address review comments Signed-off-by: Jonah Back --- .../src/processors/AwsEKSClusterProcessor.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.test.ts b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.test.ts index fc44f5f912..3e33ba740c 100644 --- a/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.test.ts +++ b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.test.ts @@ -21,7 +21,7 @@ import aws from 'aws-sdk'; describe('AwsEKSClusterProcessor', () => { AWSMock.setSDKInstance(aws); describe('readLocation', () => { - const processor = new (AwsEKSClusterProcessor as any)(); + 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 () => { From 7162fb90685ec51b9f9fcc30895223317cb16325 Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Sun, 26 Jun 2022 13:40:03 -0700 Subject: [PATCH 15/24] address review comments Signed-off-by: Jonah Back --- docs/features/kubernetes/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/kubernetes/configuration.md b/docs/features/kubernetes/configuration.md index 3009813961..a870014f19 100644 --- a/docs/features/kubernetes/configuration.md +++ b/docs/features/kubernetes/configuration.md @@ -62,7 +62,7 @@ Valid cluster locator methods are: - [`gke`](#gke) - [custom `KubernetesClustersSupplier`](#custom-kubernetesclusterssupplier) -#### `config` +#### `catalog` This cluster locator method will read cluster information from the catalog. From bbf02032b5c069900cf18cad2870e16b67ae8ac1 Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Sun, 26 Jun 2022 14:51:47 -0700 Subject: [PATCH 16/24] address review comments Signed-off-by: Jonah Back --- .changeset/plenty-books-peel.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.changeset/plenty-books-peel.md b/.changeset/plenty-books-peel.md index 3f8e25c943..f258c4d7d5 100644 --- a/.changeset/plenty-books-peel.md +++ b/.changeset/plenty-books-peel.md @@ -1,5 +1,5 @@ --- -'@backstage/plugin-kubernetes-backend': minor +'@backstage/plugin-kubernetes-backend': patch --- Add support for Kubernetes clusters in the catalog. @@ -9,8 +9,8 @@ discovery. To update your backend, you will want to do something like the follow ```javascript KubernetesBuilder.createBuilder({ - config: env.config, - logger: env.config, - discovery: env.discovery, - }) + config: env.config, + logger: env.config, + discovery: env.discovery, +}); ``` From f9f1de8100a8cc0f63afe9318605890f878fffca Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Sun, 26 Jun 2022 14:55:59 -0700 Subject: [PATCH 17/24] address review comments Signed-off-by: Jonah Back --- .changeset/eighty-comics-divide.md | 5 +++++ .changeset/itchy-stingrays-greet.md | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .changeset/eighty-comics-divide.md create mode 100644 .changeset/itchy-stingrays-greet.md 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 From 40de11b8f5f77d139b9c87b7213d4be49fc5ca9e Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Tue, 5 Jul 2022 10:17:20 -0700 Subject: [PATCH 18/24] address review comments Signed-off-by: Jonah Back --- .../cluster-locator/CatalogClusterLocator.ts | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.ts b/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.ts index 124a53a72a..03fa347e5f 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.ts @@ -34,19 +34,21 @@ export class CatalogClusterLocator implements KubernetesClustersSupplier { } 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', + }; + + filter[apiServerKey] = CATALOG_FILTER_EXISTS; + filter[apiServerCaKey] = CATALOG_FILTER_EXISTS; + filter[authProviderKey] = CATALOG_FILTER_EXISTS; + const clusters = await this.catalogClient.getEntities({ - filter: [ - { - kind: 'Resource', - 'spec.type': 'kubernetes-cluster', - 'metadata.annotations.kubernetes.io/api-server': - CATALOG_FILTER_EXISTS, - 'metadata.annotations.kubernetes.io/api-server-certificate-authority': - CATALOG_FILTER_EXISTS, - 'metadata.annotations.kubernetes.io/auth-provider': - CATALOG_FILTER_EXISTS, - }, - ], + filter: [filter], }); return clusters.items.map(entity => { const clusterDetails: ClusterDetails = { From f6216b850e7477018535f331e7f9c59df34b0c96 Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Tue, 5 Jul 2022 10:19:46 -0700 Subject: [PATCH 19/24] address review comments Signed-off-by: Jonah Back --- .../src/processors/AwsEKSClusterProcessor.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts index 620f3951df..c34b247f4d 100644 --- a/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts +++ b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts @@ -50,7 +50,7 @@ export class AwsEKSClusterProcessor implements CatalogProcessor { normalizeName(name: string): string { return name .trim() - .toLocaleLowerCase() + .toLocaleLowerCase('en-US') .replace(/[^a-zA-Z0-9\-]/g, '-'); } @@ -66,6 +66,12 @@ export class AwsEKSClusterProcessor implements CatalogProcessor { // 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) { From fb41605d79ceb61a373af0532126ae46e768888a Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Sun, 10 Jul 2022 20:10:04 -0700 Subject: [PATCH 20/24] address review comments Signed-off-by: Jonah Back --- packages/backend/src/plugins/kubernetes.ts | 1 - plugins/kubernetes-backend/package.json | 1 - .../src/cluster-locator/CatalogClusterLocator.ts | 7 +++---- .../src/cluster-locator/index.test.ts | 16 +++++----------- .../src/cluster-locator/index.ts | 6 ++---- .../src/service/KubernetesBuilder.test.ts | 15 +-------------- .../src/service/KubernetesBuilder.ts | 4 +--- 7 files changed, 12 insertions(+), 38 deletions(-) diff --git a/packages/backend/src/plugins/kubernetes.ts b/packages/backend/src/plugins/kubernetes.ts index b089b20405..3bc7a5dcbe 100644 --- a/packages/backend/src/plugins/kubernetes.ts +++ b/packages/backend/src/plugins/kubernetes.ts @@ -26,7 +26,6 @@ export default async function createPlugin( const { router } = await KubernetesBuilder.createBuilder({ logger: env.logger, config: env.config, - discovery: env.discovery, catalogApi, }).build(); return router; diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 002575e25d..10eb6828f5 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -40,7 +40,6 @@ "@backstage/catalog-client": "^1.0.4-next.1", "@backstage/catalog-model": "^1.1.0-next.2", "@backstage/config": "^1.0.1", - "@backstage/core-plugin-api": "^1.0.3-next.0", "@backstage/errors": "^1.1.0-next.0", "@backstage/plugin-auth-node": "^0.2.3-next.1", "@backstage/plugin-kubernetes-common": "^0.4.0-next.0", diff --git a/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.ts b/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.ts index 03fa347e5f..7183b71831 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/CatalogClusterLocator.ts @@ -41,12 +41,11 @@ export class CatalogClusterLocator implements KubernetesClustersSupplier { const filter: Record = { kind: 'Resource', 'spec.type': 'kubernetes-cluster', + [apiServerKey]: CATALOG_FILTER_EXISTS, + [apiServerCaKey]: CATALOG_FILTER_EXISTS, + [authProviderKey]: CATALOG_FILTER_EXISTS, }; - filter[apiServerKey] = CATALOG_FILTER_EXISTS; - filter[apiServerCaKey] = CATALOG_FILTER_EXISTS; - filter[authProviderKey] = CATALOG_FILTER_EXISTS; - const clusters = await this.catalogClient.getEntities({ filter: [filter], }); diff --git a/plugins/kubernetes-backend/src/cluster-locator/index.test.ts b/plugins/kubernetes-backend/src/cluster-locator/index.test.ts index 1ba533ccd4..f494fbbf2b 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/index.test.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/index.test.ts @@ -16,9 +16,11 @@ import { Config, ConfigReader } from '@backstage/config'; import { getCombinedClusterSupplier } from './index'; -import { PluginEndpointDiscovery } from '@backstage/backend-common'; +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,12 +47,8 @@ describe('getCombinedClusterSupplier', () => { }, 'ctx', ); - const discovery: PluginEndpointDiscovery = { - getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'), - getExternalBaseUrl: jest.fn(), - }; - const clusterSupplier = getCombinedClusterSupplier(config, discovery); + const clusterSupplier = getCombinedClusterSupplier(config, catalogApi); const result = await clusterSupplier.getClusters(); expect(result).toStrictEqual([ @@ -104,12 +102,8 @@ describe('getCombinedClusterSupplier', () => { }, 'ctx', ); - const discovery: PluginEndpointDiscovery = { - getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'), - getExternalBaseUrl: jest.fn(), - }; - expect(() => getCombinedClusterSupplier(config, discovery)).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 200d064514..b55dfe5a35 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/index.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/index.ts @@ -20,8 +20,7 @@ import { ClusterDetails, KubernetesClustersSupplier } from '../types/types'; import { ConfigClusterLocator } from './ConfigClusterLocator'; import { GkeClusterLocator } from './GkeClusterLocator'; import { CatalogClusterLocator } from './CatalogClusterLocator'; -import { PluginEndpointDiscovery } from '@backstage/backend-common'; -import { CatalogClient } from '@backstage/catalog-client'; +import { CatalogApi } from '@backstage/catalog-client'; class CombinedClustersSupplier implements KubernetesClustersSupplier { constructor(readonly clusterSuppliers: KubernetesClustersSupplier[]) {} @@ -41,10 +40,9 @@ class CombinedClustersSupplier implements KubernetesClustersSupplier { export const getCombinedClusterSupplier = ( rootConfig: Config, - discovery: PluginEndpointDiscovery, + catalogClient: CatalogApi, refreshInterval: Duration | undefined = undefined, ): KubernetesClustersSupplier => { - const catalogClient = new CatalogClient({ discoveryApi: discovery }); const clusterSuppliers = rootConfig .getConfigArray('kubernetes.clusterLocatorMethods') .map(clusterLocatorMethod => { diff --git a/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts b/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts index c39a72808f..ed56c2da8a 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts @@ -14,10 +14,7 @@ * limitations under the License. */ -import { - getVoidLogger, - PluginEndpointDiscovery, -} from '@backstage/backend-common'; +import { getVoidLogger } from '@backstage/backend-common'; import { Entity } from '@backstage/catalog-model'; import { Config, ConfigReader } from '@backstage/config'; import { ObjectsByEntityResponse } from '@backstage/plugin-kubernetes-common'; @@ -50,10 +47,6 @@ describe('KubernetesBuilder', () => { clusterLocatorMethods: [{ type: 'config', clusters: [] }], }, }); - const discovery: PluginEndpointDiscovery = { - getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'), - getExternalBaseUrl: jest.fn(), - }; const clusters: ClusterDetails[] = [ { @@ -81,7 +74,6 @@ describe('KubernetesBuilder', () => { const { router } = await KubernetesBuilder.createBuilder({ config, logger, - discovery, catalogApi, }) .setObjectsProvider(kubernetesFanOutHandler) @@ -179,10 +171,6 @@ describe('KubernetesBuilder', () => { it('custom service locator', async () => { const logger = getVoidLogger(); - const discovery: PluginEndpointDiscovery = { - getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'), - getExternalBaseUrl: jest.fn(), - }; const someCluster: ClusterDetails = { name: 'some-cluster', authProvider: 'serviceAccount', @@ -258,7 +246,6 @@ describe('KubernetesBuilder', () => { const { router } = await KubernetesBuilder.createBuilder({ logger, config, - discovery, catalogApi, }) .setClusterSupplier(clusterSupplier) diff --git a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts index 4d7eb3d6f4..304885f3e2 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts @@ -37,7 +37,6 @@ import { KubernetesFanOutHandler, } from './KubernetesFanOutHandler'; import { KubernetesClientBasedFetcher } from './KubernetesFetcher'; -import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { addResourceRoutesToRouter } from '../routes/resourcesRoutes'; import { CatalogApi } from '@backstage/catalog-client'; @@ -48,7 +47,6 @@ import { CatalogApi } from '@backstage/catalog-client'; export interface KubernetesEnvironment { logger: Logger; config: Config; - discovery: PluginEndpointDiscovery; catalogApi: CatalogApi; } @@ -190,7 +188,7 @@ export class KubernetesBuilder { const config = this.env.config; return getCombinedClusterSupplier( config, - this.env.discovery, + this.env.catalogApi, refreshInterval, ); } From 58ca051854d33bda75c6a7a2d5ec222dcbeda25d Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Sun, 10 Jul 2022 20:13:26 -0700 Subject: [PATCH 21/24] amend changeset to remove api change since CatalogApi was added in master already as a dependency Signed-off-by: Jonah Back --- .changeset/plenty-books-peel.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.changeset/plenty-books-peel.md b/.changeset/plenty-books-peel.md index f258c4d7d5..423c6f8bd4 100644 --- a/.changeset/plenty-books-peel.md +++ b/.changeset/plenty-books-peel.md @@ -3,14 +3,3 @@ --- Add support for Kubernetes clusters in the catalog. - -The KubernetesBuilder.createBuilder method now requires an additional field, -discovery. To update your backend, you will want to do something like the following: - -```javascript -KubernetesBuilder.createBuilder({ - config: env.config, - logger: env.config, - discovery: env.discovery, -}); -``` From fe5a25ecab63f058564122845ec00ba3a8184ae8 Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Sun, 10 Jul 2022 20:14:25 -0700 Subject: [PATCH 22/24] address review comments Signed-off-by: Jonah Back --- docs/features/kubernetes/configuration.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/features/kubernetes/configuration.md b/docs/features/kubernetes/configuration.md index 8df2be7db2..dce4e60a87 100644 --- a/docs/features/kubernetes/configuration.md +++ b/docs/features/kubernetes/configuration.md @@ -73,8 +73,6 @@ This cluster locator method will assume a locally running [`kubectl proxy`](http NOTE: This cluster locator method is for local development only and should not be used in production. -> > > > > > > master - #### `config` This cluster locator method will read cluster information from your app-config From 9bfddd0dd637983acf38f7b9c4afc70b0e717ada Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Sun, 10 Jul 2022 20:45:07 -0700 Subject: [PATCH 23/24] address review comments Signed-off-by: Jonah Back --- plugins/kubernetes-backend/api-report.md | 2 -- yarn.lock | 26 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/plugins/kubernetes-backend/api-report.md b/plugins/kubernetes-backend/api-report.md index 489098a91e..15b1cef853 100644 --- a/plugins/kubernetes-backend/api-report.md +++ b/plugins/kubernetes-backend/api-report.md @@ -163,8 +163,6 @@ export interface KubernetesEnvironment { // (undocumented) config: Config; // (undocumented) - discovery: PluginEndpointDiscovery; - // (undocumented) logger: Logger; } diff --git a/yarn.lock b/yarn.lock index e52a56240e..1f2901432a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18689,6 +18689,32 @@ msw@^0.39.2: type-fest "^1.2.2" yargs "^17.3.1" +msw@^0.42.0: + version "0.42.3" + resolved "https://registry.npmjs.org/msw/-/msw-0.42.3.tgz#150c475e2cb6d53c67503bd0e3f6251bfd075328" + integrity sha512-zrKBIGCDsNUCZLd3DLSeUtRruZ0riwJgORg9/bSDw3D0PTI8XUGAK3nC0LJA9g0rChGuKaWK/SwObA8wpFrz4g== + dependencies: + "@mswjs/cookies" "^0.2.0" + "@mswjs/interceptors" "^0.16.3" + "@open-draft/until" "^1.0.3" + "@types/cookie" "^0.4.1" + "@types/js-levenshtein" "^1.1.1" + chalk "4.1.1" + chokidar "^3.4.2" + cookie "^0.4.2" + graphql "^16.3.0" + headers-polyfill "^3.0.4" + inquirer "^8.2.0" + is-node-process "^1.0.1" + js-levenshtein "^1.1.6" + node-fetch "^2.6.7" + outvariant "^1.3.0" + path-to-regexp "^6.2.0" + statuses "^2.0.0" + strict-event-emitter "^0.2.0" + type-fest "^1.2.2" + yargs "^17.3.1" + msw@^0.43.0: version "0.43.1" resolved "https://registry.npmjs.org/msw/-/msw-0.43.1.tgz#57cb4af56f07442e8a6d14d76032a0ab41434256" From 677df66b48d20b3c90d1400b142956b6d2c31ec0 Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Wed, 13 Jul 2022 09:52:15 -0700 Subject: [PATCH 24/24] Fix Yarnfile Signed-off-by: Jonah Back --- yarn.lock | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/yarn.lock b/yarn.lock index 05f25a8985..d42193b56f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18764,43 +18764,10 @@ msw@^0.39.2: type-fest "^1.2.2" yargs "^17.3.1" -<<<<<<< HEAD -msw@^0.42.0: - version "0.42.3" - resolved "https://registry.npmjs.org/msw/-/msw-0.42.3.tgz#150c475e2cb6d53c67503bd0e3f6251bfd075328" - integrity sha512-zrKBIGCDsNUCZLd3DLSeUtRruZ0riwJgORg9/bSDw3D0PTI8XUGAK3nC0LJA9g0rChGuKaWK/SwObA8wpFrz4g== - dependencies: - "@mswjs/cookies" "^0.2.0" - "@mswjs/interceptors" "^0.16.3" - "@open-draft/until" "^1.0.3" - "@types/cookie" "^0.4.1" - "@types/js-levenshtein" "^1.1.1" - chalk "4.1.1" - chokidar "^3.4.2" - cookie "^0.4.2" - graphql "^16.3.0" - headers-polyfill "^3.0.4" - inquirer "^8.2.0" - is-node-process "^1.0.1" - js-levenshtein "^1.1.6" - node-fetch "^2.6.7" - outvariant "^1.3.0" - path-to-regexp "^6.2.0" - statuses "^2.0.0" - strict-event-emitter "^0.2.0" - type-fest "^1.2.2" - yargs "^17.3.1" - -msw@^0.43.0: - version "0.43.1" - resolved "https://registry.npmjs.org/msw/-/msw-0.43.1.tgz#57cb4af56f07442e8a6d14d76032a0ab41434256" - integrity sha512-wzhPpL6RsiYkyIUlTCg0aZY0aRZa4Eiubd6MOA5oJVgfuapDmvZrI8OMi4h4e+fpHD+Qsy+4unAjv3wpWia5yw== -======= msw@^0.44.0: version "0.44.0" resolved "https://registry.npmjs.org/msw/-/msw-0.44.0.tgz#28f81a789f05855e5403a399d7eefaa2d0216adf" integrity sha512-LLXfLos7JwzI1bfh0cBIKffXfyISezsMXALS2M1uq56pn1UhGVtyz8OaAguiJFxkirqn+S6e0GcKuGVDwZG1dg== ->>>>>>> master dependencies: "@mswjs/cookies" "^0.2.2" "@mswjs/interceptors" "^0.17.2"