refactor: prepare kubernetes backend fan out handler for new endpoint (#11952)
* refactor: prepare kubernetes backend fan out handler for new endpoint Signed-off-by: Matthew Clarke <mclarke@spotify.com> * add changeset Signed-off-by: Matthew Clarke <mclarke@spotify.com> * PR api report feedback Signed-off-by: Matthew Clarke <mclarke@spotify.com> * update api docs Signed-off-by: Matthew Clarke <mclarke@spotify.com> * fix api report warnings Signed-off-by: Matthew Clarke <mclarke@spotify.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-kubernetes-backend': minor
|
||||
'@backstage/plugin-kubernetes-common': minor
|
||||
---
|
||||
|
||||
Refactor `KubernetesObjectsProvider` with new methods, `KubernetesServiceLocator` now takes an `Entity` instead of `serviceId`
|
||||
@@ -5,18 +5,18 @@
|
||||
```ts
|
||||
import { Config } from '@backstage/config';
|
||||
import { Duration } from 'luxon';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import express from 'express';
|
||||
import type { FetchResponse } from '@backstage/plugin-kubernetes-common';
|
||||
import type { JsonObject } from '@backstage/types';
|
||||
import type { KubernetesFetchError } from '@backstage/plugin-kubernetes-common';
|
||||
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 { PodStatus } from '@kubernetes/client-node/dist/top';
|
||||
|
||||
// Warning: (ae-missing-release-tag) "AWSClusterDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha (undocumented)
|
||||
export interface AWSClusterDetails extends ClusterDetails {
|
||||
// (undocumented)
|
||||
assumeRole?: string;
|
||||
@@ -24,14 +24,10 @@ export interface AWSClusterDetails extends ClusterDetails {
|
||||
externalId?: string;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "AzureClusterDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha (undocumented)
|
||||
export interface AzureClusterDetails extends ClusterDetails {}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ClusterDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha (undocumented)
|
||||
export interface ClusterDetails {
|
||||
// (undocumented)
|
||||
authProvider: string;
|
||||
@@ -51,27 +47,28 @@ export interface ClusterDetails {
|
||||
url: string;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public @deprecated
|
||||
// @alpha @deprecated
|
||||
export function createRouter(options: RouterOptions): Promise<express.Router>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "CustomResource" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha (undocumented)
|
||||
export interface CustomResource extends ObjectToFetch {
|
||||
// (undocumented)
|
||||
objectType: 'customresources';
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "DEFAULT_OBJECTS" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha (undocumented)
|
||||
export type CustomResourceMatcher = Omit<ObjectToFetch, 'objectType'>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export interface CustomResourcesByEntity extends KubernetesObjectsByEntity {
|
||||
// (undocumented)
|
||||
customResources: CustomResourceMatcher[];
|
||||
}
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const DEFAULT_OBJECTS: ObjectToFetch[];
|
||||
|
||||
// Warning: (ae-missing-release-tag) "FetchResponseWrapper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha (undocumented)
|
||||
export interface FetchResponseWrapper {
|
||||
// (undocumented)
|
||||
errors: KubernetesFetchError[];
|
||||
@@ -79,14 +76,10 @@ export interface FetchResponseWrapper {
|
||||
responses: FetchResponse[];
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "GKEClusterDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha (undocumented)
|
||||
export interface GKEClusterDetails extends ClusterDetails {}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "KubernetesBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha (undocumented)
|
||||
export class KubernetesBuilder {
|
||||
constructor(env: KubernetesEnvironment);
|
||||
// (undocumented)
|
||||
@@ -145,7 +138,7 @@ export class KubernetesBuilder {
|
||||
setServiceLocator(serviceLocator?: KubernetesServiceLocator): this;
|
||||
}
|
||||
|
||||
// @public
|
||||
// @alpha
|
||||
export type KubernetesBuilderReturn = Promise<{
|
||||
router: express.Router;
|
||||
clusterSupplier: KubernetesClustersSupplier;
|
||||
@@ -155,16 +148,12 @@ export type KubernetesBuilderReturn = Promise<{
|
||||
serviceLocator: KubernetesServiceLocator;
|
||||
}>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "KubernetesClustersSupplier" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha
|
||||
export interface KubernetesClustersSupplier {
|
||||
getClusters(): Promise<ClusterDetails[]>;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "KubernetesEnvironment" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha (undocumented)
|
||||
export interface KubernetesEnvironment {
|
||||
// (undocumented)
|
||||
config: Config;
|
||||
@@ -172,9 +161,7 @@ export interface KubernetesEnvironment {
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "KubernetesFetcher" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha
|
||||
export interface KubernetesFetcher {
|
||||
// (undocumented)
|
||||
fetchObjectsForService(
|
||||
@@ -187,19 +174,27 @@ export interface KubernetesFetcher {
|
||||
): Promise<PodStatus[]>;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "KubernetesObjectsProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha (undocumented)
|
||||
export interface KubernetesObjectsByEntity {
|
||||
// (undocumented)
|
||||
auth: KubernetesRequestAuth;
|
||||
// (undocumented)
|
||||
entity: Entity;
|
||||
}
|
||||
|
||||
// @alpha (undocumented)
|
||||
export interface KubernetesObjectsProvider {
|
||||
// (undocumented)
|
||||
getCustomResourcesByEntity(
|
||||
customResourcesByEntity: CustomResourcesByEntity,
|
||||
): Promise<ObjectsByEntityResponse>;
|
||||
// (undocumented)
|
||||
getKubernetesObjectsByEntity(
|
||||
request: ObjectsByEntityRequest,
|
||||
kubernetesObjectsByEntity: KubernetesObjectsByEntity,
|
||||
): Promise<ObjectsByEntityResponse>;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "KubernetesObjectsProviderOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha (undocumented)
|
||||
export interface KubernetesObjectsProviderOptions {
|
||||
// (undocumented)
|
||||
customResources: CustomResource[];
|
||||
@@ -213,9 +208,7 @@ export interface KubernetesObjectsProviderOptions {
|
||||
serviceLocator: KubernetesServiceLocator;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "KubernetesObjectTypes" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha (undocumented)
|
||||
export type KubernetesObjectTypes =
|
||||
| 'pods'
|
||||
| 'services'
|
||||
@@ -229,17 +222,15 @@ export type KubernetesObjectTypes =
|
||||
| 'customresources'
|
||||
| 'statefulsets';
|
||||
|
||||
// Warning: (ae-missing-release-tag) "KubernetesServiceLocator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha
|
||||
export interface KubernetesServiceLocator {
|
||||
// (undocumented)
|
||||
getClustersByServiceId(serviceId: string): Promise<ClusterDetails[]>;
|
||||
getClustersByEntity(entity: Entity): Promise<{
|
||||
clusters: ClusterDetails[];
|
||||
}>;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ObjectFetchParams" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha (undocumented)
|
||||
export interface ObjectFetchParams {
|
||||
// (undocumented)
|
||||
clusterDetails:
|
||||
@@ -259,14 +250,10 @@ export interface ObjectFetchParams {
|
||||
serviceId: string;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ObjectsByEntityRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha (undocumented)
|
||||
export type ObjectsByEntityRequest = KubernetesRequestBody;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ObjectToFetch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha (undocumented)
|
||||
export interface ObjectToFetch {
|
||||
// (undocumented)
|
||||
apiVersion: string;
|
||||
@@ -278,9 +265,7 @@ export interface ObjectToFetch {
|
||||
plural: string;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "RouterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha (undocumented)
|
||||
export interface RouterOptions {
|
||||
// (undocumented)
|
||||
clusterSupplier?: KubernetesClustersSupplier;
|
||||
@@ -290,13 +275,9 @@ export interface RouterOptions {
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ServiceAccountClusterDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha (undocumented)
|
||||
export interface ServiceAccountClusterDetails extends ClusterDetails {}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ServiceLocatorMethod" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @alpha (undocumented)
|
||||
export type ServiceLocatorMethod = 'multiTenant' | 'http';
|
||||
```
|
||||
|
||||
+3
-3
@@ -16,20 +16,20 @@
|
||||
|
||||
import { KubernetesAuthTranslator } from './types';
|
||||
import { GKEClusterDetails } from '../types/types';
|
||||
import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common';
|
||||
import { KubernetesRequestAuth } from '@backstage/plugin-kubernetes-common';
|
||||
|
||||
export class GoogleKubernetesAuthTranslator
|
||||
implements KubernetesAuthTranslator
|
||||
{
|
||||
async decorateClusterDetailsWithAuth(
|
||||
clusterDetails: GKEClusterDetails,
|
||||
requestBody: KubernetesRequestBody,
|
||||
authConfig: KubernetesRequestAuth,
|
||||
): Promise<GKEClusterDetails> {
|
||||
const clusterDetailsWithAuthToken: GKEClusterDetails = Object.assign(
|
||||
{},
|
||||
clusterDetails,
|
||||
);
|
||||
const authToken: string | undefined = requestBody.auth?.google;
|
||||
const authToken: string | undefined = authConfig.google;
|
||||
|
||||
if (authToken) {
|
||||
clusterDetailsWithAuthToken.serviceAccountToken = authToken;
|
||||
|
||||
+2
-4
@@ -17,7 +17,7 @@
|
||||
import { KubernetesAuthTranslator } from './types';
|
||||
import { GoogleKubernetesAuthTranslator } from './GoogleKubernetesAuthTranslator';
|
||||
import { KubernetesAuthTranslatorGenerator } from './KubernetesAuthTranslatorGenerator';
|
||||
import { ServiceAccountKubernetesAuthTranslator } from './ServiceAccountKubernetesAuthTranslator';
|
||||
import { NoopKubernetesAuthTranslator } from './NoopKubernetesAuthTranslator';
|
||||
import { AwsIamKubernetesAuthTranslator } from './AwsIamKubernetesAuthTranslator';
|
||||
import { OidcKubernetesAuthTranslator } from './OidcKubernetesAuthTranslator';
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
@@ -42,9 +42,7 @@ describe('getKubernetesAuthTranslatorInstance', () => {
|
||||
it('can return an auth translator for serviceAccount auth', () => {
|
||||
const authTranslator: KubernetesAuthTranslator =
|
||||
sut.getKubernetesAuthTranslatorInstance('serviceAccount', { logger });
|
||||
expect(
|
||||
authTranslator instanceof ServiceAccountKubernetesAuthTranslator,
|
||||
).toBe(true);
|
||||
expect(authTranslator instanceof NoopKubernetesAuthTranslator).toBe(true);
|
||||
});
|
||||
|
||||
it('can return an auth translator for oidc auth', () => {
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@
|
||||
import { Logger } from 'winston';
|
||||
import { KubernetesAuthTranslator } from './types';
|
||||
import { GoogleKubernetesAuthTranslator } from './GoogleKubernetesAuthTranslator';
|
||||
import { ServiceAccountKubernetesAuthTranslator } from './ServiceAccountKubernetesAuthTranslator';
|
||||
import { NoopKubernetesAuthTranslator } from './NoopKubernetesAuthTranslator';
|
||||
import { AwsIamKubernetesAuthTranslator } from './AwsIamKubernetesAuthTranslator';
|
||||
import { GoogleServiceAccountAuthTranslator } from './GoogleServiceAccountAuthProvider';
|
||||
import { AzureIdentityKubernetesAuthTranslator } from './AzureIdentityKubernetesAuthTranslator';
|
||||
@@ -41,7 +41,7 @@ export class KubernetesAuthTranslatorGenerator {
|
||||
return new AzureIdentityKubernetesAuthTranslator(options.logger);
|
||||
}
|
||||
case 'serviceAccount': {
|
||||
return new ServiceAccountKubernetesAuthTranslator();
|
||||
return new NoopKubernetesAuthTranslator();
|
||||
}
|
||||
case 'googleServiceAccount': {
|
||||
return new GoogleServiceAccountAuthTranslator();
|
||||
|
||||
+1
-5
@@ -16,14 +16,10 @@
|
||||
|
||||
import { KubernetesAuthTranslator } from './types';
|
||||
import { ServiceAccountClusterDetails } from '../types/types';
|
||||
import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common';
|
||||
|
||||
export class ServiceAccountKubernetesAuthTranslator
|
||||
implements KubernetesAuthTranslator
|
||||
{
|
||||
export class NoopKubernetesAuthTranslator implements KubernetesAuthTranslator {
|
||||
async decorateClusterDetailsWithAuth(
|
||||
clusterDetails: ServiceAccountClusterDetails,
|
||||
_requestBody: KubernetesRequestBody,
|
||||
): Promise<ServiceAccountClusterDetails> {
|
||||
return clusterDetails;
|
||||
}
|
||||
+3
-12
@@ -16,15 +16,9 @@
|
||||
|
||||
import { OidcKubernetesAuthTranslator } from './OidcKubernetesAuthTranslator';
|
||||
import { ClusterDetails } from '../types/types';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
|
||||
describe('OidcKubernetesAuthTranslator tests', () => {
|
||||
const at = new OidcKubernetesAuthTranslator();
|
||||
const entity: Entity = {
|
||||
apiVersion: 'v1',
|
||||
kind: 'service',
|
||||
metadata: { name: 'test' },
|
||||
};
|
||||
const baseClusterDetails: ClusterDetails = {
|
||||
name: 'test',
|
||||
authProvider: 'oidc',
|
||||
@@ -38,10 +32,7 @@ describe('OidcKubernetesAuthTranslator tests', () => {
|
||||
...baseClusterDetails,
|
||||
},
|
||||
{
|
||||
auth: {
|
||||
oidc: { okta: 'fakeToken' },
|
||||
},
|
||||
entity,
|
||||
oidc: { okta: 'fakeToken' },
|
||||
},
|
||||
);
|
||||
|
||||
@@ -50,7 +41,7 @@ describe('OidcKubernetesAuthTranslator tests', () => {
|
||||
|
||||
it('returns error when oidcTokenProvider is not configured', async () => {
|
||||
await expect(
|
||||
at.decorateClusterDetailsWithAuth(baseClusterDetails, { entity }),
|
||||
at.decorateClusterDetailsWithAuth(baseClusterDetails, {}),
|
||||
).rejects.toThrow(
|
||||
'oidc authProvider requires a configured oidcTokenProvider',
|
||||
);
|
||||
@@ -60,7 +51,7 @@ describe('OidcKubernetesAuthTranslator tests', () => {
|
||||
await expect(
|
||||
at.decorateClusterDetailsWithAuth(
|
||||
{ oidcTokenProvider: 'okta', ...baseClusterDetails },
|
||||
{ entity },
|
||||
{},
|
||||
),
|
||||
).rejects.toThrow('Auth token not found under oidc.okta in request body');
|
||||
});
|
||||
|
||||
+3
-4
@@ -16,12 +16,12 @@
|
||||
|
||||
import { KubernetesAuthTranslator } from './types';
|
||||
import { ClusterDetails } from '../types/types';
|
||||
import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common';
|
||||
import { KubernetesRequestAuth } from '@backstage/plugin-kubernetes-common';
|
||||
|
||||
export class OidcKubernetesAuthTranslator implements KubernetesAuthTranslator {
|
||||
async decorateClusterDetailsWithAuth(
|
||||
clusterDetails: ClusterDetails,
|
||||
requestBody: KubernetesRequestBody,
|
||||
authConfig: KubernetesRequestAuth,
|
||||
): Promise<ClusterDetails> {
|
||||
const clusterDetailsWithAuthToken: ClusterDetails = Object.assign(
|
||||
{},
|
||||
@@ -36,8 +36,7 @@ export class OidcKubernetesAuthTranslator implements KubernetesAuthTranslator {
|
||||
);
|
||||
}
|
||||
|
||||
const authToken: string | undefined =
|
||||
requestBody.auth?.oidc?.[oidcTokenProvider];
|
||||
const authToken: string | undefined = authConfig.oidc?.[oidcTokenProvider];
|
||||
|
||||
if (authToken) {
|
||||
clusterDetailsWithAuthToken.serviceAccountToken = authToken;
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
*/
|
||||
|
||||
import { ClusterDetails } from '../types/types';
|
||||
import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common';
|
||||
import { KubernetesRequestAuth } from '@backstage/plugin-kubernetes-common';
|
||||
|
||||
export interface KubernetesAuthTranslator {
|
||||
decorateClusterDetailsWithAuth(
|
||||
clusterDetails: ClusterDetails,
|
||||
requestBody: KubernetesRequestBody,
|
||||
authConfig: KubernetesRequestAuth,
|
||||
): Promise<ClusterDetails>;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import '@backstage/backend-common';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { MultiTenantServiceLocator } from './MultiTenantServiceLocator';
|
||||
|
||||
describe('MultiTenantConfigClusterLocator', () => {
|
||||
@@ -23,9 +24,9 @@ describe('MultiTenantConfigClusterLocator', () => {
|
||||
getClusters: async () => [],
|
||||
});
|
||||
|
||||
const result = await sut.getClustersByServiceId('ignored');
|
||||
const result = await sut.getClustersByEntity({} as Entity);
|
||||
|
||||
expect(result).toStrictEqual([]);
|
||||
expect(result).toStrictEqual({ clusters: [] });
|
||||
});
|
||||
|
||||
it('one clusters returns one cluster details', async () => {
|
||||
@@ -42,16 +43,18 @@ describe('MultiTenantConfigClusterLocator', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const result = await sut.getClustersByServiceId('ignored');
|
||||
const result = await sut.getClustersByEntity({} as Entity);
|
||||
|
||||
expect(result).toStrictEqual([
|
||||
{
|
||||
name: 'cluster1',
|
||||
serviceAccountToken: '12345',
|
||||
url: 'http://localhost:8080',
|
||||
authProvider: 'serviceAccount',
|
||||
},
|
||||
]);
|
||||
expect(result).toStrictEqual({
|
||||
clusters: [
|
||||
{
|
||||
name: 'cluster1',
|
||||
serviceAccountToken: '12345',
|
||||
url: 'http://localhost:8080',
|
||||
authProvider: 'serviceAccount',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('two clusters returns two cluster details', async () => {
|
||||
@@ -73,20 +76,22 @@ describe('MultiTenantConfigClusterLocator', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const result = await sut.getClustersByServiceId('ignored');
|
||||
const result = await sut.getClustersByEntity({} as Entity);
|
||||
|
||||
expect(result).toStrictEqual([
|
||||
{
|
||||
name: 'cluster1',
|
||||
serviceAccountToken: 'token',
|
||||
url: 'http://localhost:8080',
|
||||
authProvider: 'serviceAccount',
|
||||
},
|
||||
{
|
||||
name: 'cluster2',
|
||||
url: 'http://localhost:8081',
|
||||
authProvider: 'google',
|
||||
},
|
||||
]);
|
||||
expect(result).toStrictEqual({
|
||||
clusters: [
|
||||
{
|
||||
name: 'cluster1',
|
||||
serviceAccountToken: 'token',
|
||||
url: 'http://localhost:8080',
|
||||
authProvider: 'serviceAccount',
|
||||
},
|
||||
{
|
||||
name: 'cluster2',
|
||||
url: 'http://localhost:8081',
|
||||
authProvider: 'google',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import {
|
||||
ClusterDetails,
|
||||
KubernetesClustersSupplier,
|
||||
@@ -30,8 +31,9 @@ export class MultiTenantServiceLocator implements KubernetesServiceLocator {
|
||||
}
|
||||
|
||||
// As this implementation always returns all clusters serviceId is ignored here
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async getClustersByServiceId(_serviceId: string): Promise<ClusterDetails[]> {
|
||||
return this.clusterSupplier.getClusters();
|
||||
getClustersByEntity(
|
||||
_entity: Entity,
|
||||
): Promise<{ clusters: ClusterDetails[] }> {
|
||||
return this.clusterSupplier.getClusters().then(clusters => ({ clusters }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
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';
|
||||
import express from 'express';
|
||||
@@ -207,8 +208,10 @@ describe('KubernetesBuilder', () => {
|
||||
};
|
||||
|
||||
const serviceLocator: KubernetesServiceLocator = {
|
||||
getClustersByServiceId(_serviceId: string): Promise<ClusterDetails[]> {
|
||||
return Promise.resolve([someCluster]);
|
||||
getClustersByEntity(
|
||||
_entity: Entity,
|
||||
): Promise<{ clusters: ClusterDetails[] }> {
|
||||
return Promise.resolve({ clusters: [someCluster] });
|
||||
},
|
||||
};
|
||||
|
||||
@@ -244,7 +247,15 @@ describe('KubernetesBuilder', () => {
|
||||
.build();
|
||||
app = express().use(router);
|
||||
|
||||
const response = await request(app).post('/services/test-service');
|
||||
const response = await request(app)
|
||||
.post('/services/test-service')
|
||||
.send({
|
||||
entity: {
|
||||
metadata: {
|
||||
name: 'thing',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.body).toEqual(result);
|
||||
expect(response.status).toEqual(200);
|
||||
|
||||
@@ -38,6 +38,10 @@ import {
|
||||
} from './KubernetesFanOutHandler';
|
||||
import { KubernetesClientBasedFetcher } from './KubernetesFetcher';
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface KubernetesEnvironment {
|
||||
logger: Logger;
|
||||
config: Config;
|
||||
@@ -46,7 +50,7 @@ export interface KubernetesEnvironment {
|
||||
/**
|
||||
* The return type of the `KubernetesBuilder.build` method
|
||||
*
|
||||
* @public
|
||||
* @alpha
|
||||
*/
|
||||
export type KubernetesBuilderReturn = Promise<{
|
||||
router: express.Router;
|
||||
@@ -57,6 +61,10 @@ export type KubernetesBuilderReturn = Promise<{
|
||||
serviceLocator: KubernetesServiceLocator;
|
||||
}>;
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export class KubernetesBuilder {
|
||||
private clusterSupplier?: KubernetesClustersSupplier;
|
||||
private defaultClusterRefreshInterval: Duration = Duration.fromObject({
|
||||
@@ -227,9 +235,10 @@ export class KubernetesBuilder {
|
||||
const serviceId = req.params.serviceId;
|
||||
const requestBody: ObjectsByEntityRequest = req.body;
|
||||
try {
|
||||
const response = await objectsProvider.getKubernetesObjectsByEntity(
|
||||
requestBody,
|
||||
);
|
||||
const response = await objectsProvider.getKubernetesObjectsByEntity({
|
||||
entity: requestBody.entity,
|
||||
auth: requestBody.auth || {},
|
||||
});
|
||||
res.json(response);
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
|
||||
@@ -22,7 +22,7 @@ import { PodStatus } from '@kubernetes/client-node/dist/top';
|
||||
const fetchObjectsForService = jest.fn();
|
||||
const fetchPodMetricsByNamespace = jest.fn();
|
||||
|
||||
const getClustersByServiceId = jest.fn();
|
||||
const getClustersByEntity = jest.fn();
|
||||
|
||||
const POD_METRICS_FIXTURE = {
|
||||
containers: [],
|
||||
@@ -166,13 +166,15 @@ describe('handleGetKubernetesObjectsForService', () => {
|
||||
});
|
||||
|
||||
it('retrieve objects for one cluster', async () => {
|
||||
getClustersByServiceId.mockImplementation(() =>
|
||||
Promise.resolve([
|
||||
{
|
||||
name: 'test-cluster',
|
||||
authProvider: 'serviceAccount',
|
||||
},
|
||||
]),
|
||||
getClustersByEntity.mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
clusters: [
|
||||
{
|
||||
name: 'test-cluster',
|
||||
authProvider: 'serviceAccount',
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
mockFetch(fetchObjectsForService);
|
||||
@@ -185,7 +187,7 @@ describe('handleGetKubernetesObjectsForService', () => {
|
||||
fetchPodMetricsByNamespace,
|
||||
},
|
||||
serviceLocator: {
|
||||
getClustersByServiceId,
|
||||
getClustersByEntity,
|
||||
},
|
||||
customResources: [],
|
||||
});
|
||||
@@ -207,9 +209,10 @@ describe('handleGetKubernetesObjectsForService', () => {
|
||||
owner: 'joe',
|
||||
},
|
||||
},
|
||||
auth: {},
|
||||
});
|
||||
|
||||
expect(getClustersByServiceId.mock.calls.length).toBe(1);
|
||||
expect(getClustersByEntity.mock.calls.length).toBe(1);
|
||||
expect(fetchObjectsForService.mock.calls.length).toBe(1);
|
||||
expect(fetchPodMetricsByNamespace.mock.calls.length).toBe(1);
|
||||
expect(fetchPodMetricsByNamespace.mock.calls[0][1]).toBe(
|
||||
@@ -265,13 +268,15 @@ describe('handleGetKubernetesObjectsForService', () => {
|
||||
});
|
||||
|
||||
it('dont call top for the same namespace twice', async () => {
|
||||
getClustersByServiceId.mockImplementation(() =>
|
||||
Promise.resolve([
|
||||
{
|
||||
name: 'test-cluster',
|
||||
authProvider: 'serviceAccount',
|
||||
},
|
||||
]),
|
||||
getClustersByEntity.mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
clusters: [
|
||||
{
|
||||
name: 'test-cluster',
|
||||
authProvider: 'serviceAccount',
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
fetchObjectsForService.mockImplementation((_: ObjectFetchParams) =>
|
||||
@@ -314,7 +319,7 @@ describe('handleGetKubernetesObjectsForService', () => {
|
||||
fetchPodMetricsByNamespace,
|
||||
},
|
||||
serviceLocator: {
|
||||
getClustersByServiceId,
|
||||
getClustersByEntity,
|
||||
},
|
||||
customResources: [],
|
||||
});
|
||||
@@ -336,9 +341,10 @@ describe('handleGetKubernetesObjectsForService', () => {
|
||||
owner: 'joe',
|
||||
},
|
||||
},
|
||||
auth: {},
|
||||
});
|
||||
|
||||
expect(getClustersByServiceId.mock.calls.length).toBe(1);
|
||||
expect(getClustersByEntity.mock.calls.length).toBe(1);
|
||||
expect(fetchObjectsForService.mock.calls.length).toBe(1);
|
||||
expect(fetchPodMetricsByNamespace.mock.calls.length).toBe(2);
|
||||
expect(fetchPodMetricsByNamespace.mock.calls[0][1]).toBe('ns-a');
|
||||
@@ -383,18 +389,20 @@ describe('handleGetKubernetesObjectsForService', () => {
|
||||
});
|
||||
|
||||
it('retrieve objects for two clusters', async () => {
|
||||
getClustersByServiceId.mockImplementation(() =>
|
||||
Promise.resolve([
|
||||
{
|
||||
name: 'test-cluster',
|
||||
authProvider: 'serviceAccount',
|
||||
dashboardUrl: 'https://k8s.foo.coom',
|
||||
},
|
||||
{
|
||||
name: 'other-cluster',
|
||||
authProvider: 'google',
|
||||
},
|
||||
]),
|
||||
getClustersByEntity.mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
clusters: [
|
||||
{
|
||||
name: 'test-cluster',
|
||||
authProvider: 'serviceAccount',
|
||||
dashboardUrl: 'https://k8s.foo.coom',
|
||||
},
|
||||
{
|
||||
name: 'other-cluster',
|
||||
authProvider: 'google',
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
mockFetch(fetchObjectsForService);
|
||||
@@ -407,15 +415,12 @@ describe('handleGetKubernetesObjectsForService', () => {
|
||||
fetchPodMetricsByNamespace,
|
||||
},
|
||||
serviceLocator: {
|
||||
getClustersByServiceId,
|
||||
getClustersByEntity,
|
||||
},
|
||||
customResources: [],
|
||||
});
|
||||
|
||||
const result = await sut.getKubernetesObjectsByEntity({
|
||||
auth: {
|
||||
google: 'google_token_123',
|
||||
},
|
||||
entity: {
|
||||
apiVersion: 'backstage.io/v1beta1',
|
||||
kind: 'Component',
|
||||
@@ -432,9 +437,12 @@ describe('handleGetKubernetesObjectsForService', () => {
|
||||
owner: 'joe',
|
||||
},
|
||||
},
|
||||
auth: {
|
||||
google: 'google_token_123',
|
||||
},
|
||||
});
|
||||
|
||||
expect(getClustersByServiceId.mock.calls.length).toBe(1);
|
||||
expect(getClustersByEntity.mock.calls.length).toBe(1);
|
||||
expect(fetchObjectsForService.mock.calls.length).toBe(2);
|
||||
expect(result).toStrictEqual({
|
||||
items: [
|
||||
@@ -527,21 +535,23 @@ describe('handleGetKubernetesObjectsForService', () => {
|
||||
});
|
||||
});
|
||||
it('retrieve objects for three clusters, only two have resources and show in ui', async () => {
|
||||
getClustersByServiceId.mockImplementation(() =>
|
||||
Promise.resolve([
|
||||
{
|
||||
name: 'test-cluster',
|
||||
authProvider: 'serviceAccount',
|
||||
},
|
||||
{
|
||||
name: 'other-cluster',
|
||||
authProvider: 'google',
|
||||
},
|
||||
{
|
||||
name: 'empty-cluster',
|
||||
authProvider: 'google',
|
||||
},
|
||||
]),
|
||||
getClustersByEntity.mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
clusters: [
|
||||
{
|
||||
name: 'test-cluster',
|
||||
authProvider: 'serviceAccount',
|
||||
},
|
||||
{
|
||||
name: 'other-cluster',
|
||||
authProvider: 'google',
|
||||
},
|
||||
{
|
||||
name: 'empty-cluster',
|
||||
authProvider: 'google',
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
mockFetch(fetchObjectsForService);
|
||||
@@ -554,15 +564,12 @@ describe('handleGetKubernetesObjectsForService', () => {
|
||||
fetchPodMetricsByNamespace,
|
||||
},
|
||||
serviceLocator: {
|
||||
getClustersByServiceId,
|
||||
getClustersByEntity,
|
||||
},
|
||||
customResources: [],
|
||||
});
|
||||
|
||||
const result = await sut.getKubernetesObjectsByEntity({
|
||||
auth: {
|
||||
google: 'google_token_123',
|
||||
},
|
||||
entity: {
|
||||
apiVersion: 'backstage.io/v1beta1',
|
||||
kind: 'Component',
|
||||
@@ -579,9 +586,12 @@ describe('handleGetKubernetesObjectsForService', () => {
|
||||
owner: 'joe',
|
||||
},
|
||||
},
|
||||
auth: {
|
||||
google: 'google_token_123',
|
||||
},
|
||||
});
|
||||
|
||||
expect(getClustersByServiceId.mock.calls.length).toBe(1);
|
||||
expect(getClustersByEntity.mock.calls.length).toBe(1);
|
||||
expect(fetchObjectsForService.mock.calls.length).toBe(3);
|
||||
expect(result).toStrictEqual({
|
||||
items: [
|
||||
@@ -673,25 +683,27 @@ describe('handleGetKubernetesObjectsForService', () => {
|
||||
});
|
||||
});
|
||||
it('retrieve objects for four clusters, two have resources and one error cluster', async () => {
|
||||
getClustersByServiceId.mockImplementation(() =>
|
||||
Promise.resolve([
|
||||
{
|
||||
name: 'test-cluster',
|
||||
authProvider: 'serviceAccount',
|
||||
},
|
||||
{
|
||||
name: 'other-cluster',
|
||||
authProvider: 'google',
|
||||
},
|
||||
{
|
||||
name: 'empty-cluster',
|
||||
authProvider: 'google',
|
||||
},
|
||||
{
|
||||
name: 'error-cluster',
|
||||
authProvider: 'google',
|
||||
},
|
||||
]),
|
||||
getClustersByEntity.mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
clusters: [
|
||||
{
|
||||
name: 'test-cluster',
|
||||
authProvider: 'serviceAccount',
|
||||
},
|
||||
{
|
||||
name: 'other-cluster',
|
||||
authProvider: 'google',
|
||||
},
|
||||
{
|
||||
name: 'empty-cluster',
|
||||
authProvider: 'google',
|
||||
},
|
||||
{
|
||||
name: 'error-cluster',
|
||||
authProvider: 'google',
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
mockFetch(fetchObjectsForService);
|
||||
@@ -704,15 +716,12 @@ describe('handleGetKubernetesObjectsForService', () => {
|
||||
fetchPodMetricsByNamespace,
|
||||
},
|
||||
serviceLocator: {
|
||||
getClustersByServiceId,
|
||||
getClustersByEntity,
|
||||
},
|
||||
customResources: [],
|
||||
});
|
||||
|
||||
const result = await sut.getKubernetesObjectsByEntity({
|
||||
auth: {
|
||||
google: 'google_token_123',
|
||||
},
|
||||
entity: {
|
||||
apiVersion: 'backstage.io/v1beta1',
|
||||
kind: 'Component',
|
||||
@@ -729,9 +738,12 @@ describe('handleGetKubernetesObjectsForService', () => {
|
||||
owner: 'joe',
|
||||
},
|
||||
},
|
||||
auth: {
|
||||
google: 'google_token_123',
|
||||
},
|
||||
});
|
||||
|
||||
expect(getClustersByServiceId.mock.calls.length).toBe(1);
|
||||
expect(getClustersByEntity.mock.calls.length).toBe(1);
|
||||
expect(fetchObjectsForService.mock.calls.length).toBe(4);
|
||||
expect(result).toStrictEqual({
|
||||
items: [
|
||||
|
||||
@@ -14,16 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Logger } from 'winston';
|
||||
import {
|
||||
ClusterDetails,
|
||||
CustomResource,
|
||||
KubernetesFetcher,
|
||||
KubernetesObjectsProviderOptions,
|
||||
KubernetesServiceLocator,
|
||||
ObjectsByEntityRequest,
|
||||
FetchResponseWrapper,
|
||||
ObjectToFetch,
|
||||
CustomResource,
|
||||
CustomResourceMatcher,
|
||||
CustomResourcesByEntity,
|
||||
KubernetesObjectsByEntity,
|
||||
} from '../types/types';
|
||||
import { KubernetesAuthTranslator } from '../kubernetes-auth-translator/types';
|
||||
import { KubernetesAuthTranslatorGenerator } from '../kubernetes-auth-translator/KubernetesAuthTranslatorGenerator';
|
||||
@@ -35,6 +39,7 @@ import {
|
||||
FetchResponse,
|
||||
ObjectsByEntityResponse,
|
||||
PodFetchResponse,
|
||||
KubernetesRequestAuth,
|
||||
} from '@backstage/plugin-kubernetes-common';
|
||||
import {
|
||||
ContainerStatus,
|
||||
@@ -42,6 +47,10 @@ import {
|
||||
PodStatus,
|
||||
} from '@kubernetes/client-node';
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export const DEFAULT_OBJECTS: ObjectToFetch[] = [
|
||||
{
|
||||
group: '',
|
||||
@@ -178,26 +187,44 @@ export class KubernetesFanOutHandler {
|
||||
this.authTranslators = {};
|
||||
}
|
||||
|
||||
async getKubernetesObjectsByEntity(
|
||||
requestBody: KubernetesRequestBody,
|
||||
): Promise<ObjectsByEntityResponse> {
|
||||
const entityName =
|
||||
requestBody.entity?.metadata?.annotations?.[
|
||||
'backstage.io/kubernetes-id'
|
||||
] || requestBody.entity?.metadata?.name;
|
||||
|
||||
const clusterDetails: ClusterDetails[] =
|
||||
await this.serviceLocator.getClustersByServiceId(entityName);
|
||||
|
||||
// Execute all of these async actions simultaneously/without blocking sequentially as no common object is modified by them
|
||||
const promises: Promise<ClusterDetails>[] = clusterDetails.map(cd => {
|
||||
return this.getAuthTranslator(
|
||||
cd.authProvider,
|
||||
).decorateClusterDetailsWithAuth(cd, requestBody);
|
||||
});
|
||||
const clusterDetailsDecoratedForAuth: ClusterDetails[] = await Promise.all(
|
||||
promises,
|
||||
async getCustomResourcesByEntity({
|
||||
entity,
|
||||
auth,
|
||||
customResources,
|
||||
}: CustomResourcesByEntity): Promise<ObjectsByEntityResponse> {
|
||||
// Don't fetch the default object types only the provided custom resources
|
||||
return this.fanOutRequests(
|
||||
entity,
|
||||
auth,
|
||||
new Set<ObjectToFetch>(),
|
||||
customResources,
|
||||
);
|
||||
}
|
||||
|
||||
async getKubernetesObjectsByEntity({
|
||||
entity,
|
||||
auth,
|
||||
}: KubernetesObjectsByEntity): Promise<ObjectsByEntityResponse> {
|
||||
return this.fanOutRequests(
|
||||
entity,
|
||||
auth,
|
||||
this.objectTypesToFetch,
|
||||
this.customResources,
|
||||
);
|
||||
}
|
||||
|
||||
private async fanOutRequests(
|
||||
entity: Entity,
|
||||
auth: KubernetesRequestAuth,
|
||||
objectTypesToFetch: Set<ObjectToFetch>,
|
||||
customResources: CustomResourceMatcher[],
|
||||
) {
|
||||
const entityName =
|
||||
entity.metadata?.annotations?.['backstage.io/kubernetes-id'] ||
|
||||
entity.metadata?.name;
|
||||
|
||||
const clusterDetailsDecoratedForAuth: ClusterDetails[] =
|
||||
await this.decorateClusterDetailsWithAuth(entity, auth);
|
||||
|
||||
this.logger.info(
|
||||
`entity.metadata.name=${entityName} clusterDetails=[${clusterDetailsDecoratedForAuth
|
||||
@@ -206,14 +233,12 @@ export class KubernetesFanOutHandler {
|
||||
);
|
||||
|
||||
const labelSelector: string =
|
||||
requestBody.entity?.metadata?.annotations?.[
|
||||
entity.metadata?.annotations?.[
|
||||
'backstage.io/kubernetes-label-selector'
|
||||
] || `backstage.io/kubernetes-id=${entityName}`;
|
||||
|
||||
const namespace =
|
||||
requestBody.entity?.metadata?.annotations?.[
|
||||
'backstage.io/kubernetes-namespace'
|
||||
];
|
||||
entity.metadata?.annotations?.['backstage.io/kubernetes-namespace'];
|
||||
|
||||
return Promise.all(
|
||||
clusterDetailsDecoratedForAuth.map(clusterDetailsItem => {
|
||||
@@ -221,9 +246,12 @@ export class KubernetesFanOutHandler {
|
||||
.fetchObjectsForService({
|
||||
serviceId: entityName,
|
||||
clusterDetails: clusterDetailsItem,
|
||||
objectTypesToFetch: this.objectTypesToFetch,
|
||||
objectTypesToFetch: objectTypesToFetch,
|
||||
labelSelector,
|
||||
customResources: this.customResources,
|
||||
customResources: customResources.map(c => ({
|
||||
...c,
|
||||
objectType: 'customresources',
|
||||
})),
|
||||
namespace,
|
||||
})
|
||||
.then(result => this.getMetricsForPods(clusterDetailsItem, result))
|
||||
@@ -232,6 +260,27 @@ export class KubernetesFanOutHandler {
|
||||
).then(this.toObjectsByEntityResponse);
|
||||
}
|
||||
|
||||
private async decorateClusterDetailsWithAuth(
|
||||
entity: Entity,
|
||||
auth: KubernetesRequestAuth,
|
||||
) {
|
||||
const clusterDetails: ClusterDetails[] = await (
|
||||
await this.serviceLocator.getClustersByEntity(entity)
|
||||
).clusters;
|
||||
|
||||
// Execute all of these async actions simultaneously/without blocking sequentially as no common object is modified by them
|
||||
return await Promise.all(
|
||||
clusterDetails.map(cd => {
|
||||
const kubernetesAuthTranslator: KubernetesAuthTranslator =
|
||||
this.getAuthTranslator(cd.authProvider);
|
||||
return kubernetesAuthTranslator.decorateClusterDetailsWithAuth(
|
||||
cd,
|
||||
auth,
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
toObjectsByEntityResponse(
|
||||
clusterObjects: ClusterObjects[],
|
||||
): ObjectsByEntityResponse {
|
||||
|
||||
@@ -20,6 +20,10 @@ import { KubernetesClustersSupplier } from '../types/types';
|
||||
import express from 'express';
|
||||
import { KubernetesBuilder } from './KubernetesBuilder';
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface RouterOptions {
|
||||
logger: Logger;
|
||||
config: Config;
|
||||
@@ -38,6 +42,8 @@ export interface RouterOptions {
|
||||
* config,
|
||||
* }).build();
|
||||
* ```
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export async function createRouter(
|
||||
options: RouterOptions,
|
||||
|
||||
@@ -14,16 +14,22 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Logger } from 'winston';
|
||||
import type { JsonObject } from '@backstage/types';
|
||||
import type {
|
||||
FetchResponse,
|
||||
KubernetesFetchError,
|
||||
KubernetesRequestAuth,
|
||||
KubernetesRequestBody,
|
||||
ObjectsByEntityResponse,
|
||||
} from '@backstage/plugin-kubernetes-common';
|
||||
import { PodStatus } from '@kubernetes/client-node/dist/top';
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface ObjectFetchParams {
|
||||
serviceId: string;
|
||||
clusterDetails:
|
||||
@@ -37,8 +43,11 @@ export interface ObjectFetchParams {
|
||||
namespace?: string;
|
||||
}
|
||||
|
||||
// Fetches information from a kubernetes cluster using the cluster details object
|
||||
// to target a specific cluster
|
||||
/**
|
||||
* Fetches information from a kubernetes cluster using the cluster details object to target a specific cluster
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface KubernetesFetcher {
|
||||
fetchObjectsForService(
|
||||
params: ObjectFetchParams,
|
||||
@@ -49,13 +58,19 @@ export interface KubernetesFetcher {
|
||||
): Promise<PodStatus[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface FetchResponseWrapper {
|
||||
errors: KubernetesFetchError[];
|
||||
responses: FetchResponse[];
|
||||
}
|
||||
|
||||
// TODO fairly sure there's a easier way to do this
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface ObjectToFetch {
|
||||
objectType: KubernetesObjectTypes;
|
||||
group: string;
|
||||
@@ -63,10 +78,24 @@ export interface ObjectToFetch {
|
||||
plural: string;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface CustomResource extends ObjectToFetch {
|
||||
objectType: 'customresources';
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export type CustomResourceMatcher = Omit<ObjectToFetch, 'objectType'>;
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export type KubernetesObjectTypes =
|
||||
| 'pods'
|
||||
| 'services'
|
||||
@@ -80,7 +109,10 @@ export type KubernetesObjectTypes =
|
||||
| 'customresources'
|
||||
| 'statefulsets';
|
||||
|
||||
// Used to load cluster details from different sources
|
||||
/**
|
||||
* Used to load cluster details from different sources
|
||||
* @alpha
|
||||
*/
|
||||
export interface KubernetesClustersSupplier {
|
||||
/**
|
||||
* Returns the cached list of clusters.
|
||||
@@ -91,13 +123,24 @@ export interface KubernetesClustersSupplier {
|
||||
getClusters(): Promise<ClusterDetails[]>;
|
||||
}
|
||||
|
||||
// Used to locate which cluster(s) a service is running on
|
||||
/**
|
||||
* Used to locate which cluster(s) a service is running on
|
||||
* @alpha
|
||||
*/
|
||||
export interface KubernetesServiceLocator {
|
||||
getClustersByServiceId(serviceId: string): Promise<ClusterDetails[]>;
|
||||
getClustersByEntity(entity: Entity): Promise<{ clusters: ClusterDetails[] }>;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export type ServiceLocatorMethod = 'multiTenant' | 'http'; // TODO implement http
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface ClusterDetails {
|
||||
/**
|
||||
* Specifies the name of the Kubernetes cluster.
|
||||
@@ -151,14 +194,37 @@ export interface ClusterDetails {
|
||||
dashboardParameters?: JsonObject;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface GKEClusterDetails extends ClusterDetails {}
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface AzureClusterDetails extends ClusterDetails {}
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface ServiceAccountClusterDetails extends ClusterDetails {}
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface AWSClusterDetails extends ClusterDetails {
|
||||
assumeRole?: string;
|
||||
externalId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface KubernetesObjectsProviderOptions {
|
||||
logger: Logger;
|
||||
fetcher: KubernetesFetcher;
|
||||
@@ -167,10 +233,38 @@ export interface KubernetesObjectsProviderOptions {
|
||||
objectTypesToFetch?: ObjectToFetch[];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export type ObjectsByEntityRequest = KubernetesRequestBody;
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface KubernetesObjectsByEntity {
|
||||
entity: Entity;
|
||||
auth: KubernetesRequestAuth;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface CustomResourcesByEntity extends KubernetesObjectsByEntity {
|
||||
customResources: CustomResourceMatcher[];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface KubernetesObjectsProvider {
|
||||
getKubernetesObjectsByEntity(
|
||||
request: ObjectsByEntityRequest,
|
||||
kubernetesObjectsByEntity: KubernetesObjectsByEntity,
|
||||
): Promise<ObjectsByEntityResponse>;
|
||||
getCustomResourcesByEntity(
|
||||
customResourcesByEntity: CustomResourcesByEntity,
|
||||
): Promise<ObjectsByEntityResponse>;
|
||||
}
|
||||
|
||||
@@ -190,17 +190,24 @@ export interface KubernetesFetchError {
|
||||
statusCode?: number;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "KubernetesRequestAuth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export interface KubernetesRequestAuth {
|
||||
// (undocumented)
|
||||
google?: string;
|
||||
// (undocumented)
|
||||
oidc?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "KubernetesRequestBody" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export interface KubernetesRequestBody {
|
||||
// (undocumented)
|
||||
auth?: {
|
||||
google?: string;
|
||||
oidc?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
};
|
||||
auth?: KubernetesRequestAuth;
|
||||
// (undocumented)
|
||||
entity: Entity;
|
||||
}
|
||||
|
||||
@@ -29,13 +29,15 @@ import {
|
||||
} from '@kubernetes/client-node';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
|
||||
export interface KubernetesRequestBody {
|
||||
auth?: {
|
||||
google?: string;
|
||||
oidc?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
export interface KubernetesRequestAuth {
|
||||
google?: string;
|
||||
oidc?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface KubernetesRequestBody {
|
||||
auth?: KubernetesRequestAuth;
|
||||
entity: Entity;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user