Merge pull request #21590 from andmagom/kubernetes/newExtensionPoint
New ExtensionPoints for Kubernetes Plugin
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
---
|
||||
'@backstage/plugin-kubernetes-node': patch
|
||||
'@backstage/plugin-kubernetes-backend': patch
|
||||
---
|
||||
|
||||
The `kubernetes-node` plugin has been modified to house a new extension points for Kubernetes backend plugin;
|
||||
`KubernetesClusterSupplierExtensionPoint` is introduced .
|
||||
`kubernetesAuthStrategyExtensionPoint` is introduced .
|
||||
`kubernetesFetcherExtensionPoint` is introduced .
|
||||
`kubernetesServiceLocatorExtensionPoint` is introduced .
|
||||
|
||||
The `kubernetes-backend` plugin was modified to use this new extension point.
|
||||
@@ -3,21 +3,20 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { AuthenticationStrategy as AuthenticationStrategy_2 } from '@backstage/plugin-kubernetes-node';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { ClusterDetails as ClusterDetails_2 } from '@backstage/plugin-kubernetes-node';
|
||||
import { Config } from '@backstage/config';
|
||||
import type { CustomResourceMatcher } from '@backstage/plugin-kubernetes-common';
|
||||
import { CustomResourcesByEntity } from '@backstage/plugin-kubernetes-node';
|
||||
import { CustomResource as CustomResource_2 } from '@backstage/plugin-kubernetes-node';
|
||||
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 { KubernetesObjectsByEntity } from '@backstage/plugin-kubernetes-node';
|
||||
import { KubernetesObjectsProvider } from '@backstage/plugin-kubernetes-node';
|
||||
import * as k8sAuthTypes from '@backstage/plugin-kubernetes-node';
|
||||
import { KubernetesClustersSupplier as KubernetesClustersSupplier_2 } from '@backstage/plugin-kubernetes-node';
|
||||
import { KubernetesObjectsProvider as KubernetesObjectsProvider_2 } from '@backstage/plugin-kubernetes-node';
|
||||
import { KubernetesRequestAuth } from '@backstage/plugin-kubernetes-common';
|
||||
import type { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common';
|
||||
import { Logger } from 'winston';
|
||||
import { ObjectToFetch as ObjectToFetch_2 } from '@backstage/plugin-kubernetes-node';
|
||||
import { PermissionEvaluator } from '@backstage/plugin-permission-common';
|
||||
import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
import { RequestHandler } from 'http-proxy-middleware';
|
||||
@@ -42,19 +41,11 @@ export class AnonymousStrategy implements AuthenticationStrategy {
|
||||
validateCluster(): Error[];
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface AuthenticationStrategy {
|
||||
// (undocumented)
|
||||
getCredential(
|
||||
clusterDetails: ClusterDetails,
|
||||
authConfig: KubernetesRequestAuth,
|
||||
): Promise<KubernetesCredential>;
|
||||
// (undocumented)
|
||||
validateCluster(authMetadata: AuthMetadata): Error[];
|
||||
}
|
||||
// @public @deprecated (undocumented)
|
||||
export type AuthenticationStrategy = k8sAuthTypes.AuthenticationStrategy;
|
||||
|
||||
// @public
|
||||
export type AuthMetadata = Record<string, string>;
|
||||
// @public @deprecated (undocumented)
|
||||
export type AuthMetadata = k8sAuthTypes.AuthMetadata;
|
||||
|
||||
// @public (undocumented)
|
||||
export class AwsIamStrategy implements AuthenticationStrategy {
|
||||
@@ -74,36 +65,17 @@ export class AzureIdentityStrategy implements AuthenticationStrategy {
|
||||
validateCluster(): Error[];
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ClusterDetails {
|
||||
// (undocumented)
|
||||
authMetadata: AuthMetadata;
|
||||
// (undocumented)
|
||||
caData?: string | undefined;
|
||||
// (undocumented)
|
||||
caFile?: string | undefined;
|
||||
customResources?: CustomResourceMatcher[];
|
||||
dashboardApp?: string;
|
||||
dashboardParameters?: JsonObject;
|
||||
dashboardUrl?: string;
|
||||
name: string;
|
||||
skipMetricsLookup?: boolean;
|
||||
// (undocumented)
|
||||
skipTLSVerify?: boolean;
|
||||
// (undocumented)
|
||||
url: string;
|
||||
}
|
||||
// @public @deprecated (undocumented)
|
||||
export type ClusterDetails = k8sAuthTypes.ClusterDetails;
|
||||
|
||||
// @public @deprecated
|
||||
export function createRouter(options: RouterOptions): Promise<express.Router>;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface CustomResource extends ObjectToFetch {
|
||||
// (undocumented)
|
||||
objectType: 'customresources';
|
||||
}
|
||||
// @public @deprecated (undocumented)
|
||||
export type CustomResource = k8sAuthTypes.CustomResource;
|
||||
|
||||
export { CustomResourcesByEntity };
|
||||
// @public @deprecated (undocumented)
|
||||
export type CustomResourcesByEntity = k8sAuthTypes.CustomResourcesByEntity;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DEFAULT_OBJECTS: ObjectToFetch[];
|
||||
@@ -127,13 +99,8 @@ export type DispatchStrategyOptions = {
|
||||
};
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface FetchResponseWrapper {
|
||||
// (undocumented)
|
||||
errors: KubernetesFetchError[];
|
||||
// (undocumented)
|
||||
responses: FetchResponse[];
|
||||
}
|
||||
// @public @deprecated (undocumented)
|
||||
export type FetchResponseWrapper = k8sAuthTypes.FetchResponseWrapper;
|
||||
|
||||
// @public (undocumented)
|
||||
export class GoogleServiceAccountStrategy implements AuthenticationStrategy {
|
||||
@@ -169,37 +136,37 @@ export class KubernetesBuilder {
|
||||
build(): KubernetesBuilderReturn;
|
||||
// (undocumented)
|
||||
protected buildAuthStrategyMap(): {
|
||||
[key: string]: AuthenticationStrategy;
|
||||
[key: string]: AuthenticationStrategy_2;
|
||||
};
|
||||
// (undocumented)
|
||||
protected buildClusterSupplier(
|
||||
refreshInterval: Duration,
|
||||
): KubernetesClustersSupplier;
|
||||
): KubernetesClustersSupplier_2;
|
||||
// (undocumented)
|
||||
protected buildCustomResources(): CustomResource[];
|
||||
protected buildCustomResources(): CustomResource_2[];
|
||||
// (undocumented)
|
||||
protected buildFetcher(): KubernetesFetcher;
|
||||
// (undocumented)
|
||||
protected buildHttpServiceLocator(
|
||||
_clusterSupplier: KubernetesClustersSupplier,
|
||||
_clusterSupplier: KubernetesClustersSupplier_2,
|
||||
): KubernetesServiceLocator;
|
||||
// (undocumented)
|
||||
protected buildMultiTenantServiceLocator(
|
||||
clusterSupplier: KubernetesClustersSupplier,
|
||||
clusterSupplier: KubernetesClustersSupplier_2,
|
||||
): KubernetesServiceLocator;
|
||||
// (undocumented)
|
||||
protected buildObjectsProvider(
|
||||
options: KubernetesObjectsProviderOptions,
|
||||
): KubernetesObjectsProvider;
|
||||
): KubernetesObjectsProvider_2;
|
||||
// (undocumented)
|
||||
protected buildProxy(
|
||||
logger: Logger,
|
||||
clusterSupplier: KubernetesClustersSupplier,
|
||||
clusterSupplier: KubernetesClustersSupplier_2,
|
||||
): KubernetesProxy;
|
||||
// (undocumented)
|
||||
protected buildRouter(
|
||||
objectsProvider: KubernetesObjectsProvider,
|
||||
clusterSupplier: KubernetesClustersSupplier,
|
||||
objectsProvider: KubernetesObjectsProvider_2,
|
||||
clusterSupplier: KubernetesClustersSupplier_2,
|
||||
catalogApi: CatalogApi,
|
||||
proxy: KubernetesProxy,
|
||||
permissionApi: PermissionEvaluator,
|
||||
@@ -207,11 +174,11 @@ export class KubernetesBuilder {
|
||||
// (undocumented)
|
||||
protected buildServiceLocator(
|
||||
method: ServiceLocatorMethod,
|
||||
clusterSupplier: KubernetesClustersSupplier,
|
||||
clusterSupplier: KubernetesClustersSupplier_2,
|
||||
): KubernetesServiceLocator;
|
||||
// (undocumented)
|
||||
protected buildSingleTenantServiceLocator(
|
||||
clusterSupplier: KubernetesClustersSupplier,
|
||||
clusterSupplier: KubernetesClustersSupplier_2,
|
||||
): KubernetesServiceLocator;
|
||||
// (undocumented)
|
||||
static createBuilder(env: KubernetesEnvironment): KubernetesBuilder;
|
||||
@@ -219,26 +186,26 @@ export class KubernetesBuilder {
|
||||
protected readonly env: KubernetesEnvironment;
|
||||
// (undocumented)
|
||||
protected fetchClusterDetails(
|
||||
clusterSupplier: KubernetesClustersSupplier,
|
||||
): Promise<ClusterDetails[]>;
|
||||
clusterSupplier: KubernetesClustersSupplier_2,
|
||||
): Promise<ClusterDetails_2[]>;
|
||||
// (undocumented)
|
||||
protected getAuthStrategyMap(): {
|
||||
[key: string]: AuthenticationStrategy;
|
||||
[key: string]: AuthenticationStrategy_2;
|
||||
};
|
||||
// (undocumented)
|
||||
protected getClusterSupplier(): KubernetesClustersSupplier;
|
||||
protected getClusterSupplier(): KubernetesClustersSupplier_2;
|
||||
// (undocumented)
|
||||
protected getFetcher(): KubernetesFetcher;
|
||||
// (undocumented)
|
||||
protected getObjectsProvider(
|
||||
options: KubernetesObjectsProviderOptions,
|
||||
): KubernetesObjectsProvider;
|
||||
): KubernetesObjectsProvider_2;
|
||||
// (undocumented)
|
||||
protected getObjectTypesToFetch(): ObjectToFetch[] | undefined;
|
||||
protected getObjectTypesToFetch(): ObjectToFetch_2[] | undefined;
|
||||
// (undocumented)
|
||||
protected getProxy(
|
||||
logger: Logger,
|
||||
clusterSupplier: KubernetesClustersSupplier,
|
||||
clusterSupplier: KubernetesClustersSupplier_2,
|
||||
): KubernetesProxy;
|
||||
// (undocumented)
|
||||
protected getServiceLocator(): KubernetesServiceLocator;
|
||||
@@ -249,13 +216,13 @@ export class KubernetesBuilder {
|
||||
[key: string]: AuthenticationStrategy;
|
||||
}): void;
|
||||
// (undocumented)
|
||||
setClusterSupplier(clusterSupplier?: KubernetesClustersSupplier): this;
|
||||
setClusterSupplier(clusterSupplier?: KubernetesClustersSupplier_2): this;
|
||||
// (undocumented)
|
||||
setDefaultClusterRefreshInterval(refreshInterval: Duration): this;
|
||||
// (undocumented)
|
||||
setFetcher(fetcher?: KubernetesFetcher): this;
|
||||
// (undocumented)
|
||||
setObjectsProvider(objectsProvider?: KubernetesObjectsProvider): this;
|
||||
setObjectsProvider(objectsProvider?: KubernetesObjectsProvider_2): this;
|
||||
// (undocumented)
|
||||
setProxy(proxy?: KubernetesProxy): this;
|
||||
// (undocumented)
|
||||
@@ -265,31 +232,23 @@ export class KubernetesBuilder {
|
||||
// @public
|
||||
export type KubernetesBuilderReturn = Promise<{
|
||||
router: express.Router;
|
||||
clusterSupplier: KubernetesClustersSupplier;
|
||||
clusterSupplier: KubernetesClustersSupplier_2;
|
||||
customResources: CustomResource[];
|
||||
fetcher: KubernetesFetcher;
|
||||
proxy: KubernetesProxy;
|
||||
objectsProvider: KubernetesObjectsProvider;
|
||||
objectsProvider: KubernetesObjectsProvider_2;
|
||||
serviceLocator: KubernetesServiceLocator;
|
||||
authStrategyMap: {
|
||||
[key: string]: AuthenticationStrategy;
|
||||
};
|
||||
}>;
|
||||
|
||||
// @public
|
||||
export interface KubernetesClustersSupplier {
|
||||
getClusters(): Promise<ClusterDetails[]>;
|
||||
}
|
||||
// @public @deprecated (undocumented)
|
||||
export type KubernetesClustersSupplier =
|
||||
k8sAuthTypes.KubernetesClustersSupplier;
|
||||
|
||||
// @public
|
||||
export type KubernetesCredential =
|
||||
| {
|
||||
type: 'bearer token';
|
||||
token: string;
|
||||
}
|
||||
| {
|
||||
type: 'anonymous';
|
||||
};
|
||||
// @public @deprecated (undocumented)
|
||||
export type KubernetesCredential = k8sAuthTypes.KubernetesCredential;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface KubernetesEnvironment {
|
||||
@@ -303,57 +262,30 @@ export interface KubernetesEnvironment {
|
||||
permissions: PermissionEvaluator;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface KubernetesFetcher {
|
||||
// (undocumented)
|
||||
fetchObjectsForService(
|
||||
params: ObjectFetchParams,
|
||||
): Promise<FetchResponseWrapper>;
|
||||
// (undocumented)
|
||||
fetchPodMetricsByNamespaces(
|
||||
clusterDetails: ClusterDetails,
|
||||
credential: KubernetesCredential,
|
||||
namespaces: Set<string>,
|
||||
labelSelector?: string,
|
||||
): Promise<FetchResponseWrapper>;
|
||||
}
|
||||
// @public @deprecated (undocumented)
|
||||
export type KubernetesFetcher = k8sAuthTypes.KubernetesFetcher;
|
||||
|
||||
export { KubernetesObjectsByEntity };
|
||||
|
||||
export { KubernetesObjectsProvider };
|
||||
// @public @deprecated (undocumented)
|
||||
export type KubernetesObjectsProvider = k8sAuthTypes.KubernetesObjectsProvider;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface KubernetesObjectsProviderOptions {
|
||||
// (undocumented)
|
||||
config: Config;
|
||||
// (undocumented)
|
||||
customResources: CustomResource[];
|
||||
customResources: k8sAuthTypes.CustomResource[];
|
||||
// (undocumented)
|
||||
fetcher: KubernetesFetcher;
|
||||
fetcher: k8sAuthTypes.KubernetesFetcher;
|
||||
// (undocumented)
|
||||
logger: Logger;
|
||||
// (undocumented)
|
||||
objectTypesToFetch?: ObjectToFetch[];
|
||||
objectTypesToFetch?: k8sAuthTypes.ObjectToFetch[];
|
||||
// (undocumented)
|
||||
serviceLocator: KubernetesServiceLocator;
|
||||
serviceLocator: k8sAuthTypes.KubernetesServiceLocator;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type KubernetesObjectTypes =
|
||||
| 'pods'
|
||||
| 'services'
|
||||
| 'configmaps'
|
||||
| 'deployments'
|
||||
| 'limitranges'
|
||||
| 'resourcequotas'
|
||||
| 'replicasets'
|
||||
| 'horizontalpodautoscalers'
|
||||
| 'jobs'
|
||||
| 'cronjobs'
|
||||
| 'ingresses'
|
||||
| 'customresources'
|
||||
| 'statefulsets'
|
||||
| 'daemonsets';
|
||||
// @public @deprecated (undocumented)
|
||||
export type KubernetesObjectTypes = k8sAuthTypes.KubernetesObjectTypes;
|
||||
|
||||
// @public
|
||||
export class KubernetesProxy {
|
||||
@@ -376,49 +308,17 @@ export type KubernetesProxyOptions = {
|
||||
authStrategy: AuthenticationStrategy;
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface KubernetesServiceLocator {
|
||||
// (undocumented)
|
||||
getClustersByEntity(
|
||||
entity: Entity,
|
||||
requestContext: ServiceLocatorRequestContext,
|
||||
): Promise<{
|
||||
clusters: ClusterDetails[];
|
||||
}>;
|
||||
}
|
||||
// @public @deprecated (undocumented)
|
||||
export type KubernetesServiceLocator = k8sAuthTypes.KubernetesServiceLocator;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ObjectFetchParams {
|
||||
// (undocumented)
|
||||
clusterDetails: ClusterDetails;
|
||||
// (undocumented)
|
||||
credential: KubernetesCredential;
|
||||
// (undocumented)
|
||||
customResources: CustomResource[];
|
||||
// (undocumented)
|
||||
labelSelector?: string;
|
||||
// (undocumented)
|
||||
namespace?: string;
|
||||
// (undocumented)
|
||||
objectTypesToFetch: Set<ObjectToFetch>;
|
||||
// (undocumented)
|
||||
serviceId: string;
|
||||
}
|
||||
// @public @deprecated (undocumented)
|
||||
export type ObjectFetchParams = k8sAuthTypes.ObjectFetchParams;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ObjectsByEntityRequest = KubernetesRequestBody;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ObjectToFetch {
|
||||
// (undocumented)
|
||||
apiVersion: string;
|
||||
// (undocumented)
|
||||
group: string;
|
||||
// (undocumented)
|
||||
objectType: KubernetesObjectTypes;
|
||||
// (undocumented)
|
||||
plural: string;
|
||||
}
|
||||
// @public @deprecated (undocumented)
|
||||
export type ObjectToFetch = k8sAuthTypes.ObjectToFetch;
|
||||
|
||||
// @public (undocumented)
|
||||
export class OidcStrategy implements AuthenticationStrategy {
|
||||
@@ -458,13 +358,9 @@ export class ServiceAccountStrategy implements AuthenticationStrategy {
|
||||
// @public (undocumented)
|
||||
export type ServiceLocatorMethod = 'multiTenant' | 'singleTenant' | 'http';
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ServiceLocatorRequestContext {
|
||||
// (undocumented)
|
||||
customResources: CustomResourceMatcher[];
|
||||
// (undocumented)
|
||||
objectTypesToFetch: Set<ObjectToFetch>;
|
||||
}
|
||||
// @public @deprecated (undocumented)
|
||||
export type ServiceLocatorRequestContext =
|
||||
k8sAuthTypes.ServiceLocatorRequestContext;
|
||||
|
||||
// @public (undocumented)
|
||||
export type SigningCreds = {
|
||||
|
||||
@@ -90,6 +90,8 @@
|
||||
"@backstage/backend-app-api": "workspace:^",
|
||||
"@backstage/backend-test-utils": "workspace:^",
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/plugin-permission-backend": "workspace:^",
|
||||
"@backstage/plugin-permission-backend-module-allow-all-policy": "workspace:^",
|
||||
"@types/aws4": "^1.5.1",
|
||||
"msw": "^1.0.0",
|
||||
"supertest": "^6.1.3",
|
||||
|
||||
@@ -14,25 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AuthMetadata, ClusterDetails } from '../types/types';
|
||||
import { KubernetesRequestAuth } from '@backstage/plugin-kubernetes-common';
|
||||
import * as k8sAuthTypes from '@backstage/plugin-kubernetes-node';
|
||||
|
||||
// TODO remove this re-export as a breaking change after a couple of releases
|
||||
/**
|
||||
* @public @deprecated Import it from \@backstage/plugin-kubernetes-node instead
|
||||
*/
|
||||
export type AuthenticationStrategy = k8sAuthTypes.AuthenticationStrategy;
|
||||
|
||||
/**
|
||||
* Authentication data used to make a request to Kubernetes
|
||||
* @public
|
||||
* @public @deprecated Import it from \@backstage/plugin-kubernetes-node instead
|
||||
*/
|
||||
export type KubernetesCredential =
|
||||
| { type: 'bearer token'; token: string }
|
||||
| { type: 'anonymous' };
|
||||
|
||||
/**
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface AuthenticationStrategy {
|
||||
getCredential(
|
||||
clusterDetails: ClusterDetails,
|
||||
authConfig: KubernetesRequestAuth,
|
||||
): Promise<KubernetesCredential>;
|
||||
validateCluster(authMetadata: AuthMetadata): Error[];
|
||||
}
|
||||
export type KubernetesCredential = k8sAuthTypes.KubernetesCredential;
|
||||
|
||||
@@ -26,6 +26,20 @@ import {
|
||||
KubernetesObjectsProviderExtensionPoint,
|
||||
kubernetesObjectsProviderExtensionPoint,
|
||||
KubernetesObjectsProvider,
|
||||
KubernetesClusterSupplierExtensionPoint,
|
||||
kubernetesClusterSupplierExtensionPoint,
|
||||
KubernetesClustersSupplier,
|
||||
KubernetesAuthStrategyExtensionPoint,
|
||||
AuthenticationStrategy,
|
||||
kubernetesAuthStrategyExtensionPoint,
|
||||
KubernetesFetcher,
|
||||
KubernetesServiceLocatorExtensionPoint,
|
||||
KubernetesServiceLocator,
|
||||
kubernetesServiceLocatorExtensionPoint,
|
||||
} from '@backstage/plugin-kubernetes-node';
|
||||
import {
|
||||
KubernetesFetcherExtensionPoint,
|
||||
kubernetesFetcherExtensionPoint,
|
||||
} from '@backstage/plugin-kubernetes-node';
|
||||
|
||||
class ObjectsProvider implements KubernetesObjectsProviderExtensionPoint {
|
||||
@@ -45,6 +59,86 @@ class ObjectsProvider implements KubernetesObjectsProviderExtensionPoint {
|
||||
}
|
||||
}
|
||||
|
||||
class ClusterSuplier implements KubernetesClusterSupplierExtensionPoint {
|
||||
private clusterSupplier: KubernetesClustersSupplier | undefined;
|
||||
|
||||
getClusterSupplier() {
|
||||
return this.clusterSupplier;
|
||||
}
|
||||
|
||||
addClusterSupplier(clusterSupplier: KubernetesClustersSupplier) {
|
||||
if (this.clusterSupplier) {
|
||||
throw new Error(
|
||||
'Multiple Kubernetes Cluster Suppliers is not supported at this time',
|
||||
);
|
||||
}
|
||||
this.clusterSupplier = clusterSupplier;
|
||||
}
|
||||
}
|
||||
|
||||
class Fetcher implements KubernetesFetcherExtensionPoint {
|
||||
private fetcher: KubernetesFetcher | undefined;
|
||||
|
||||
getFetcher() {
|
||||
return this.fetcher;
|
||||
}
|
||||
|
||||
addFetcher(fetcher: KubernetesFetcher) {
|
||||
if (this.fetcher) {
|
||||
throw new Error(
|
||||
'Multiple Kubernetes Fetchers is not supported at this time',
|
||||
);
|
||||
}
|
||||
this.fetcher = fetcher;
|
||||
}
|
||||
}
|
||||
|
||||
class ServiceLocator implements KubernetesServiceLocatorExtensionPoint {
|
||||
private serviceLocator: KubernetesServiceLocator | undefined;
|
||||
|
||||
getServiceLocator() {
|
||||
return this.serviceLocator;
|
||||
}
|
||||
|
||||
addServiceLocator(serviceLocator: KubernetesServiceLocator) {
|
||||
if (this.serviceLocator) {
|
||||
throw new Error(
|
||||
'Multiple Kubernetes Service Locators is not supported at this time',
|
||||
);
|
||||
}
|
||||
this.serviceLocator = serviceLocator;
|
||||
}
|
||||
}
|
||||
|
||||
class AuthStrategy implements KubernetesAuthStrategyExtensionPoint {
|
||||
private authStrategies: Array<{
|
||||
key: string;
|
||||
strategy: AuthenticationStrategy;
|
||||
}>;
|
||||
|
||||
constructor() {
|
||||
this.authStrategies = new Array<{
|
||||
key: string;
|
||||
strategy: AuthenticationStrategy;
|
||||
}>();
|
||||
}
|
||||
|
||||
static addAuthStrategiesFromArray(
|
||||
authStrategies: Array<{ key: string; strategy: AuthenticationStrategy }>,
|
||||
builder: KubernetesBuilder,
|
||||
) {
|
||||
authStrategies.forEach(st => builder.addAuthStrategy(st.key, st.strategy));
|
||||
}
|
||||
|
||||
getAuthenticationStrategies() {
|
||||
return this.authStrategies;
|
||||
}
|
||||
|
||||
addAuthStrategy(key: string, authStrategy: AuthenticationStrategy) {
|
||||
this.authStrategies.push({ key, strategy: authStrategy });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the backend plugin that provides the Kubernetes integration.
|
||||
* @alpha
|
||||
@@ -53,10 +147,31 @@ class ObjectsProvider implements KubernetesObjectsProviderExtensionPoint {
|
||||
export const kubernetesPlugin = createBackendPlugin({
|
||||
pluginId: 'kubernetes',
|
||||
register(env) {
|
||||
const extensionPoint = new ObjectsProvider();
|
||||
const extPointObjectsProvider = new ObjectsProvider();
|
||||
const extPointClusterSuplier = new ClusterSuplier();
|
||||
const extPointAuthStrategy = new AuthStrategy();
|
||||
const extPointFetcher = new Fetcher();
|
||||
const extPointServiceLocator = new ServiceLocator();
|
||||
|
||||
env.registerExtensionPoint(
|
||||
kubernetesObjectsProviderExtensionPoint,
|
||||
extensionPoint,
|
||||
extPointObjectsProvider,
|
||||
);
|
||||
env.registerExtensionPoint(
|
||||
kubernetesClusterSupplierExtensionPoint,
|
||||
extPointClusterSuplier,
|
||||
);
|
||||
env.registerExtensionPoint(
|
||||
kubernetesAuthStrategyExtensionPoint,
|
||||
extPointAuthStrategy,
|
||||
);
|
||||
env.registerExtensionPoint(
|
||||
kubernetesFetcherExtensionPoint,
|
||||
extPointFetcher,
|
||||
);
|
||||
env.registerExtensionPoint(
|
||||
kubernetesServiceLocatorExtensionPoint,
|
||||
extPointServiceLocator,
|
||||
);
|
||||
|
||||
env.registerInit({
|
||||
@@ -70,14 +185,22 @@ export const kubernetesPlugin = createBackendPlugin({
|
||||
async init({ http, logger, config, catalogApi, permissions }) {
|
||||
const winstonLogger = loggerToWinstonLogger(logger);
|
||||
// TODO: expose all of the customization & extension points of the builder here
|
||||
const { router } = await KubernetesBuilder.createBuilder({
|
||||
const builder: KubernetesBuilder = KubernetesBuilder.createBuilder({
|
||||
logger: winstonLogger,
|
||||
config,
|
||||
catalogApi,
|
||||
permissions,
|
||||
})
|
||||
.setObjectsProvider(extensionPoint.getObjectsProvider())
|
||||
.build();
|
||||
.setObjectsProvider(extPointObjectsProvider.getObjectsProvider())
|
||||
.setClusterSupplier(extPointClusterSuplier.getClusterSupplier())
|
||||
.setFetcher(extPointFetcher.getFetcher())
|
||||
.setServiceLocator(extPointServiceLocator.getServiceLocator());
|
||||
|
||||
AuthStrategy.addAuthStrategiesFromArray(
|
||||
extPointAuthStrategy.getAuthenticationStrategies(),
|
||||
builder,
|
||||
);
|
||||
const { router } = await builder.build();
|
||||
http.use(router);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -14,106 +14,181 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Config, ConfigReader } from '@backstage/config';
|
||||
import {
|
||||
ANNOTATION_KUBERNETES_AUTH_PROVIDER,
|
||||
ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER,
|
||||
ObjectsByEntityResponse,
|
||||
KubernetesRequestAuth,
|
||||
} from '@backstage/plugin-kubernetes-common';
|
||||
import express from 'express';
|
||||
import request from 'supertest';
|
||||
import {
|
||||
ClusterDetails,
|
||||
FetchResponseWrapper,
|
||||
KubernetesClustersSupplier,
|
||||
KubernetesFetcher,
|
||||
KubernetesServiceLocator,
|
||||
ObjectFetchParams,
|
||||
} from '../types/types';
|
||||
import { KubernetesCredential } from '../auth/types';
|
||||
import { KubernetesBuilder } from './KubernetesBuilder';
|
||||
import { KubernetesFanOutHandler } from './KubernetesFanOutHandler';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import {
|
||||
HEADER_KUBERNETES_CLUSTER,
|
||||
HEADER_KUBERNETES_AUTH,
|
||||
} from './KubernetesProxy';
|
||||
import { setupServer } from 'msw/node';
|
||||
import { setupRequestMockHandlers } from '@backstage/backend-test-utils';
|
||||
import {
|
||||
ServiceMock,
|
||||
mockServices,
|
||||
setupRequestMockHandlers,
|
||||
startTestBackend,
|
||||
} from '@backstage/backend-test-utils';
|
||||
import { rest } from 'msw';
|
||||
import {
|
||||
AuthorizeResult,
|
||||
PermissionEvaluator,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import {
|
||||
PermissionsService,
|
||||
createBackendModule,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import {
|
||||
KubernetesObjectsProvider,
|
||||
kubernetesAuthStrategyExtensionPoint,
|
||||
kubernetesClusterSupplierExtensionPoint,
|
||||
kubernetesObjectsProviderExtensionPoint,
|
||||
kubernetesFetcherExtensionPoint,
|
||||
kubernetesServiceLocatorExtensionPoint,
|
||||
} from '@backstage/plugin-kubernetes-node';
|
||||
import { ExtendedHttpServer } from '@backstage/backend-app-api';
|
||||
|
||||
describe('KubernetesBuilder', () => {
|
||||
let app: express.Express;
|
||||
let kubernetesFanOutHandler: jest.Mocked<KubernetesFanOutHandler>;
|
||||
let config: Config;
|
||||
let catalogApi: CatalogApi;
|
||||
let permissions: jest.Mocked<PermissionEvaluator>;
|
||||
let app: ExtendedHttpServer;
|
||||
let objectsProviderMock: KubernetesObjectsProvider;
|
||||
const happyK8SResult = {
|
||||
items: [
|
||||
{
|
||||
clusterOne: {
|
||||
pods: [
|
||||
{
|
||||
metadata: {
|
||||
name: 'pod1',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
} as any;
|
||||
const policyMock: jest.Mocked<PermissionEvaluator> = {
|
||||
authorize: jest.fn(),
|
||||
authorizeConditional: jest.fn(),
|
||||
};
|
||||
const permissionsMock: ServiceMock<PermissionsService> =
|
||||
mockServices.permissions.mock(policyMock);
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.resetAllMocks();
|
||||
const logger = getVoidLogger();
|
||||
config = new ConfigReader({
|
||||
kubernetes: {
|
||||
serviceLocatorMethod: { type: 'multiTenant' },
|
||||
clusterLocatorMethods: [{ type: 'config', clusters: [] }],
|
||||
},
|
||||
|
||||
objectsProviderMock = {
|
||||
getKubernetesObjectsByEntity: jest.fn().mockImplementation(_ => {
|
||||
return Promise.resolve(happyK8SResult);
|
||||
}),
|
||||
getCustomResourcesByEntity: jest.fn().mockImplementation(_ => {
|
||||
return Promise.resolve(happyK8SResult);
|
||||
}),
|
||||
};
|
||||
|
||||
const clusterSupplierMock = {
|
||||
getClusters: jest.fn().mockImplementation(_ => {
|
||||
return Promise.resolve([
|
||||
{
|
||||
name: 'some-cluster',
|
||||
url: 'https://localhost:1234',
|
||||
authMetadata: {
|
||||
[ANNOTATION_KUBERNETES_AUTH_PROVIDER]: 'serviceAccount',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'some-other-cluster',
|
||||
url: 'https://localhost:1235',
|
||||
authMetadata: {
|
||||
[ANNOTATION_KUBERNETES_AUTH_PROVIDER]: 'oidc',
|
||||
[ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER]: 'google',
|
||||
},
|
||||
},
|
||||
]);
|
||||
}),
|
||||
};
|
||||
|
||||
jest.mock('@backstage/catalog-client', () => ({
|
||||
CatalogClient: jest.fn().mockImplementation(() => ({
|
||||
getEntityByRef: jest.fn().mockImplementation(entityRef => {
|
||||
if (entityRef.name === 'noentity') {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
return Promise.resolve({
|
||||
kind: entityRef.kind,
|
||||
metadata: {
|
||||
name: entityRef.name,
|
||||
namespace: entityRef.namespace,
|
||||
},
|
||||
} as Entity);
|
||||
}),
|
||||
})),
|
||||
}));
|
||||
|
||||
const { server } = await startTestBackend({
|
||||
features: [
|
||||
mockServices.rootConfig.factory({
|
||||
data: {
|
||||
kubernetes: {
|
||||
serviceLocatorMethod: {
|
||||
type: 'multiTenant',
|
||||
},
|
||||
clusterLocatorMethods: [
|
||||
{
|
||||
type: 'config',
|
||||
clusters: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
import('@backstage/plugin-kubernetes-backend/alpha'),
|
||||
import('@backstage/plugin-permission-backend/alpha'),
|
||||
import('@backstage/plugin-permission-backend-module-allow-all-policy'),
|
||||
createBackendModule({
|
||||
pluginId: 'kubernetes',
|
||||
moduleId: 'testObjectsProvider',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: { extension: kubernetesObjectsProviderExtensionPoint },
|
||||
async init({ extension }) {
|
||||
extension.addObjectsProvider(objectsProviderMock);
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
createBackendModule({
|
||||
pluginId: 'kubernetes',
|
||||
moduleId: 'testClusterSupplier',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: { extension: kubernetesClusterSupplierExtensionPoint },
|
||||
async init({ extension }) {
|
||||
extension.addClusterSupplier(clusterSupplierMock);
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const clusters: ClusterDetails[] = [
|
||||
{
|
||||
name: 'some-cluster',
|
||||
url: 'https://localhost:1234',
|
||||
authMetadata: {
|
||||
[ANNOTATION_KUBERNETES_AUTH_PROVIDER]: 'serviceAccount',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'some-other-cluster',
|
||||
url: 'https://localhost:1235',
|
||||
authMetadata: {
|
||||
[ANNOTATION_KUBERNETES_AUTH_PROVIDER]: 'oidc',
|
||||
[ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER]: 'google',
|
||||
},
|
||||
},
|
||||
];
|
||||
const clusterSupplier: KubernetesClustersSupplier = {
|
||||
async getClusters() {
|
||||
return clusters;
|
||||
},
|
||||
};
|
||||
|
||||
kubernetesFanOutHandler = {
|
||||
getKubernetesObjectsByEntity: jest.fn(),
|
||||
} as any;
|
||||
|
||||
permissions = {
|
||||
authorize: jest.fn(),
|
||||
authorizeConditional: jest.fn(),
|
||||
};
|
||||
|
||||
const { router } = await KubernetesBuilder.createBuilder({
|
||||
config,
|
||||
logger,
|
||||
catalogApi,
|
||||
permissions,
|
||||
})
|
||||
.setObjectsProvider(kubernetesFanOutHandler)
|
||||
.setClusterSupplier(clusterSupplier)
|
||||
.build();
|
||||
|
||||
app = express().use(router);
|
||||
app = server;
|
||||
});
|
||||
|
||||
describe('get /clusters', () => {
|
||||
it('happy path: lists clusters', async () => {
|
||||
const response = await request(app).get('/clusters');
|
||||
const response = await request(app).get('/api/kubernetes/clusters');
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toStrictEqual({
|
||||
@@ -133,70 +208,41 @@ describe('KubernetesBuilder', () => {
|
||||
});
|
||||
describe('post /services/:serviceId', () => {
|
||||
it('happy path: lists kubernetes objects without auth in request body', async () => {
|
||||
const result = {
|
||||
clusterOne: {
|
||||
pods: [
|
||||
{
|
||||
metadata: {
|
||||
name: 'pod1',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
kubernetesFanOutHandler.getKubernetesObjectsByEntity.mockReturnValueOnce(
|
||||
Promise.resolve(result),
|
||||
const response = await request(app).post(
|
||||
'/api/kubernetes/services/test-service',
|
||||
);
|
||||
|
||||
const response = await request(app).post('/services/test-service');
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual(result);
|
||||
expect(response.body).toEqual(happyK8SResult);
|
||||
});
|
||||
|
||||
it('happy path: lists kubernetes objects with auth in request body', async () => {
|
||||
const result = {
|
||||
clusterOne: {
|
||||
pods: [
|
||||
{
|
||||
metadata: {
|
||||
name: 'pod1',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
kubernetesFanOutHandler.getKubernetesObjectsByEntity.mockReturnValueOnce(
|
||||
Promise.resolve(result),
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/services/test-service')
|
||||
.post('/api/kubernetes/services/test-service')
|
||||
.send({
|
||||
auth: {
|
||||
google: 'google_token_123',
|
||||
},
|
||||
})
|
||||
.set('Content-Type', 'application/json');
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual(result);
|
||||
expect(response.body).toEqual(happyK8SResult);
|
||||
});
|
||||
|
||||
it('internal error: lists kubernetes objects', async () => {
|
||||
kubernetesFanOutHandler.getKubernetesObjectsByEntity.mockRejectedValue(
|
||||
Error('some internal error'),
|
||||
);
|
||||
objectsProviderMock.getKubernetesObjectsByEntity = jest
|
||||
.fn()
|
||||
.mockRejectedValue(Error('some internal error'));
|
||||
|
||||
const response = await request(app).post('/services/test-service');
|
||||
const response = await request(app).post(
|
||||
'/api/kubernetes/services/test-service',
|
||||
);
|
||||
|
||||
expect(response.status).toEqual(500);
|
||||
expect(response.body).toEqual({ error: 'some internal error' });
|
||||
});
|
||||
|
||||
it('custom service locator', async () => {
|
||||
const logger = getVoidLogger();
|
||||
const someCluster: ClusterDetails = {
|
||||
const someCluster = {
|
||||
name: 'some-cluster',
|
||||
url: 'https://localhost:1234',
|
||||
authMetadata: {
|
||||
@@ -212,11 +258,13 @@ describe('KubernetesBuilder', () => {
|
||||
authMetadata: { [ANNOTATION_KUBERNETES_AUTH_PROVIDER]: 'google' },
|
||||
},
|
||||
];
|
||||
const clusterSupplier: KubernetesClustersSupplier = {
|
||||
async getClusters() {
|
||||
return clusters;
|
||||
},
|
||||
|
||||
const clusterSupplierMock = {
|
||||
getClusters: jest.fn().mockImplementation(_ => {
|
||||
return Promise.resolve(clusters);
|
||||
}),
|
||||
};
|
||||
|
||||
const pod = {
|
||||
metadata: {
|
||||
name: 'pod1',
|
||||
@@ -240,7 +288,7 @@ describe('KubernetesBuilder', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const serviceLocator: KubernetesServiceLocator = {
|
||||
const mockServiceLocator: KubernetesServiceLocator = {
|
||||
getClustersByEntity(
|
||||
_entity: Entity,
|
||||
): Promise<{ clusters: ClusterDetails[] }> {
|
||||
@@ -248,7 +296,7 @@ describe('KubernetesBuilder', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const fetcher: KubernetesFetcher = {
|
||||
const mockFetcher: KubernetesFetcher = {
|
||||
fetchPodMetricsByNamespaces(
|
||||
_clusterDetails: ClusterDetails,
|
||||
_credential: KubernetesCredential,
|
||||
@@ -271,20 +319,67 @@ describe('KubernetesBuilder', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const { router } = await KubernetesBuilder.createBuilder({
|
||||
logger,
|
||||
config,
|
||||
catalogApi,
|
||||
permissions,
|
||||
})
|
||||
.setClusterSupplier(clusterSupplier)
|
||||
.setServiceLocator(serviceLocator)
|
||||
.setFetcher(fetcher)
|
||||
.build();
|
||||
app = express().use(router);
|
||||
const { server } = await startTestBackend({
|
||||
features: [
|
||||
mockServices.rootConfig.factory({
|
||||
data: {
|
||||
kubernetes: {
|
||||
serviceLocatorMethod: {
|
||||
type: 'multiTenant',
|
||||
},
|
||||
clusterLocatorMethods: [
|
||||
{
|
||||
type: 'config',
|
||||
clusters: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
import('@backstage/plugin-kubernetes-backend/alpha'),
|
||||
createBackendModule({
|
||||
pluginId: 'kubernetes',
|
||||
moduleId: 'testClusterSupplier',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: { extension: kubernetesClusterSupplierExtensionPoint },
|
||||
async init({ extension }) {
|
||||
extension.addClusterSupplier(clusterSupplierMock);
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
createBackendModule({
|
||||
pluginId: 'kubernetes',
|
||||
moduleId: 'testFetcher',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: { extension: kubernetesFetcherExtensionPoint },
|
||||
async init({ extension }) {
|
||||
extension.addFetcher(mockFetcher);
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
createBackendModule({
|
||||
pluginId: 'kubernetes',
|
||||
moduleId: 'testServiceLocator',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: { extension: kubernetesServiceLocatorExtensionPoint },
|
||||
async init({ extension }) {
|
||||
extension.addServiceLocator(mockServiceLocator);
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
app = server;
|
||||
|
||||
const response = await request(app)
|
||||
.post('/services/test-service')
|
||||
.post('/api/kubernetes/services/test-service')
|
||||
.send({
|
||||
entity: {
|
||||
metadata: {
|
||||
@@ -298,9 +393,6 @@ describe('KubernetesBuilder', () => {
|
||||
});
|
||||
|
||||
it('reads auth data for custom strategy', async () => {
|
||||
permissions.authorize.mockResolvedValue([
|
||||
{ result: AuthorizeResult.ALLOW },
|
||||
]);
|
||||
const mockFetcher = {
|
||||
fetchPodMetricsByNamespaces: jest
|
||||
.fn()
|
||||
@@ -312,43 +404,93 @@ describe('KubernetesBuilder', () => {
|
||||
],
|
||||
}),
|
||||
};
|
||||
const { router } = await KubernetesBuilder.createBuilder({
|
||||
logger: getVoidLogger(),
|
||||
config,
|
||||
catalogApi,
|
||||
permissions,
|
||||
})
|
||||
.addAuthStrategy('custom', {
|
||||
getCredential: jest
|
||||
.fn<
|
||||
Promise<KubernetesCredential>,
|
||||
[ClusterDetails, KubernetesRequestAuth]
|
||||
>()
|
||||
.mockImplementation(async (_, requestAuth) => ({
|
||||
type: 'bearer token',
|
||||
token: requestAuth.custom as string,
|
||||
})),
|
||||
validateCluster: jest.fn().mockReturnValue([]),
|
||||
})
|
||||
.setClusterSupplier({
|
||||
getClusters: jest
|
||||
.fn<Promise<ClusterDetails[]>, []>()
|
||||
.mockResolvedValue([
|
||||
{
|
||||
name: 'custom-cluster',
|
||||
url: 'http://my.cluster.url',
|
||||
authMetadata: {
|
||||
[ANNOTATION_KUBERNETES_AUTH_PROVIDER]: 'custom',
|
||||
},
|
||||
|
||||
const clusterSupplierMock = {
|
||||
getClusters: jest.fn().mockImplementation(_ => {
|
||||
return Promise.resolve([
|
||||
{
|
||||
name: 'custom-cluster',
|
||||
url: 'http://my.cluster.url',
|
||||
authMetadata: {
|
||||
[ANNOTATION_KUBERNETES_AUTH_PROVIDER]: 'custom',
|
||||
},
|
||||
]),
|
||||
})
|
||||
.setFetcher(mockFetcher)
|
||||
.build();
|
||||
app = express().use(router);
|
||||
},
|
||||
]);
|
||||
}),
|
||||
};
|
||||
|
||||
const { server } = await startTestBackend({
|
||||
features: [
|
||||
mockServices.rootConfig.factory({
|
||||
data: {
|
||||
kubernetes: {
|
||||
serviceLocatorMethod: {
|
||||
type: 'multiTenant',
|
||||
},
|
||||
clusterLocatorMethods: [
|
||||
{
|
||||
type: 'config',
|
||||
clusters: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
import('@backstage/plugin-kubernetes-backend/alpha'),
|
||||
createBackendModule({
|
||||
pluginId: 'kubernetes',
|
||||
moduleId: 'testClusterSupplier',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: { extension: kubernetesClusterSupplierExtensionPoint },
|
||||
async init({ extension }) {
|
||||
extension.addClusterSupplier(clusterSupplierMock);
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
createBackendModule({
|
||||
pluginId: 'kubernetes',
|
||||
moduleId: 'testAuthStrategy',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: { extension: kubernetesAuthStrategyExtensionPoint },
|
||||
async init({ extension }) {
|
||||
extension.addAuthStrategy('custom', {
|
||||
getCredential: jest
|
||||
.fn<
|
||||
Promise<KubernetesCredential>,
|
||||
[ClusterDetails, KubernetesRequestAuth]
|
||||
>()
|
||||
.mockImplementation(async (_, requestAuth) => ({
|
||||
type: 'bearer token',
|
||||
token: requestAuth.custom as string,
|
||||
})),
|
||||
validateCluster: jest.fn().mockReturnValue([]),
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
createBackendModule({
|
||||
pluginId: 'kubernetes',
|
||||
moduleId: 'testFetcher',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: { extension: kubernetesFetcherExtensionPoint },
|
||||
async init({ extension }) {
|
||||
extension.addFetcher(mockFetcher);
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
app = server;
|
||||
|
||||
await request(app)
|
||||
.post('/services/test-service')
|
||||
.post('/api/kubernetes/services/test-service')
|
||||
.send({
|
||||
entity: {
|
||||
metadata: {
|
||||
@@ -400,12 +542,8 @@ describe('KubernetesBuilder', () => {
|
||||
},
|
||||
};
|
||||
|
||||
permissions.authorize.mockReturnValue(
|
||||
Promise.resolve([{ result: AuthorizeResult.ALLOW }]),
|
||||
);
|
||||
|
||||
const proxyEndpointRequest = request(app)
|
||||
.post('/proxy/api/v1/namespaces')
|
||||
.post('/api/kubernetes/proxy/api/v1/namespaces')
|
||||
.set(HEADER_KUBERNETES_CLUSTER, 'some-cluster')
|
||||
.set(HEADER_KUBERNETES_AUTH, 'randomtoken')
|
||||
.send(requestBody);
|
||||
@@ -425,12 +563,8 @@ metadata:
|
||||
name: new-ns
|
||||
`;
|
||||
|
||||
permissions.authorize.mockReturnValue(
|
||||
Promise.resolve([{ result: AuthorizeResult.ALLOW }]),
|
||||
);
|
||||
|
||||
const proxyEndpointRequest = request(app)
|
||||
.post('/proxy/api/v1/namespaces')
|
||||
.post('/api/kubernetes/proxy/api/v1/namespaces')
|
||||
.set(HEADER_KUBERNETES_CLUSTER, 'some-cluster')
|
||||
.set(HEADER_KUBERNETES_AUTH, 'randomtoken')
|
||||
.set('content-type', 'application/yaml')
|
||||
@@ -451,12 +585,35 @@ metadata:
|
||||
},
|
||||
};
|
||||
|
||||
permissions.authorize.mockReturnValue(
|
||||
Promise.resolve([{ result: AuthorizeResult.DENY }]),
|
||||
);
|
||||
permissionsMock.authorize.mockResolvedValue([
|
||||
{ result: AuthorizeResult.DENY },
|
||||
]);
|
||||
|
||||
const proxyEndpointRequest = request(app)
|
||||
.post('/proxy/api/v1/namespaces')
|
||||
const { server } = await startTestBackend({
|
||||
features: [
|
||||
mockServices.rootConfig.factory({
|
||||
data: {
|
||||
kubernetes: {
|
||||
serviceLocatorMethod: {
|
||||
type: 'multiTenant',
|
||||
},
|
||||
clusterLocatorMethods: [
|
||||
{
|
||||
type: 'config',
|
||||
clusters: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
permissionsMock.factory,
|
||||
import('@backstage/plugin-kubernetes-backend/alpha'),
|
||||
// import('@backstage/plugin-permission-backend/alpha'),
|
||||
],
|
||||
});
|
||||
|
||||
const proxyEndpointRequest = request(server)
|
||||
.post('/api/kubernetes/proxy/api/v1/namespaces')
|
||||
.set(HEADER_KUBERNETES_CLUSTER, 'some-cluster')
|
||||
.set(HEADER_KUBERNETES_AUTH, 'randomtoken')
|
||||
.send(requestBody);
|
||||
@@ -477,42 +634,90 @@ metadata:
|
||||
return res(ctx.json({ items: [] }));
|
||||
}),
|
||||
);
|
||||
permissions.authorize.mockResolvedValue([
|
||||
{ result: AuthorizeResult.ALLOW },
|
||||
]);
|
||||
const { router } = await KubernetesBuilder.createBuilder({
|
||||
logger: getVoidLogger(),
|
||||
config,
|
||||
catalogApi,
|
||||
permissions,
|
||||
})
|
||||
.addAuthStrategy('custom', {
|
||||
getCredential: jest
|
||||
.fn<
|
||||
Promise<KubernetesCredential>,
|
||||
[ClusterDetails, KubernetesRequestAuth]
|
||||
>()
|
||||
.mockResolvedValue({ type: 'anonymous' }),
|
||||
validateCluster: jest.fn().mockReturnValue([]),
|
||||
})
|
||||
.setClusterSupplier({
|
||||
getClusters: jest
|
||||
.fn<Promise<ClusterDetails[]>, []>()
|
||||
.mockResolvedValue([
|
||||
{
|
||||
name: 'custom-cluster',
|
||||
url: 'http://my.cluster.url',
|
||||
authMetadata: {
|
||||
[ANNOTATION_KUBERNETES_AUTH_PROVIDER]: 'custom',
|
||||
},
|
||||
|
||||
const clusterSupplierMock = {
|
||||
getClusters: jest.fn().mockImplementation(_ => {
|
||||
return Promise.resolve([
|
||||
{
|
||||
name: 'custom-cluster',
|
||||
url: 'http://my.cluster.url',
|
||||
authMetadata: {
|
||||
[ANNOTATION_KUBERNETES_AUTH_PROVIDER]: 'custom',
|
||||
},
|
||||
]),
|
||||
})
|
||||
.build();
|
||||
app = express().use(router);
|
||||
},
|
||||
]);
|
||||
}),
|
||||
};
|
||||
|
||||
const { server } = await startTestBackend({
|
||||
features: [
|
||||
mockServices.rootConfig.factory({
|
||||
data: {
|
||||
kubernetes: {
|
||||
serviceLocatorMethod: {
|
||||
type: 'multiTenant',
|
||||
},
|
||||
clusterLocatorMethods: [
|
||||
{
|
||||
type: 'config',
|
||||
clusters: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
import('@backstage/plugin-kubernetes-backend/alpha'),
|
||||
createBackendModule({
|
||||
pluginId: 'kubernetes',
|
||||
moduleId: 'testObjectsProvider',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: { extension: kubernetesObjectsProviderExtensionPoint },
|
||||
async init({ extension }) {
|
||||
extension.addObjectsProvider(objectsProviderMock);
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
createBackendModule({
|
||||
pluginId: 'kubernetes',
|
||||
moduleId: 'testClusterSupplier',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: { extension: kubernetesClusterSupplierExtensionPoint },
|
||||
async init({ extension }) {
|
||||
extension.addClusterSupplier(clusterSupplierMock);
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
createBackendModule({
|
||||
pluginId: 'kubernetes',
|
||||
moduleId: 'testAuthStrategy',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: { extension: kubernetesAuthStrategyExtensionPoint },
|
||||
async init({ extension }) {
|
||||
extension.addAuthStrategy('custom', {
|
||||
getCredential: jest
|
||||
.fn<
|
||||
Promise<KubernetesCredential>,
|
||||
[ClusterDetails, KubernetesRequestAuth]
|
||||
>()
|
||||
.mockResolvedValue({ type: 'anonymous' }),
|
||||
validateCluster: jest.fn().mockReturnValue([]),
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
app = server;
|
||||
|
||||
const proxyEndpointRequest = request(app)
|
||||
.get('/proxy/api/v1/namespaces')
|
||||
.get('/api/kubernetes/proxy/api/v1/namespaces')
|
||||
.set(HEADER_KUBERNETES_CLUSTER, 'custom-cluster')
|
||||
.set(HEADER_KUBERNETES_AUTH, 'custom-token');
|
||||
worker.use(rest.all(proxyEndpointRequest.url, req => req.passthrough()));
|
||||
@@ -522,29 +727,44 @@ metadata:
|
||||
});
|
||||
|
||||
it('should not permit custom auth strategies with dashes', async () => {
|
||||
const throwError = () =>
|
||||
KubernetesBuilder.createBuilder({
|
||||
logger: getVoidLogger(),
|
||||
config,
|
||||
catalogApi,
|
||||
permissions,
|
||||
}).addAuthStrategy('custom-strategy', {
|
||||
getCredential: jest
|
||||
.fn<
|
||||
Promise<KubernetesCredential>,
|
||||
[ClusterDetails, KubernetesRequestAuth]
|
||||
>()
|
||||
.mockResolvedValue({ type: 'anonymous' }),
|
||||
validateCluster: jest.fn().mockReturnValue([]),
|
||||
const throwError = async () => {
|
||||
await startTestBackend({
|
||||
features: [
|
||||
import('@backstage/plugin-kubernetes-backend/alpha'),
|
||||
createBackendModule({
|
||||
pluginId: 'kubernetes',
|
||||
moduleId: 'testAuthStrategy',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: { extension: kubernetesAuthStrategyExtensionPoint },
|
||||
async init({ extension }) {
|
||||
extension.addAuthStrategy('custom-strategy', {
|
||||
getCredential: jest
|
||||
.fn<
|
||||
Promise<KubernetesCredential>,
|
||||
[ClusterDetails, KubernetesRequestAuth]
|
||||
>()
|
||||
.mockResolvedValue({ type: 'anonymous' }),
|
||||
validateCluster: jest.fn().mockReturnValue([]),
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
expect(throwError).toThrow('Strategy name can not include dashes');
|
||||
await expect(throwError).rejects.toThrow(
|
||||
'Strategy name can not include dashes',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('get /.well-known/backstage/permissions/metadata', () => {
|
||||
it('lists permissions supported by the kubernetes plugin', async () => {
|
||||
const response = await request(app).get(
|
||||
'/.well-known/backstage/permissions/metadata',
|
||||
'/api/kubernetes/.well-known/backstage/permissions/metadata',
|
||||
);
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
|
||||
@@ -46,7 +46,6 @@ import { MultiTenantServiceLocator } from '../service-locator/MultiTenantService
|
||||
import { SingleTenantServiceLocator } from '../service-locator/SingleTenantServiceLocator';
|
||||
import {
|
||||
CustomResource,
|
||||
KubernetesClustersSupplier,
|
||||
KubernetesFetcher,
|
||||
KubernetesObjectsProviderOptions,
|
||||
KubernetesObjectTypes,
|
||||
@@ -54,7 +53,10 @@ import {
|
||||
ObjectsByEntityRequest,
|
||||
ServiceLocatorMethod,
|
||||
} from '../types/types';
|
||||
import { KubernetesObjectsProvider } from '@backstage/plugin-kubernetes-node';
|
||||
import {
|
||||
KubernetesClustersSupplier,
|
||||
KubernetesObjectsProvider,
|
||||
} from '@backstage/plugin-kubernetes-node';
|
||||
import {
|
||||
DEFAULT_OBJECTS,
|
||||
KubernetesFanOutHandler,
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Logger } from 'winston';
|
||||
import {
|
||||
ClusterDetails,
|
||||
KubernetesFetcher,
|
||||
KubernetesObjectsProviderOptions,
|
||||
KubernetesServiceLocator,
|
||||
@@ -26,7 +25,6 @@ import {
|
||||
ObjectToFetch,
|
||||
CustomResource,
|
||||
} from '../types/types';
|
||||
import { AuthenticationStrategy, KubernetesCredential } from '../auth/types';
|
||||
import {
|
||||
ClientContainerStatus,
|
||||
ClientCurrentResourceUsage,
|
||||
@@ -45,7 +43,10 @@ import {
|
||||
PodStatus,
|
||||
} from '@kubernetes/client-node';
|
||||
import {
|
||||
AuthenticationStrategy,
|
||||
ClusterDetails,
|
||||
CustomResourcesByEntity,
|
||||
KubernetesCredential,
|
||||
KubernetesObjectsByEntity,
|
||||
} from '@backstage/plugin-kubernetes-node';
|
||||
|
||||
|
||||
@@ -26,12 +26,10 @@ import {
|
||||
import lodash, { Dictionary } from 'lodash';
|
||||
import { Logger } from 'winston';
|
||||
import {
|
||||
ClusterDetails,
|
||||
FetchResponseWrapper,
|
||||
KubernetesFetcher,
|
||||
ObjectFetchParams,
|
||||
} from '../types/types';
|
||||
import { KubernetesCredential } from '../auth/types';
|
||||
import {
|
||||
ANNOTATION_KUBERNETES_AUTH_PROVIDER,
|
||||
FetchResponse,
|
||||
@@ -43,6 +41,10 @@ import fetch, { RequestInit, Response } from 'node-fetch';
|
||||
import * as https from 'https';
|
||||
import fs from 'fs-extra';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import {
|
||||
ClusterDetails,
|
||||
KubernetesCredential,
|
||||
} from '@backstage/plugin-kubernetes-node';
|
||||
|
||||
export interface KubernetesClientBasedFetcherOptions {
|
||||
logger: Logger;
|
||||
|
||||
@@ -15,9 +15,3 @@
|
||||
*/
|
||||
|
||||
export * from './types';
|
||||
|
||||
export type {
|
||||
CustomResourcesByEntity,
|
||||
KubernetesObjectsByEntity,
|
||||
KubernetesObjectsProvider,
|
||||
} from '@backstage/plugin-kubernetes-node';
|
||||
|
||||
@@ -14,131 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Logger } from 'winston';
|
||||
import type { JsonObject } from '@backstage/types';
|
||||
import type {
|
||||
CustomResourceMatcher,
|
||||
FetchResponse,
|
||||
KubernetesFetchError,
|
||||
KubernetesRequestBody,
|
||||
} from '@backstage/plugin-kubernetes-common';
|
||||
import type { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common';
|
||||
import { Config } from '@backstage/config';
|
||||
import { KubernetesCredential } from '../auth/types';
|
||||
|
||||
/**
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface ObjectFetchParams {
|
||||
serviceId: string;
|
||||
clusterDetails: ClusterDetails;
|
||||
credential: KubernetesCredential;
|
||||
objectTypesToFetch: Set<ObjectToFetch>;
|
||||
labelSelector?: string;
|
||||
customResources: CustomResource[];
|
||||
namespace?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches information from a kubernetes cluster using the cluster details object to target a specific cluster
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface KubernetesFetcher {
|
||||
fetchObjectsForService(
|
||||
params: ObjectFetchParams,
|
||||
): Promise<FetchResponseWrapper>;
|
||||
fetchPodMetricsByNamespaces(
|
||||
clusterDetails: ClusterDetails,
|
||||
credential: KubernetesCredential,
|
||||
namespaces: Set<string>,
|
||||
labelSelector?: string,
|
||||
): Promise<FetchResponseWrapper>;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface FetchResponseWrapper {
|
||||
errors: KubernetesFetchError[];
|
||||
responses: FetchResponse[];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface ObjectToFetch {
|
||||
objectType: KubernetesObjectTypes;
|
||||
group: string;
|
||||
apiVersion: string;
|
||||
plural: string;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface CustomResource extends ObjectToFetch {
|
||||
objectType: 'customresources';
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type KubernetesObjectTypes =
|
||||
| 'pods'
|
||||
| 'services'
|
||||
| 'configmaps'
|
||||
| 'deployments'
|
||||
| 'limitranges'
|
||||
| 'resourcequotas'
|
||||
| 'replicasets'
|
||||
| 'horizontalpodautoscalers'
|
||||
| 'jobs'
|
||||
| 'cronjobs'
|
||||
| 'ingresses'
|
||||
| 'customresources'
|
||||
| 'statefulsets'
|
||||
| 'daemonsets';
|
||||
// If updating this list, also make sure to update
|
||||
// `objectTypes` and `apiVersionOverrides` in config.d.ts!
|
||||
|
||||
/**
|
||||
* Used to load cluster details from different sources
|
||||
* @public
|
||||
*/
|
||||
export interface KubernetesClustersSupplier {
|
||||
/**
|
||||
* Returns the cached list of clusters.
|
||||
*
|
||||
* Implementations _should_ cache the clusters and refresh them periodically,
|
||||
* as getClusters is called whenever the list of clusters is needed.
|
||||
*/
|
||||
getClusters(): Promise<ClusterDetails[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ServiceLocatorRequestContext {
|
||||
objectTypesToFetch: Set<ObjectToFetch>;
|
||||
customResources: CustomResourceMatcher[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to locate which cluster(s) a service is running on
|
||||
* @public
|
||||
*/
|
||||
export interface KubernetesServiceLocator {
|
||||
getClustersByEntity(
|
||||
entity: Entity,
|
||||
requestContext: ServiceLocatorRequestContext,
|
||||
): Promise<{ clusters: ClusterDetails[] }>;
|
||||
}
|
||||
import * as k8sTypes from '@backstage/plugin-kubernetes-node';
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -146,70 +25,6 @@ export interface KubernetesServiceLocator {
|
||||
*/
|
||||
export type ServiceLocatorMethod = 'multiTenant' | 'singleTenant' | 'http'; // TODO implement http
|
||||
|
||||
/**
|
||||
* Provider-specific authentication configuration
|
||||
* @public
|
||||
*/
|
||||
export type AuthMetadata = Record<string, string>;
|
||||
|
||||
/**
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface ClusterDetails {
|
||||
/**
|
||||
* Specifies the name of the Kubernetes cluster.
|
||||
*/
|
||||
name: string;
|
||||
url: string;
|
||||
authMetadata: AuthMetadata;
|
||||
skipTLSVerify?: boolean;
|
||||
/**
|
||||
* Whether to skip the lookup to the metrics server to retrieve pod resource usage.
|
||||
* It is not guaranteed that the Kubernetes distro has the metrics server installed.
|
||||
*/
|
||||
skipMetricsLookup?: boolean;
|
||||
caData?: string | undefined;
|
||||
caFile?: string | undefined;
|
||||
/**
|
||||
* Specifies the link to the Kubernetes dashboard managing this cluster.
|
||||
* @remarks
|
||||
* Note that you should specify the app used for the dashboard
|
||||
* using the dashboardApp property, in order to properly format
|
||||
* links to kubernetes resources, otherwise it will assume that you're running the standard one.
|
||||
* @see dashboardApp
|
||||
* @see dashboardParameters
|
||||
*/
|
||||
dashboardUrl?: string;
|
||||
/**
|
||||
* Specifies the app that provides the Kubernetes dashboard.
|
||||
* This will be used for formatting links to kubernetes objects inside the dashboard.
|
||||
* @remarks
|
||||
* The existing apps are: standard, rancher, openshift, gke, aks, eks
|
||||
* Note that it will default to the regular dashboard provided by the Kubernetes project (standard).
|
||||
* Note that you can add your own formatter by registering it to the clusterLinksFormatters dictionary.
|
||||
* @defaultValue standard
|
||||
* @see dashboardUrl
|
||||
* @example
|
||||
* ```ts
|
||||
* import { clusterLinksFormatters } from '@backstage/plugin-kubernetes';
|
||||
* clusterLinksFormatters.myDashboard = (options) => ...;
|
||||
* ```
|
||||
*/
|
||||
dashboardApp?: string;
|
||||
/**
|
||||
* Specifies specific parameters used by some dashboard URL formatters.
|
||||
* This is used by the GKE formatter which requires the project, region and cluster name.
|
||||
* @see dashboardApp
|
||||
*/
|
||||
dashboardParameters?: JsonObject;
|
||||
/**
|
||||
* Specifies which custom resources to look for when returning an entity's
|
||||
* Kubernetes resources.
|
||||
*/
|
||||
customResources?: CustomResourceMatcher[];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @public
|
||||
@@ -217,10 +32,10 @@ export interface ClusterDetails {
|
||||
export interface KubernetesObjectsProviderOptions {
|
||||
logger: Logger;
|
||||
config: Config;
|
||||
fetcher: KubernetesFetcher;
|
||||
serviceLocator: KubernetesServiceLocator;
|
||||
customResources: CustomResource[];
|
||||
objectTypesToFetch?: ObjectToFetch[];
|
||||
fetcher: k8sTypes.KubernetesFetcher;
|
||||
serviceLocator: k8sTypes.KubernetesServiceLocator;
|
||||
customResources: k8sTypes.CustomResource[];
|
||||
objectTypesToFetch?: k8sTypes.ObjectToFetch[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,3 +43,73 @@ export interface KubernetesObjectsProviderOptions {
|
||||
* @public
|
||||
*/
|
||||
export type ObjectsByEntityRequest = KubernetesRequestBody;
|
||||
|
||||
// TODO remove this re-export as a breaking change after a couple of releases
|
||||
/**
|
||||
* @public @deprecated Import it from \@backstage/plugin-kubernetes-node instead
|
||||
*/
|
||||
export type KubernetesObjectsProvider = k8sTypes.KubernetesObjectsProvider;
|
||||
|
||||
/**
|
||||
* @public @deprecated Import it from \@backstage/plugin-kubernetes-node instead
|
||||
*/
|
||||
export type CustomResourcesByEntity = k8sTypes.CustomResourcesByEntity;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Import it from \@backstage/plugin-kubernetes-node instead
|
||||
*/
|
||||
export type AuthMetadata = k8sTypes.AuthMetadata;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Import it from \@backstage/plugin-kubernetes-node instead
|
||||
*/
|
||||
export type ClusterDetails = k8sTypes.ClusterDetails;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Import it from \@backstage/plugin-kubernetes-node instead
|
||||
*/
|
||||
export type KubernetesClustersSupplier = k8sTypes.KubernetesClustersSupplier;
|
||||
|
||||
/**
|
||||
* @public @deprecated Import it from \@backstage/plugin-kubernetes-node instead
|
||||
*/
|
||||
export type KubernetesObjectTypes = k8sTypes.KubernetesObjectTypes;
|
||||
|
||||
/**
|
||||
* @public @deprecated Import it from \@backstage/plugin-kubernetes-node instead
|
||||
*/
|
||||
export type ObjectToFetch = k8sTypes.ObjectToFetch;
|
||||
|
||||
/**
|
||||
* @public @deprecated Import it from \@backstage/plugin-kubernetes-node instead
|
||||
*/
|
||||
export type CustomResource = k8sTypes.CustomResource;
|
||||
|
||||
/**
|
||||
* @public @deprecated Import it from \@backstage/plugin-kubernetes-node instead
|
||||
*/
|
||||
export type ObjectFetchParams = k8sTypes.ObjectFetchParams;
|
||||
|
||||
/**
|
||||
* @public @deprecated Import it from \@backstage/plugin-kubernetes-node instead
|
||||
*/
|
||||
export type FetchResponseWrapper = k8sTypes.FetchResponseWrapper;
|
||||
|
||||
/**
|
||||
* @public @deprecated Import it from \@backstage/plugin-kubernetes-node instead
|
||||
*/
|
||||
export type KubernetesFetcher = k8sTypes.KubernetesFetcher;
|
||||
|
||||
/**
|
||||
* @public @deprecated Import it from \@backstage/plugin-kubernetes-node instead
|
||||
*/
|
||||
export type ServiceLocatorRequestContext =
|
||||
k8sTypes.ServiceLocatorRequestContext;
|
||||
|
||||
/**
|
||||
* @public @deprecated Import it from \@backstage/plugin-kubernetes-node instead
|
||||
*/
|
||||
export type KubernetesServiceLocator = k8sTypes.KubernetesServiceLocator;
|
||||
|
||||
@@ -3,19 +3,131 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { AuthenticationStrategy as AuthenticationStrategy_2 } from '@backstage/plugin-kubernetes-node';
|
||||
import { CustomResourceMatcher } from '@backstage/plugin-kubernetes-common';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { ExtensionPoint } from '@backstage/backend-plugin-api';
|
||||
import { FetchResponse } from '@backstage/plugin-kubernetes-common';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { KubernetesClustersSupplier as KubernetesClustersSupplier_2 } from '@backstage/plugin-kubernetes-node';
|
||||
import { KubernetesFetcher as KubernetesFetcher_2 } from '@backstage/plugin-kubernetes-node';
|
||||
import { KubernetesFetchError } from '@backstage/plugin-kubernetes-common';
|
||||
import { KubernetesObjectsProvider as KubernetesObjectsProvider_2 } from '@backstage/plugin-kubernetes-node';
|
||||
import { KubernetesRequestAuth } from '@backstage/plugin-kubernetes-common';
|
||||
import { KubernetesServiceLocator as KubernetesServiceLocator_2 } from '@backstage/plugin-kubernetes-node';
|
||||
import { ObjectsByEntityResponse } from '@backstage/plugin-kubernetes-common';
|
||||
|
||||
// @public (undocumented)
|
||||
export interface AuthenticationStrategy {
|
||||
// (undocumented)
|
||||
getCredential(
|
||||
clusterDetails: ClusterDetails,
|
||||
authConfig: KubernetesRequestAuth,
|
||||
): Promise<KubernetesCredential>;
|
||||
// (undocumented)
|
||||
validateCluster(authMetadata: AuthMetadata): Error[];
|
||||
}
|
||||
|
||||
// @public
|
||||
export type AuthMetadata = Record<string, string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ClusterDetails {
|
||||
// (undocumented)
|
||||
authMetadata: AuthMetadata;
|
||||
// (undocumented)
|
||||
caData?: string | undefined;
|
||||
// (undocumented)
|
||||
caFile?: string | undefined;
|
||||
customResources?: CustomResourceMatcher[];
|
||||
dashboardApp?: string;
|
||||
dashboardParameters?: JsonObject;
|
||||
dashboardUrl?: string;
|
||||
name: string;
|
||||
skipMetricsLookup?: boolean;
|
||||
// (undocumented)
|
||||
skipTLSVerify?: boolean;
|
||||
// (undocumented)
|
||||
url: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface CustomResource extends ObjectToFetch {
|
||||
// (undocumented)
|
||||
objectType: 'customresources';
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface CustomResourcesByEntity extends KubernetesObjectsByEntity {
|
||||
// (undocumented)
|
||||
customResources: CustomResourceMatcher[];
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface FetchResponseWrapper {
|
||||
// (undocumented)
|
||||
errors: KubernetesFetchError[];
|
||||
// (undocumented)
|
||||
responses: FetchResponse[];
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface KubernetesAuthStrategyExtensionPoint {
|
||||
// (undocumented)
|
||||
addAuthStrategy(key: string, strategy: AuthenticationStrategy_2): void;
|
||||
}
|
||||
|
||||
// @public
|
||||
export const kubernetesAuthStrategyExtensionPoint: ExtensionPoint<KubernetesAuthStrategyExtensionPoint>;
|
||||
|
||||
// @public
|
||||
export interface KubernetesClustersSupplier {
|
||||
getClusters(): Promise<ClusterDetails[]>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface KubernetesClusterSupplierExtensionPoint {
|
||||
// (undocumented)
|
||||
addClusterSupplier(clusterSupplier: KubernetesClustersSupplier_2): void;
|
||||
}
|
||||
|
||||
// @public
|
||||
export const kubernetesClusterSupplierExtensionPoint: ExtensionPoint<KubernetesClusterSupplierExtensionPoint>;
|
||||
|
||||
// @public
|
||||
export type KubernetesCredential =
|
||||
| {
|
||||
type: 'bearer token';
|
||||
token: string;
|
||||
}
|
||||
| {
|
||||
type: 'anonymous';
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface KubernetesFetcher {
|
||||
// (undocumented)
|
||||
fetchObjectsForService(
|
||||
params: ObjectFetchParams,
|
||||
): Promise<FetchResponseWrapper>;
|
||||
// (undocumented)
|
||||
fetchPodMetricsByNamespaces(
|
||||
clusterDetails: ClusterDetails,
|
||||
credential: KubernetesCredential,
|
||||
namespaces: Set<string>,
|
||||
labelSelector?: string,
|
||||
): Promise<FetchResponseWrapper>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface KubernetesFetcherExtensionPoint {
|
||||
// (undocumented)
|
||||
addFetcher(fetcher: KubernetesFetcher_2): void;
|
||||
}
|
||||
|
||||
// @public
|
||||
export const kubernetesFetcherExtensionPoint: ExtensionPoint<KubernetesFetcherExtensionPoint>;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface KubernetesObjectsByEntity {
|
||||
// (undocumented)
|
||||
@@ -44,4 +156,79 @@ export interface KubernetesObjectsProviderExtensionPoint {
|
||||
|
||||
// @public
|
||||
export const kubernetesObjectsProviderExtensionPoint: ExtensionPoint<KubernetesObjectsProviderExtensionPoint>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type KubernetesObjectTypes =
|
||||
| 'pods'
|
||||
| 'services'
|
||||
| 'configmaps'
|
||||
| 'deployments'
|
||||
| 'limitranges'
|
||||
| 'resourcequotas'
|
||||
| 'replicasets'
|
||||
| 'horizontalpodautoscalers'
|
||||
| 'jobs'
|
||||
| 'cronjobs'
|
||||
| 'ingresses'
|
||||
| 'customresources'
|
||||
| 'statefulsets'
|
||||
| 'daemonsets';
|
||||
|
||||
// @public
|
||||
export interface KubernetesServiceLocator {
|
||||
// (undocumented)
|
||||
getClustersByEntity(
|
||||
entity: Entity,
|
||||
requestContext: ServiceLocatorRequestContext,
|
||||
): Promise<{
|
||||
clusters: ClusterDetails[];
|
||||
}>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface KubernetesServiceLocatorExtensionPoint {
|
||||
// (undocumented)
|
||||
addServiceLocator(serviceLocator: KubernetesServiceLocator_2): void;
|
||||
}
|
||||
|
||||
// @public
|
||||
export const kubernetesServiceLocatorExtensionPoint: ExtensionPoint<KubernetesServiceLocatorExtensionPoint>;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ObjectFetchParams {
|
||||
// (undocumented)
|
||||
clusterDetails: ClusterDetails;
|
||||
// (undocumented)
|
||||
credential: KubernetesCredential;
|
||||
// (undocumented)
|
||||
customResources: CustomResource[];
|
||||
// (undocumented)
|
||||
labelSelector?: string;
|
||||
// (undocumented)
|
||||
namespace?: string;
|
||||
// (undocumented)
|
||||
objectTypesToFetch: Set<ObjectToFetch>;
|
||||
// (undocumented)
|
||||
serviceId: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ObjectToFetch {
|
||||
// (undocumented)
|
||||
apiVersion: string;
|
||||
// (undocumented)
|
||||
group: string;
|
||||
// (undocumented)
|
||||
objectType: KubernetesObjectTypes;
|
||||
// (undocumented)
|
||||
plural: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ServiceLocatorRequestContext {
|
||||
// (undocumented)
|
||||
customResources: CustomResourceMatcher[];
|
||||
// (undocumented)
|
||||
objectTypesToFetch: Set<ObjectToFetch>;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"dependencies": {
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
"@backstage/catalog-model": "workspace:^",
|
||||
"@backstage/plugin-kubernetes-common": "workspace:^"
|
||||
"@backstage/plugin-kubernetes-common": "workspace:^",
|
||||
"@backstage/types": "workspace:^"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createExtensionPoint } from '@backstage/backend-plugin-api';
|
||||
import { KubernetesObjectsProvider } from '@backstage/plugin-kubernetes-node';
|
||||
import {
|
||||
AuthenticationStrategy,
|
||||
KubernetesClustersSupplier,
|
||||
KubernetesFetcher,
|
||||
KubernetesObjectsProvider,
|
||||
KubernetesServiceLocator,
|
||||
} from '@backstage/plugin-kubernetes-node';
|
||||
|
||||
/**
|
||||
* The interface for {@link kubernetesObjectsProviderExtensionPoint}.
|
||||
@@ -34,3 +40,79 @@ export const kubernetesObjectsProviderExtensionPoint =
|
||||
createExtensionPoint<KubernetesObjectsProviderExtensionPoint>({
|
||||
id: 'kubernetes.objects-provider',
|
||||
});
|
||||
|
||||
/**
|
||||
* The interface for {@link kubernetesClusterSupplierExtensionPoint}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface KubernetesClusterSupplierExtensionPoint {
|
||||
addClusterSupplier(clusterSupplier: KubernetesClustersSupplier): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* An extension point the exposes the ability to configure a cluster supplier.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const kubernetesClusterSupplierExtensionPoint =
|
||||
createExtensionPoint<KubernetesClusterSupplierExtensionPoint>({
|
||||
id: 'kubernetes.cluster-supplier',
|
||||
});
|
||||
|
||||
/**
|
||||
* The interface for {@link kubernetesAuthStrategyExtensionPoint}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface KubernetesAuthStrategyExtensionPoint {
|
||||
addAuthStrategy(key: string, strategy: AuthenticationStrategy): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* An extension point the exposes the ability to add an Auth Strategy.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const kubernetesAuthStrategyExtensionPoint =
|
||||
createExtensionPoint<KubernetesAuthStrategyExtensionPoint>({
|
||||
id: 'kubernetes.auth-strategy',
|
||||
});
|
||||
|
||||
/**
|
||||
* The interface for {@link kubernetesFetcherExtensionPoint}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface KubernetesFetcherExtensionPoint {
|
||||
addFetcher(fetcher: KubernetesFetcher): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* An extension point the exposes the ability to configure a kubernetes fetcher.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const kubernetesFetcherExtensionPoint =
|
||||
createExtensionPoint<KubernetesFetcherExtensionPoint>({
|
||||
id: 'kubernetes.fetcher',
|
||||
});
|
||||
|
||||
/**
|
||||
* The interface for {@link kubernetesServiceLocatorExtensionPoint}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface KubernetesServiceLocatorExtensionPoint {
|
||||
addServiceLocator(serviceLocator: KubernetesServiceLocator): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* An extension point the exposes the ability to configure a kubernetes service locator.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const kubernetesServiceLocatorExtensionPoint =
|
||||
createExtensionPoint<KubernetesServiceLocatorExtensionPoint>({
|
||||
id: 'kubernetes.service-locator',
|
||||
});
|
||||
|
||||
@@ -29,9 +29,5 @@
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export {
|
||||
kubernetesObjectsProviderExtensionPoint,
|
||||
type KubernetesObjectsProviderExtensionPoint,
|
||||
} from './extensions';
|
||||
|
||||
export * from './extensions';
|
||||
export * from './types';
|
||||
|
||||
@@ -16,9 +16,12 @@
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import {
|
||||
CustomResourceMatcher,
|
||||
FetchResponse,
|
||||
KubernetesFetchError,
|
||||
KubernetesRequestAuth,
|
||||
ObjectsByEntityResponse,
|
||||
} from '@backstage/plugin-kubernetes-common';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -51,3 +54,200 @@ export interface KubernetesObjectsByEntity {
|
||||
export interface CustomResourcesByEntity extends KubernetesObjectsByEntity {
|
||||
customResources: CustomResourceMatcher[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider-specific authentication configuration
|
||||
* @public
|
||||
*/
|
||||
export type AuthMetadata = Record<string, string>;
|
||||
|
||||
/**
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface ClusterDetails {
|
||||
/**
|
||||
* Specifies the name of the Kubernetes cluster.
|
||||
*/
|
||||
name: string;
|
||||
url: string;
|
||||
authMetadata: AuthMetadata;
|
||||
skipTLSVerify?: boolean;
|
||||
/**
|
||||
* Whether to skip the lookup to the metrics server to retrieve pod resource usage.
|
||||
* It is not guaranteed that the Kubernetes distro has the metrics server installed.
|
||||
*/
|
||||
skipMetricsLookup?: boolean;
|
||||
caData?: string | undefined;
|
||||
caFile?: string | undefined;
|
||||
/**
|
||||
* Specifies the link to the Kubernetes dashboard managing this cluster.
|
||||
* @remarks
|
||||
* Note that you should specify the app used for the dashboard
|
||||
* using the dashboardApp property, in order to properly format
|
||||
* links to kubernetes resources, otherwise it will assume that you're running the standard one.
|
||||
* @see dashboardApp
|
||||
* @see dashboardParameters
|
||||
*/
|
||||
dashboardUrl?: string;
|
||||
/**
|
||||
* Specifies the app that provides the Kubernetes dashboard.
|
||||
* This will be used for formatting links to kubernetes objects inside the dashboard.
|
||||
* @remarks
|
||||
* The existing apps are: standard, rancher, openshift, gke, aks, eks
|
||||
* Note that it will default to the regular dashboard provided by the Kubernetes project (standard).
|
||||
* Note that you can add your own formatter by registering it to the clusterLinksFormatters dictionary.
|
||||
* @defaultValue standard
|
||||
* @see dashboardUrl
|
||||
* @example
|
||||
* ```ts
|
||||
* import { clusterLinksFormatters } from '@backstage/plugin-kubernetes';
|
||||
* clusterLinksFormatters.myDashboard = (options) => ...;
|
||||
* ```
|
||||
*/
|
||||
dashboardApp?: string;
|
||||
/**
|
||||
* Specifies specific parameters used by some dashboard URL formatters.
|
||||
* This is used by the GKE formatter which requires the project, region and cluster name.
|
||||
* @see dashboardApp
|
||||
*/
|
||||
dashboardParameters?: JsonObject;
|
||||
/**
|
||||
* Specifies which custom resources to look for when returning an entity's
|
||||
* Kubernetes resources.
|
||||
*/
|
||||
customResources?: CustomResourceMatcher[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to load cluster details from different sources
|
||||
* @public
|
||||
*/
|
||||
export interface KubernetesClustersSupplier {
|
||||
/**
|
||||
* Returns the cached list of clusters.
|
||||
*
|
||||
* Implementations _should_ cache the clusters and refresh them periodically,
|
||||
* as getClusters is called whenever the list of clusters is needed.
|
||||
*/
|
||||
getClusters(): Promise<ClusterDetails[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authentication data used to make a request to Kubernetes
|
||||
* @public
|
||||
*/
|
||||
export type KubernetesCredential =
|
||||
| { type: 'bearer token'; token: string }
|
||||
| { type: 'anonymous' };
|
||||
|
||||
/**
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface AuthenticationStrategy {
|
||||
getCredential(
|
||||
clusterDetails: ClusterDetails,
|
||||
authConfig: KubernetesRequestAuth,
|
||||
): Promise<KubernetesCredential>;
|
||||
validateCluster(authMetadata: AuthMetadata): Error[];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type KubernetesObjectTypes =
|
||||
| 'pods'
|
||||
| 'services'
|
||||
| 'configmaps'
|
||||
| 'deployments'
|
||||
| 'limitranges'
|
||||
| 'resourcequotas'
|
||||
| 'replicasets'
|
||||
| 'horizontalpodautoscalers'
|
||||
| 'jobs'
|
||||
| 'cronjobs'
|
||||
| 'ingresses'
|
||||
| 'customresources'
|
||||
| 'statefulsets'
|
||||
| 'daemonsets';
|
||||
// If updating this list, also make sure to update
|
||||
// `objectTypes` and `apiVersionOverrides` in config.d.ts on @backstage/plugin-kubernetes-backend!
|
||||
|
||||
/**
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface ObjectToFetch {
|
||||
objectType: KubernetesObjectTypes; // TODO - Review
|
||||
group: string;
|
||||
apiVersion: string;
|
||||
plural: string;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface CustomResource extends ObjectToFetch {
|
||||
objectType: 'customresources';
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface ObjectFetchParams {
|
||||
serviceId: string;
|
||||
clusterDetails: ClusterDetails;
|
||||
credential: KubernetesCredential;
|
||||
objectTypesToFetch: Set<ObjectToFetch>;
|
||||
labelSelector?: string;
|
||||
customResources: CustomResource[];
|
||||
namespace?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface FetchResponseWrapper {
|
||||
errors: KubernetesFetchError[];
|
||||
responses: FetchResponse[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches information from a kubernetes cluster using the cluster details object to target a specific cluster
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface KubernetesFetcher {
|
||||
fetchObjectsForService(
|
||||
params: ObjectFetchParams,
|
||||
): Promise<FetchResponseWrapper>;
|
||||
fetchPodMetricsByNamespaces(
|
||||
clusterDetails: ClusterDetails,
|
||||
credential: KubernetesCredential,
|
||||
namespaces: Set<string>,
|
||||
labelSelector?: string,
|
||||
): Promise<FetchResponseWrapper>;
|
||||
}
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ServiceLocatorRequestContext {
|
||||
objectTypesToFetch: Set<ObjectToFetch>;
|
||||
customResources: CustomResourceMatcher[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to locate which cluster(s) a service is running on
|
||||
* @public
|
||||
*/
|
||||
export interface KubernetesServiceLocator {
|
||||
getClustersByEntity(
|
||||
entity: Entity,
|
||||
requestContext: ServiceLocatorRequestContext,
|
||||
): Promise<{ clusters: ClusterDetails[] }>;
|
||||
}
|
||||
|
||||
@@ -7576,6 +7576,8 @@ __metadata:
|
||||
"@backstage/plugin-catalog-node": "workspace:^"
|
||||
"@backstage/plugin-kubernetes-common": "workspace:^"
|
||||
"@backstage/plugin-kubernetes-node": "workspace:^"
|
||||
"@backstage/plugin-permission-backend": "workspace:^"
|
||||
"@backstage/plugin-permission-backend-module-allow-all-policy": "workspace:^"
|
||||
"@backstage/plugin-permission-common": "workspace:^"
|
||||
"@backstage/plugin-permission-node": "workspace:^"
|
||||
"@backstage/types": "workspace:^"
|
||||
@@ -7675,6 +7677,7 @@ __metadata:
|
||||
"@backstage/catalog-model": "workspace:^"
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/plugin-kubernetes-common": "workspace:^"
|
||||
"@backstage/types": "workspace:^"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
|
||||
Reference in New Issue
Block a user