Merge pull request #11789 from backjo/feature/EKSCatalog
Add EKS Cluster Processor and Kubernetes Catalog Collector
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/catalog-model': patch
|
||||
---
|
||||
|
||||
Add shared annotations for Kubernetes clusters
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-aws': patch
|
||||
---
|
||||
|
||||
Add processor for ingesting EKS clusters into the catalog
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-kubernetes-backend': patch
|
||||
---
|
||||
|
||||
Add support for Kubernetes clusters in the catalog.
|
||||
@@ -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).
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<Credentials>;
|
||||
|
||||
// @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<boolean>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export class AwsOrganizationCloudAccountProcessor implements CatalogProcessor {
|
||||
// (undocumented)
|
||||
|
||||
@@ -22,3 +22,4 @@
|
||||
|
||||
export * from './processors';
|
||||
export * from './providers';
|
||||
export * from './types';
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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
|
||||
* `<accountId>/<region>`.
|
||||
*
|
||||
* @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<boolean> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -14,5 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { AwsEKSClusterProcessor } from './AwsEKSClusterProcessor';
|
||||
export { AwsOrganizationCloudAccountProcessor } from './AwsOrganizationCloudAccountProcessor';
|
||||
export { AwsS3DiscoveryProcessor } from './AwsS3DiscoveryProcessor';
|
||||
|
||||
@@ -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<Credentials>;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<CatalogApi> 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',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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<ClusterDetails[]> {
|
||||
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<string, symbol | string> = {
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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"'),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -71,8 +71,6 @@ describe('KubernetesBuilder', () => {
|
||||
getKubernetesObjectsByEntity: jest.fn(),
|
||||
} as any;
|
||||
|
||||
catalogApi = {} as CatalogApi;
|
||||
|
||||
const { router } = await KubernetesBuilder.createBuilder({
|
||||
config,
|
||||
logger,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -39,6 +39,8 @@ export async function createStandaloneApplication(
|
||||
): Promise<express.Application> {
|
||||
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());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user