@backstage/plugin-kubernetes-backend 0.20.0-next.1 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/config.d.ts +7 -0
- package/dist/auth/AksStrategy.cjs.js.map +1 -1
- package/dist/auth/AnonymousStrategy.cjs.js.map +1 -1
- package/dist/auth/AwsIamStrategy.cjs.js.map +1 -1
- package/dist/auth/AzureIdentityStrategy.cjs.js.map +1 -1
- package/dist/auth/DispatchStrategy.cjs.js.map +1 -1
- package/dist/auth/GoogleServiceAccountStrategy.cjs.js +22 -2
- package/dist/auth/GoogleServiceAccountStrategy.cjs.js.map +1 -1
- package/dist/auth/GoogleStrategy.cjs.js.map +1 -1
- package/dist/auth/OidcStrategy.cjs.js.map +1 -1
- package/dist/auth/ServiceAccountStrategy.cjs.js.map +1 -1
- package/dist/auth/buildDefaultAuthStrategyMap.cjs.js +27 -0
- package/dist/auth/buildDefaultAuthStrategyMap.cjs.js.map +1 -0
- package/dist/auth/requirePermission.cjs.js.map +1 -1
- package/dist/cluster-locator/CatalogClusterLocator.cjs.js +7 -10
- package/dist/cluster-locator/CatalogClusterLocator.cjs.js.map +1 -1
- package/dist/cluster-locator/ConfigClusterLocator.cjs.js.map +1 -1
- package/dist/cluster-locator/GkeClusterLocator.cjs.js.map +1 -1
- package/dist/cluster-locator/LocalKubectlProxyLocator.cjs.js.map +1 -1
- package/dist/cluster-locator/index.cjs.js +2 -2
- package/dist/cluster-locator/index.cjs.js.map +1 -1
- package/dist/index.d.ts +26 -1
- package/dist/package.json.cjs.js +2 -127
- package/dist/package.json.cjs.js.map +1 -1
- package/dist/plugin.cjs.js +60 -25
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/routes/resourcesRoutes.cjs.js +3 -5
- package/dist/routes/resourcesRoutes.cjs.js.map +1 -1
- package/dist/service/KubernetesFanOutHandler.cjs.js.map +1 -1
- package/dist/service/KubernetesFetcher.cjs.js.map +1 -1
- package/dist/service/KubernetesInitializer.cjs.js +143 -0
- package/dist/service/KubernetesInitializer.cjs.js.map +1 -0
- package/dist/service/KubernetesProxy.cjs.js.map +1 -1
- package/dist/service/KubernetesRouter.cjs.js +167 -0
- package/dist/service/KubernetesRouter.cjs.js.map +1 -0
- package/dist/service/runPeriodically.cjs.js.map +1 -1
- package/dist/service-locator/CatalogRelationServiceLocator.cjs.js.map +1 -1
- package/dist/service-locator/MultiTenantServiceLocator.cjs.js.map +1 -1
- package/dist/service-locator/SingleTenantServiceLocator.cjs.js.map +1 -1
- package/dist/service-locator/buildDefaultServiceLocator.cjs.js +31 -0
- package/dist/service-locator/buildDefaultServiceLocator.cjs.js.map +1 -0
- package/package.json +20 -20
- package/dist/service/KubernetesBuilder.cjs.js +0 -374
- package/dist/service/KubernetesBuilder.cjs.js.map +0 -1
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var KubernetesFetcher = require('./KubernetesFetcher.cjs.js');
|
|
4
|
+
var buildDefaultAuthStrategyMap = require('../auth/buildDefaultAuthStrategyMap.cjs.js');
|
|
5
|
+
var luxon = require('luxon');
|
|
6
|
+
var index = require('../cluster-locator/index.cjs.js');
|
|
7
|
+
var DispatchStrategy = require('../auth/DispatchStrategy.cjs.js');
|
|
8
|
+
var buildDefaultServiceLocator = require('../service-locator/buildDefaultServiceLocator.cjs.js');
|
|
9
|
+
var KubernetesFanOutHandler = require('./KubernetesFanOutHandler.cjs.js');
|
|
10
|
+
|
|
11
|
+
class KubernetesInitializer {
|
|
12
|
+
constructor(opts) {
|
|
13
|
+
this.opts = opts;
|
|
14
|
+
}
|
|
15
|
+
static create(opts) {
|
|
16
|
+
return new KubernetesInitializer(opts);
|
|
17
|
+
}
|
|
18
|
+
async defaultFetcher() {
|
|
19
|
+
return new KubernetesFetcher.KubernetesClientBasedFetcher({
|
|
20
|
+
logger: this.opts.logger
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async defaultAuthStrategy() {
|
|
24
|
+
return buildDefaultAuthStrategyMap.buildDefaultAuthStrategyMap({
|
|
25
|
+
logger: this.opts.logger,
|
|
26
|
+
config: this.opts.config
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
async defaultClusterSupplier(opts) {
|
|
30
|
+
const refreshInterval = luxon.Duration.fromObject({
|
|
31
|
+
minutes: 60
|
|
32
|
+
});
|
|
33
|
+
return index.getCombinedClusterSupplier(
|
|
34
|
+
this.opts.config,
|
|
35
|
+
this.opts.catalog,
|
|
36
|
+
new DispatchStrategy.DispatchStrategy({
|
|
37
|
+
authStrategyMap: Object.fromEntries(opts.authStrategyMap.entries())
|
|
38
|
+
}),
|
|
39
|
+
this.opts.logger,
|
|
40
|
+
refreshInterval,
|
|
41
|
+
this.opts.auth
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
async defaultServiceLocator(opts) {
|
|
45
|
+
return buildDefaultServiceLocator.buildDefaultServiceLocator({
|
|
46
|
+
config: this.opts.config,
|
|
47
|
+
clusterSupplier: opts.clusterSupplier
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async defaultObjectsProvider(opts) {
|
|
51
|
+
return new KubernetesFanOutHandler.KubernetesFanOutHandler({
|
|
52
|
+
logger: this.opts.logger,
|
|
53
|
+
config: this.opts.config,
|
|
54
|
+
fetcher: opts.fetcher,
|
|
55
|
+
serviceLocator: opts.serviceLocator,
|
|
56
|
+
customResources: opts.customResources,
|
|
57
|
+
objectTypesToFetch: opts.objectTypesToFetch,
|
|
58
|
+
authStrategy: new DispatchStrategy.DispatchStrategy({
|
|
59
|
+
authStrategyMap: Object.fromEntries(opts.authStrategyMap.entries())
|
|
60
|
+
})
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async defaultObjectsProviderOptions() {
|
|
64
|
+
const customResources = (this.opts.config.getOptionalConfigArray("kubernetes.customResources") ?? []).map(
|
|
65
|
+
(c) => ({
|
|
66
|
+
group: c.getString("group"),
|
|
67
|
+
apiVersion: c.getString("apiVersion"),
|
|
68
|
+
plural: c.getString("plural"),
|
|
69
|
+
objectType: "customresources"
|
|
70
|
+
})
|
|
71
|
+
);
|
|
72
|
+
const objectTypesToFetchStrings = this.opts.config.getOptionalStringArray(
|
|
73
|
+
"kubernetes.objectTypes"
|
|
74
|
+
);
|
|
75
|
+
const apiVersionOverrides = this.opts.config.getOptionalConfig(
|
|
76
|
+
"kubernetes.apiVersionOverrides"
|
|
77
|
+
);
|
|
78
|
+
let objectTypesToFetch = void 0;
|
|
79
|
+
if (objectTypesToFetchStrings) {
|
|
80
|
+
objectTypesToFetch = KubernetesFanOutHandler.ALL_OBJECTS.filter(
|
|
81
|
+
(obj) => objectTypesToFetchStrings.includes(obj.objectType)
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
if (apiVersionOverrides) {
|
|
85
|
+
objectTypesToFetch ??= KubernetesFanOutHandler.DEFAULT_OBJECTS;
|
|
86
|
+
for (const obj of objectTypesToFetch) {
|
|
87
|
+
if (apiVersionOverrides.has(obj.objectType)) {
|
|
88
|
+
obj.apiVersion = apiVersionOverrides.getString(obj.objectType);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
customResources,
|
|
94
|
+
objectTypesToFetch: objectTypesToFetch ?? []
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
async init() {
|
|
98
|
+
const fetcher = await this.opts.fetcher?.({ getDefault: this.defaultFetcher }) ?? await this.defaultFetcher();
|
|
99
|
+
const authStrategyMap = this.opts.authStrategyMap ?? await this.defaultAuthStrategy();
|
|
100
|
+
const clusterSupplier = await this.opts.clusterSupplier?.({
|
|
101
|
+
getDefault: () => this.defaultClusterSupplier({ authStrategyMap })
|
|
102
|
+
}) ?? await this.defaultClusterSupplier({ authStrategyMap });
|
|
103
|
+
const serviceLocator = await this.opts.serviceLocator?.({
|
|
104
|
+
getDefault: () => this.defaultServiceLocator({ clusterSupplier }),
|
|
105
|
+
clusterSupplier
|
|
106
|
+
}) ?? await this.defaultServiceLocator({ clusterSupplier });
|
|
107
|
+
const { customResources, objectTypesToFetch } = await this.defaultObjectsProviderOptions();
|
|
108
|
+
const objectsProvider = await this.opts.objectsProvider?.({
|
|
109
|
+
getDefault: () => this.defaultObjectsProvider({
|
|
110
|
+
clusterSupplier,
|
|
111
|
+
authStrategyMap,
|
|
112
|
+
fetcher,
|
|
113
|
+
serviceLocator,
|
|
114
|
+
customResources,
|
|
115
|
+
objectTypesToFetch
|
|
116
|
+
}),
|
|
117
|
+
clusterSupplier,
|
|
118
|
+
serviceLocator,
|
|
119
|
+
customResources,
|
|
120
|
+
objectTypesToFetch,
|
|
121
|
+
authStrategy: new DispatchStrategy.DispatchStrategy({
|
|
122
|
+
authStrategyMap: Object.fromEntries(authStrategyMap.entries())
|
|
123
|
+
})
|
|
124
|
+
}) ?? await this.defaultObjectsProvider({
|
|
125
|
+
clusterSupplier,
|
|
126
|
+
authStrategyMap,
|
|
127
|
+
fetcher,
|
|
128
|
+
serviceLocator,
|
|
129
|
+
customResources,
|
|
130
|
+
objectTypesToFetch
|
|
131
|
+
});
|
|
132
|
+
return {
|
|
133
|
+
fetcher,
|
|
134
|
+
authStrategyMap,
|
|
135
|
+
clusterSupplier,
|
|
136
|
+
serviceLocator,
|
|
137
|
+
objectsProvider
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
exports.KubernetesInitializer = KubernetesInitializer;
|
|
143
|
+
//# sourceMappingURL=KubernetesInitializer.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KubernetesInitializer.cjs.js","sources":["../../src/service/KubernetesInitializer.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AuthenticationStrategy,\n CustomResource,\n KubernetesClustersSupplier,\n KubernetesClusterSupplierFactory,\n KubernetesFetcher,\n KubernetesFetcherFactory,\n KubernetesObjectsProviderFactory,\n KubernetesObjectTypes,\n KubernetesServiceLocator,\n KubernetesServiceLocatorFactory,\n ObjectToFetch,\n} from '@backstage/plugin-kubernetes-node';\nimport { KubernetesClientBasedFetcher } from './KubernetesFetcher';\nimport {\n AuthService,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { buildDefaultAuthStrategyMap } from '../auth/buildDefaultAuthStrategyMap';\nimport { Duration } from 'luxon';\nimport { getCombinedClusterSupplier } from '../cluster-locator';\nimport { DispatchStrategy } from '../auth/DispatchStrategy';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { buildDefaultServiceLocator } from '../service-locator/buildDefaultServiceLocator';\nimport {\n ALL_OBJECTS,\n DEFAULT_OBJECTS,\n KubernetesFanOutHandler,\n} from './KubernetesFanOutHandler';\n\nexport class KubernetesInitializer {\n constructor(\n private readonly opts: {\n fetcher?: KubernetesFetcherFactory;\n authStrategyMap?: Map<string, AuthenticationStrategy>;\n clusterSupplier?: KubernetesClusterSupplierFactory;\n logger: LoggerService;\n config: RootConfigService;\n catalog: CatalogService;\n auth: AuthService;\n serviceLocator?: KubernetesServiceLocatorFactory;\n objectsProvider?: KubernetesObjectsProviderFactory;\n },\n ) {}\n\n static create(opts: {\n fetcher?: KubernetesFetcherFactory;\n clusterSupplier?: KubernetesClusterSupplierFactory;\n serviceLocator?: KubernetesServiceLocatorFactory;\n objectsProvider?: KubernetesObjectsProviderFactory;\n authStrategyMap?: Map<string, AuthenticationStrategy>;\n logger: LoggerService;\n config: RootConfigService;\n catalog: CatalogService;\n auth: AuthService;\n }) {\n return new KubernetesInitializer(opts);\n }\n\n private async defaultFetcher() {\n return new KubernetesClientBasedFetcher({\n logger: this.opts.logger,\n });\n }\n\n private async defaultAuthStrategy() {\n return buildDefaultAuthStrategyMap({\n logger: this.opts.logger,\n config: this.opts.config,\n });\n }\n\n private async defaultClusterSupplier(opts: {\n authStrategyMap: Map<string, AuthenticationStrategy>;\n }) {\n const refreshInterval = Duration.fromObject({\n minutes: 60,\n });\n\n return getCombinedClusterSupplier(\n this.opts.config,\n this.opts.catalog,\n new DispatchStrategy({\n authStrategyMap: Object.fromEntries(opts.authStrategyMap.entries()),\n }),\n this.opts.logger,\n refreshInterval,\n this.opts.auth,\n );\n }\n\n private async defaultServiceLocator(opts: {\n clusterSupplier: KubernetesClustersSupplier;\n }) {\n return buildDefaultServiceLocator({\n config: this.opts.config,\n clusterSupplier: opts.clusterSupplier,\n });\n }\n\n private async defaultObjectsProvider(opts: {\n clusterSupplier: KubernetesClustersSupplier;\n authStrategyMap: Map<string, AuthenticationStrategy>;\n fetcher: KubernetesFetcher;\n serviceLocator: KubernetesServiceLocator;\n customResources: CustomResource[];\n objectTypesToFetch: ObjectToFetch[];\n }) {\n return new KubernetesFanOutHandler({\n logger: this.opts.logger,\n config: this.opts.config,\n fetcher: opts.fetcher,\n serviceLocator: opts.serviceLocator,\n customResources: opts.customResources,\n objectTypesToFetch: opts.objectTypesToFetch,\n authStrategy: new DispatchStrategy({\n authStrategyMap: Object.fromEntries(opts.authStrategyMap.entries()),\n }),\n });\n }\n\n private async defaultObjectsProviderOptions() {\n const customResources: CustomResource[] = (\n this.opts.config.getOptionalConfigArray('kubernetes.customResources') ??\n []\n ).map(\n c =>\n ({\n group: c.getString('group'),\n apiVersion: c.getString('apiVersion'),\n plural: c.getString('plural'),\n objectType: 'customresources',\n } as CustomResource),\n );\n const objectTypesToFetchStrings = this.opts.config.getOptionalStringArray(\n 'kubernetes.objectTypes',\n ) as KubernetesObjectTypes[];\n\n const apiVersionOverrides = this.opts.config.getOptionalConfig(\n 'kubernetes.apiVersionOverrides',\n );\n\n let objectTypesToFetch: ObjectToFetch[] | undefined = undefined;\n\n if (objectTypesToFetchStrings) {\n objectTypesToFetch = ALL_OBJECTS.filter(obj =>\n objectTypesToFetchStrings.includes(obj.objectType),\n );\n }\n\n if (apiVersionOverrides) {\n objectTypesToFetch ??= DEFAULT_OBJECTS;\n\n for (const obj of objectTypesToFetch) {\n if (apiVersionOverrides.has(obj.objectType)) {\n obj.apiVersion = apiVersionOverrides.getString(obj.objectType);\n }\n }\n }\n\n return {\n customResources,\n objectTypesToFetch: objectTypesToFetch ?? [],\n };\n }\n\n async init() {\n const fetcher =\n (await this.opts.fetcher?.({ getDefault: this.defaultFetcher })) ??\n (await this.defaultFetcher());\n\n const authStrategyMap =\n this.opts.authStrategyMap ?? (await this.defaultAuthStrategy());\n\n const clusterSupplier =\n (await this.opts.clusterSupplier?.({\n getDefault: () => this.defaultClusterSupplier({ authStrategyMap }),\n })) ?? (await this.defaultClusterSupplier({ authStrategyMap }));\n\n const serviceLocator =\n (await this.opts.serviceLocator?.({\n getDefault: () => this.defaultServiceLocator({ clusterSupplier }),\n clusterSupplier,\n })) ?? (await this.defaultServiceLocator({ clusterSupplier }));\n\n const { customResources, objectTypesToFetch } =\n await this.defaultObjectsProviderOptions();\n\n const objectsProvider =\n (await this.opts.objectsProvider?.({\n getDefault: () =>\n this.defaultObjectsProvider({\n clusterSupplier,\n authStrategyMap,\n fetcher,\n serviceLocator,\n customResources,\n objectTypesToFetch,\n }),\n clusterSupplier,\n serviceLocator,\n customResources,\n objectTypesToFetch,\n authStrategy: new DispatchStrategy({\n authStrategyMap: Object.fromEntries(authStrategyMap.entries()),\n }),\n })) ??\n (await this.defaultObjectsProvider({\n clusterSupplier,\n authStrategyMap,\n fetcher,\n serviceLocator,\n customResources,\n objectTypesToFetch,\n }));\n\n return {\n fetcher,\n authStrategyMap,\n clusterSupplier,\n serviceLocator,\n objectsProvider,\n };\n }\n}\n"],"names":["KubernetesClientBasedFetcher","buildDefaultAuthStrategyMap","Duration","getCombinedClusterSupplier","DispatchStrategy","buildDefaultServiceLocator","KubernetesFanOutHandler","ALL_OBJECTS","DEFAULT_OBJECTS"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KubernetesProxy.cjs.js","sources":["../../src/service/KubernetesProxy.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n ErrorResponseBody,\n ForwardedError,\n NotAllowedError,\n NotFoundError,\n serializeError,\n} from '@backstage/errors';\nimport {\n ANNOTATION_KUBERNETES_AUTH_PROVIDER,\n SERVICEACCOUNT_CA_PATH,\n kubernetesProxyPermission,\n KubernetesRequestAuth,\n} from '@backstage/plugin-kubernetes-common';\nimport { AuthorizeResult } from '@backstage/plugin-permission-common';\nimport type { Cluster } from '@kubernetes/client-node';\nimport { createProxyMiddleware, RequestHandler } from 'http-proxy-middleware';\nimport fs from 'fs-extra';\n\nimport {\n AuthenticationStrategy,\n ClusterDetails,\n KubernetesClustersSupplier,\n} from '@backstage/plugin-kubernetes-node';\n\nimport type { Request } from 'express';\nimport { IncomingHttpHeaders } from 'http';\nimport {\n DiscoveryService,\n HttpAuthService,\n LoggerService,\n PermissionsService,\n} from '@backstage/backend-plugin-api';\n\nexport const APPLICATION_JSON: string = 'application/json';\n\n/**\n * The header that is used to specify the cluster name.\n *\n * @public\n */\nexport const HEADER_KUBERNETES_CLUSTER: string = 'Backstage-Kubernetes-Cluster';\n\n/**\n * The header that is used to specify the Authentication Authorities token.\n * e.x if using the google auth provider as your authentication authority then this field would be the google provided bearer token.\n * @public\n */\nexport const HEADER_KUBERNETES_AUTH: string =\n 'Backstage-Kubernetes-Authorization';\n\n/**\n * The options object expected to be passed as a parameter to KubernetesProxy.createRequestHandler().\n *\n * @public\n */\nexport type KubernetesProxyCreateRequestHandlerOptions = {\n permissionApi: PermissionsService;\n};\n\n/**\n * Options accepted as a parameter by the KubernetesProxy\n *\n * @public\n */\nexport type KubernetesProxyOptions = {\n logger: LoggerService;\n clusterSupplier: KubernetesClustersSupplier;\n authStrategy: AuthenticationStrategy;\n discovery: DiscoveryService;\n httpAuth: HttpAuthService;\n};\n\n/**\n * A proxy that routes requests to the Kubernetes API.\n *\n * @public\n */\nexport class KubernetesProxy {\n private readonly middlewareForClusterName = new Map<string, RequestHandler>();\n private readonly logger: LoggerService;\n private readonly clusterSupplier: KubernetesClustersSupplier;\n private readonly authStrategy: AuthenticationStrategy;\n private readonly httpAuth: HttpAuthService;\n\n constructor(options: KubernetesProxyOptions) {\n this.logger = options.logger;\n this.clusterSupplier = options.clusterSupplier;\n this.authStrategy = options.authStrategy;\n this.httpAuth = options.httpAuth;\n }\n\n public createRequestHandler(\n options: KubernetesProxyCreateRequestHandlerOptions,\n ): RequestHandler {\n const { permissionApi } = options;\n return async (req, res, next) => {\n const authorizeResponse = await permissionApi.authorize(\n [{ permission: kubernetesProxyPermission }],\n {\n credentials: await this.httpAuth.credentials(req),\n },\n );\n const auth = authorizeResponse[0];\n\n if (auth.result === AuthorizeResult.DENY) {\n res.status(403).json({ error: new NotAllowedError('Unauthorized') });\n return;\n }\n\n const middleware = await this.getMiddleware(req);\n\n // If req is an upgrade handshake, use middleware upgrade instead of http request handler https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade\n if (\n req.header('connection')?.toLowerCase() === 'upgrade' &&\n req.header('upgrade')?.toLowerCase() === 'websocket'\n ) {\n // Missing the `head`, since it's optional we pass undefined to avoid type issues\n middleware.upgrade!(req, req.socket, undefined);\n } else {\n middleware(req, res, next);\n }\n };\n }\n\n // We create one middleware per remote cluster and hold on to them, because\n // the secure property isn't possible to decide on a per-request basis with a\n // single middleware instance - and we don't expect it to change over time.\n private async getMiddleware(originalReq: Request): Promise<RequestHandler> {\n const originalCluster = await this.getClusterForRequest(originalReq);\n let middleware = this.middlewareForClusterName.get(originalCluster.name);\n if (!middleware) {\n const logger = this.logger.child({ cluster: originalCluster.name });\n middleware = createProxyMiddleware({\n logProvider: () => ({\n log: logger.info.bind(logger),\n debug: logger.debug.bind(logger),\n info: logger.info.bind(logger),\n warn: logger.warn.bind(logger),\n error: logger.error.bind(logger),\n }),\n ws: true,\n secure: !originalCluster.skipTLSVerify,\n changeOrigin: true,\n pathRewrite: async (path, req) => {\n // Re-evaluate the cluster on each request, in case it has changed\n const cluster = await this.getClusterForRequest(req);\n const url = new URL(cluster.url);\n return path.replace(\n new RegExp(`^${originalReq.baseUrl}`),\n url.pathname || '',\n );\n },\n router: async req => {\n // Re-evaluate the cluster on each request, in case it has changed\n const cluster = await this.getClusterForRequest(req);\n const url = new URL(cluster.url);\n\n const { bufferFromFileOrString } = await import(\n '@kubernetes/client-node'\n );\n\n const target: any = {\n protocol: url.protocol,\n host: url.hostname,\n port: url.port,\n ca: bufferFromFileOrString(\n cluster.caFile,\n cluster.caData,\n )?.toString(),\n };\n\n const authHeader =\n req.headers[HEADER_KUBERNETES_AUTH.toLocaleLowerCase('en-US')];\n if (typeof authHeader === 'string') {\n req.headers.authorization = authHeader;\n } else {\n // Map Backstage-Kubernetes-Authorization-X-X headers to a KubernetesRequestAuth object\n const authObj = KubernetesProxy.authHeadersToKubernetesRequestAuth(\n req.headers,\n );\n\n const credential = await this.getClusterForRequest(req).then(cd => {\n return this.authStrategy.getCredential(cd, authObj);\n });\n\n if (credential.type === 'bearer token') {\n req.headers.authorization = `Bearer ${credential.token}`;\n } else if (credential.type === 'x509 client certificate') {\n target.key = credential.key;\n target.cert = credential.cert;\n }\n }\n\n return target;\n },\n onError: (error, req, res) => {\n const wrappedError = new ForwardedError(\n `Cluster '${originalCluster.name}' request error`,\n error,\n );\n\n logger.error('Kubernetes proxy error', wrappedError);\n\n const body: ErrorResponseBody = {\n error: serializeError(wrappedError, {\n includeStack: process.env.NODE_ENV === 'development',\n }),\n request: { method: req.method, url: req.originalUrl },\n response: { statusCode: 500 },\n };\n res.status(500).json(body);\n },\n });\n this.middlewareForClusterName.set(originalCluster.name, middleware);\n }\n return middleware;\n }\n\n private async getClusterForRequest(req: Request): Promise<ClusterDetails> {\n const { KubeConfig } = await import('@kubernetes/client-node');\n\n const clusterName = req.headers[HEADER_KUBERNETES_CLUSTER.toLowerCase()];\n const clusters = await this.clusterSupplier.getClusters({\n credentials: await this.httpAuth.credentials(req),\n });\n\n if (!clusters || clusters.length <= 0) {\n throw new NotFoundError(`No Clusters configured`);\n }\n\n const hasClusterNameHeader =\n typeof clusterName === 'string' && clusterName.length > 0;\n\n let cluster: ClusterDetails | undefined;\n\n if (hasClusterNameHeader) {\n cluster = clusters.find(c => c.name === clusterName);\n } else if (clusters.length === 1) {\n cluster = clusters.at(0);\n }\n\n if (!cluster) {\n throw new NotFoundError(`Cluster '${clusterName}' not found`);\n }\n\n const authProvider =\n cluster.authMetadata[ANNOTATION_KUBERNETES_AUTH_PROVIDER];\n\n if (\n authProvider === 'serviceAccount' &&\n fs.pathExistsSync(SERVICEACCOUNT_CA_PATH) &&\n !cluster.authMetadata.serviceAccountToken\n ) {\n const kc = new KubeConfig();\n kc.loadFromCluster();\n const clusterFromKubeConfig = kc.getCurrentCluster() as Cluster;\n\n const url = new URL(clusterFromKubeConfig.server);\n cluster.url = clusterFromKubeConfig.server;\n if (url.protocol === 'https:') {\n cluster.caFile = clusterFromKubeConfig.caFile;\n }\n }\n\n return cluster;\n }\n\n private static authHeadersToKubernetesRequestAuth(\n originalHeaders: IncomingHttpHeaders,\n ): KubernetesRequestAuth {\n return Object.keys(originalHeaders)\n .filter(header => header.startsWith('backstage-kubernetes-authorization'))\n .map(header =>\n KubernetesProxy.headerToDictionary(header, originalHeaders),\n )\n .filter(headerAsDic => Object.keys(headerAsDic).length !== 0)\n .reduce(KubernetesProxy.combineHeaders, {});\n }\n\n private static headerToDictionary(\n header: string,\n originalHeaders: IncomingHttpHeaders,\n ): KubernetesRequestAuth {\n const obj: KubernetesRequestAuth = {};\n const headerSplitted = header.split('-');\n if (headerSplitted.length >= 4) {\n const framework = headerSplitted[3].toLowerCase();\n if (headerSplitted.length >= 5) {\n const provider = headerSplitted.slice(4).join('-').toLowerCase();\n obj[framework] = { [provider]: originalHeaders[header] };\n } else {\n obj[framework] = originalHeaders[header];\n }\n }\n return obj;\n }\n\n private static combineHeaders(\n authObj: any,\n header: any,\n ): KubernetesRequestAuth {\n const framework = Object.keys(header)[0];\n\n if (authObj[framework]) {\n authObj[framework] = {\n ...authObj[framework],\n ...header[framework],\n };\n } else {\n authObj[framework] = header[framework];\n }\n\n return authObj;\n }\n}\n"],"names":["kubernetesProxyPermission","AuthorizeResult","NotAllowedError","createProxyMiddleware","ForwardedError","serializeError","NotFoundError","ANNOTATION_KUBERNETES_AUTH_PROVIDER","fs","SERVICEACCOUNT_CA_PATH"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"KubernetesProxy.cjs.js","sources":["../../src/service/KubernetesProxy.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n ErrorResponseBody,\n ForwardedError,\n NotAllowedError,\n NotFoundError,\n serializeError,\n} from '@backstage/errors';\nimport {\n ANNOTATION_KUBERNETES_AUTH_PROVIDER,\n SERVICEACCOUNT_CA_PATH,\n kubernetesProxyPermission,\n KubernetesRequestAuth,\n} from '@backstage/plugin-kubernetes-common';\nimport { AuthorizeResult } from '@backstage/plugin-permission-common';\nimport type { Cluster } from '@kubernetes/client-node';\nimport { createProxyMiddleware, RequestHandler } from 'http-proxy-middleware';\nimport fs from 'fs-extra';\n\nimport {\n AuthenticationStrategy,\n ClusterDetails,\n KubernetesClustersSupplier,\n} from '@backstage/plugin-kubernetes-node';\n\nimport type { Request } from 'express';\nimport { IncomingHttpHeaders } from 'http';\nimport {\n DiscoveryService,\n HttpAuthService,\n LoggerService,\n PermissionsService,\n} from '@backstage/backend-plugin-api';\n\nexport const APPLICATION_JSON: string = 'application/json';\n\n/**\n * The header that is used to specify the cluster name.\n *\n * @public\n */\nexport const HEADER_KUBERNETES_CLUSTER: string = 'Backstage-Kubernetes-Cluster';\n\n/**\n * The header that is used to specify the Authentication Authorities token.\n * e.x if using the google auth provider as your authentication authority then this field would be the google provided bearer token.\n * @public\n */\nexport const HEADER_KUBERNETES_AUTH: string =\n 'Backstage-Kubernetes-Authorization';\n\n/**\n * The options object expected to be passed as a parameter to KubernetesProxy.createRequestHandler().\n *\n * @public\n */\nexport type KubernetesProxyCreateRequestHandlerOptions = {\n permissionApi: PermissionsService;\n};\n\n/**\n * Options accepted as a parameter by the KubernetesProxy\n *\n * @public\n */\nexport type KubernetesProxyOptions = {\n logger: LoggerService;\n clusterSupplier: KubernetesClustersSupplier;\n authStrategy: AuthenticationStrategy;\n discovery: DiscoveryService;\n httpAuth: HttpAuthService;\n};\n\n/**\n * A proxy that routes requests to the Kubernetes API.\n *\n * @public\n */\nexport class KubernetesProxy {\n private readonly middlewareForClusterName = new Map<string, RequestHandler>();\n private readonly logger: LoggerService;\n private readonly clusterSupplier: KubernetesClustersSupplier;\n private readonly authStrategy: AuthenticationStrategy;\n private readonly httpAuth: HttpAuthService;\n\n constructor(options: KubernetesProxyOptions) {\n this.logger = options.logger;\n this.clusterSupplier = options.clusterSupplier;\n this.authStrategy = options.authStrategy;\n this.httpAuth = options.httpAuth;\n }\n\n public createRequestHandler(\n options: KubernetesProxyCreateRequestHandlerOptions,\n ): RequestHandler {\n const { permissionApi } = options;\n return async (req, res, next) => {\n const authorizeResponse = await permissionApi.authorize(\n [{ permission: kubernetesProxyPermission }],\n {\n credentials: await this.httpAuth.credentials(req),\n },\n );\n const auth = authorizeResponse[0];\n\n if (auth.result === AuthorizeResult.DENY) {\n res.status(403).json({ error: new NotAllowedError('Unauthorized') });\n return;\n }\n\n const middleware = await this.getMiddleware(req);\n\n // If req is an upgrade handshake, use middleware upgrade instead of http request handler https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade\n if (\n req.header('connection')?.toLowerCase() === 'upgrade' &&\n req.header('upgrade')?.toLowerCase() === 'websocket'\n ) {\n // Missing the `head`, since it's optional we pass undefined to avoid type issues\n middleware.upgrade!(req, req.socket, undefined);\n } else {\n middleware(req, res, next);\n }\n };\n }\n\n // We create one middleware per remote cluster and hold on to them, because\n // the secure property isn't possible to decide on a per-request basis with a\n // single middleware instance - and we don't expect it to change over time.\n private async getMiddleware(originalReq: Request): Promise<RequestHandler> {\n const originalCluster = await this.getClusterForRequest(originalReq);\n let middleware = this.middlewareForClusterName.get(originalCluster.name);\n if (!middleware) {\n const logger = this.logger.child({ cluster: originalCluster.name });\n middleware = createProxyMiddleware({\n logProvider: () => ({\n log: logger.info.bind(logger),\n debug: logger.debug.bind(logger),\n info: logger.info.bind(logger),\n warn: logger.warn.bind(logger),\n error: logger.error.bind(logger),\n }),\n ws: true,\n secure: !originalCluster.skipTLSVerify,\n changeOrigin: true,\n pathRewrite: async (path, req) => {\n // Re-evaluate the cluster on each request, in case it has changed\n const cluster = await this.getClusterForRequest(req);\n const url = new URL(cluster.url);\n return path.replace(\n new RegExp(`^${originalReq.baseUrl}`),\n url.pathname || '',\n );\n },\n router: async req => {\n // Re-evaluate the cluster on each request, in case it has changed\n const cluster = await this.getClusterForRequest(req);\n const url = new URL(cluster.url);\n\n const { bufferFromFileOrString } = await import(\n '@kubernetes/client-node'\n );\n\n const target: any = {\n protocol: url.protocol,\n host: url.hostname,\n port: url.port,\n ca: bufferFromFileOrString(\n cluster.caFile,\n cluster.caData,\n )?.toString(),\n };\n\n const authHeader =\n req.headers[HEADER_KUBERNETES_AUTH.toLocaleLowerCase('en-US')];\n if (typeof authHeader === 'string') {\n req.headers.authorization = authHeader;\n } else {\n // Map Backstage-Kubernetes-Authorization-X-X headers to a KubernetesRequestAuth object\n const authObj = KubernetesProxy.authHeadersToKubernetesRequestAuth(\n req.headers,\n );\n\n const credential = await this.getClusterForRequest(req).then(cd => {\n return this.authStrategy.getCredential(cd, authObj);\n });\n\n if (credential.type === 'bearer token') {\n req.headers.authorization = `Bearer ${credential.token}`;\n } else if (credential.type === 'x509 client certificate') {\n target.key = credential.key;\n target.cert = credential.cert;\n }\n }\n\n return target;\n },\n onError: (error, req, res) => {\n const wrappedError = new ForwardedError(\n `Cluster '${originalCluster.name}' request error`,\n error,\n );\n\n logger.error('Kubernetes proxy error', wrappedError);\n\n const body: ErrorResponseBody = {\n error: serializeError(wrappedError, {\n includeStack: process.env.NODE_ENV === 'development',\n }),\n request: { method: req.method, url: req.originalUrl },\n response: { statusCode: 500 },\n };\n res.status(500).json(body);\n },\n });\n this.middlewareForClusterName.set(originalCluster.name, middleware);\n }\n return middleware;\n }\n\n private async getClusterForRequest(req: Request): Promise<ClusterDetails> {\n const { KubeConfig } = await import('@kubernetes/client-node');\n\n const clusterName = req.headers[HEADER_KUBERNETES_CLUSTER.toLowerCase()];\n const clusters = await this.clusterSupplier.getClusters({\n credentials: await this.httpAuth.credentials(req),\n });\n\n if (!clusters || clusters.length <= 0) {\n throw new NotFoundError(`No Clusters configured`);\n }\n\n const hasClusterNameHeader =\n typeof clusterName === 'string' && clusterName.length > 0;\n\n let cluster: ClusterDetails | undefined;\n\n if (hasClusterNameHeader) {\n cluster = clusters.find(c => c.name === clusterName);\n } else if (clusters.length === 1) {\n cluster = clusters.at(0);\n }\n\n if (!cluster) {\n throw new NotFoundError(`Cluster '${clusterName}' not found`);\n }\n\n const authProvider =\n cluster.authMetadata[ANNOTATION_KUBERNETES_AUTH_PROVIDER];\n\n if (\n authProvider === 'serviceAccount' &&\n fs.pathExistsSync(SERVICEACCOUNT_CA_PATH) &&\n !cluster.authMetadata.serviceAccountToken\n ) {\n const kc = new KubeConfig();\n kc.loadFromCluster();\n const clusterFromKubeConfig = kc.getCurrentCluster() as Cluster;\n\n const url = new URL(clusterFromKubeConfig.server);\n cluster.url = clusterFromKubeConfig.server;\n if (url.protocol === 'https:') {\n cluster.caFile = clusterFromKubeConfig.caFile;\n }\n }\n\n return cluster;\n }\n\n private static authHeadersToKubernetesRequestAuth(\n originalHeaders: IncomingHttpHeaders,\n ): KubernetesRequestAuth {\n return Object.keys(originalHeaders)\n .filter(header => header.startsWith('backstage-kubernetes-authorization'))\n .map(header =>\n KubernetesProxy.headerToDictionary(header, originalHeaders),\n )\n .filter(headerAsDic => Object.keys(headerAsDic).length !== 0)\n .reduce(KubernetesProxy.combineHeaders, {});\n }\n\n private static headerToDictionary(\n header: string,\n originalHeaders: IncomingHttpHeaders,\n ): KubernetesRequestAuth {\n const obj: KubernetesRequestAuth = {};\n const headerSplitted = header.split('-');\n if (headerSplitted.length >= 4) {\n const framework = headerSplitted[3].toLowerCase();\n if (headerSplitted.length >= 5) {\n const provider = headerSplitted.slice(4).join('-').toLowerCase();\n obj[framework] = { [provider]: originalHeaders[header] };\n } else {\n obj[framework] = originalHeaders[header];\n }\n }\n return obj;\n }\n\n private static combineHeaders(\n authObj: any,\n header: any,\n ): KubernetesRequestAuth {\n const framework = Object.keys(header)[0];\n\n if (authObj[framework]) {\n authObj[framework] = {\n ...authObj[framework],\n ...header[framework],\n };\n } else {\n authObj[framework] = header[framework];\n }\n\n return authObj;\n }\n}\n"],"names":["kubernetesProxyPermission","AuthorizeResult","NotAllowedError","createProxyMiddleware","ForwardedError","serializeError","NotFoundError","ANNOTATION_KUBERNETES_AUTH_PROVIDER","fs","SERVICEACCOUNT_CA_PATH"],"mappings":"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"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var pluginKubernetesCommon = require('@backstage/plugin-kubernetes-common');
|
|
4
|
+
var pluginPermissionNode = require('@backstage/plugin-permission-node');
|
|
5
|
+
var express = require('express');
|
|
6
|
+
var Router = require('express-promise-router');
|
|
7
|
+
require('@aws-sdk/credential-providers');
|
|
8
|
+
require('@aws-sdk/signature-v4');
|
|
9
|
+
require('@aws-crypto/sha256-js');
|
|
10
|
+
require('@backstage/integration-aws-node');
|
|
11
|
+
require('@azure/identity');
|
|
12
|
+
require('@google-cloud/container');
|
|
13
|
+
var DispatchStrategy = require('../auth/DispatchStrategy.cjs.js');
|
|
14
|
+
require('fs-extra');
|
|
15
|
+
var resourcesRoutes = require('../routes/resourcesRoutes.cjs.js');
|
|
16
|
+
var KubernetesProxy = require('./KubernetesProxy.cjs.js');
|
|
17
|
+
var requirePermission = require('../auth/requirePermission.cjs.js');
|
|
18
|
+
|
|
19
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
20
|
+
|
|
21
|
+
var express__default = /*#__PURE__*/_interopDefaultCompat(express);
|
|
22
|
+
var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
|
|
23
|
+
|
|
24
|
+
class KubernetesRouter {
|
|
25
|
+
constructor(env) {
|
|
26
|
+
this.env = env;
|
|
27
|
+
}
|
|
28
|
+
static create(env) {
|
|
29
|
+
return new KubernetesRouter(env);
|
|
30
|
+
}
|
|
31
|
+
async getRouter() {
|
|
32
|
+
const {
|
|
33
|
+
logger,
|
|
34
|
+
config,
|
|
35
|
+
permissions,
|
|
36
|
+
authStrategyMap,
|
|
37
|
+
clusterSupplier,
|
|
38
|
+
objectsProvider,
|
|
39
|
+
catalog,
|
|
40
|
+
discovery,
|
|
41
|
+
httpAuth
|
|
42
|
+
} = this.env;
|
|
43
|
+
logger.info("Initializing Kubernetes backend");
|
|
44
|
+
if (!config.has("kubernetes")) {
|
|
45
|
+
if (process.env.NODE_ENV !== "development") {
|
|
46
|
+
throw new Error("Kubernetes configuration is missing");
|
|
47
|
+
}
|
|
48
|
+
logger.warn(
|
|
49
|
+
"Failed to initialize kubernetes backend: kubernetes config is missing"
|
|
50
|
+
);
|
|
51
|
+
return Router__default.default();
|
|
52
|
+
}
|
|
53
|
+
const proxy = this.buildProxy(
|
|
54
|
+
logger,
|
|
55
|
+
clusterSupplier,
|
|
56
|
+
discovery,
|
|
57
|
+
httpAuth,
|
|
58
|
+
authStrategyMap
|
|
59
|
+
);
|
|
60
|
+
return this.buildRouter(
|
|
61
|
+
objectsProvider,
|
|
62
|
+
clusterSupplier,
|
|
63
|
+
catalog,
|
|
64
|
+
proxy,
|
|
65
|
+
permissions,
|
|
66
|
+
httpAuth,
|
|
67
|
+
authStrategyMap
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
buildProxy(logger, clusterSupplier, discovery, httpAuth, authStrategyMap) {
|
|
71
|
+
const authStrategy = new DispatchStrategy.DispatchStrategy({
|
|
72
|
+
authStrategyMap
|
|
73
|
+
});
|
|
74
|
+
return new KubernetesProxy.KubernetesProxy({
|
|
75
|
+
logger,
|
|
76
|
+
clusterSupplier,
|
|
77
|
+
authStrategy,
|
|
78
|
+
discovery,
|
|
79
|
+
httpAuth
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
buildRouter(objectsProvider, clusterSupplier, catalog, proxy, permissionApi, httpAuth, authStrategyMap) {
|
|
83
|
+
const logger = this.env.logger;
|
|
84
|
+
const router = Router__default.default();
|
|
85
|
+
router.use("/proxy", proxy.createRequestHandler({ permissionApi }));
|
|
86
|
+
router.use(express__default.default.json());
|
|
87
|
+
router.use(
|
|
88
|
+
pluginPermissionNode.createPermissionIntegrationRouter({
|
|
89
|
+
permissions: pluginKubernetesCommon.kubernetesPermissions
|
|
90
|
+
})
|
|
91
|
+
);
|
|
92
|
+
router.post("/services/:serviceId", async (req, res) => {
|
|
93
|
+
await requirePermission.requirePermission(
|
|
94
|
+
permissionApi,
|
|
95
|
+
pluginKubernetesCommon.kubernetesResourcesReadPermission,
|
|
96
|
+
httpAuth,
|
|
97
|
+
req
|
|
98
|
+
);
|
|
99
|
+
const serviceId = req.params.serviceId;
|
|
100
|
+
const requestBody = req.body;
|
|
101
|
+
try {
|
|
102
|
+
const response = await objectsProvider.getKubernetesObjectsByEntity(
|
|
103
|
+
{
|
|
104
|
+
entity: requestBody.entity,
|
|
105
|
+
auth: requestBody.auth || {}
|
|
106
|
+
},
|
|
107
|
+
{ credentials: await httpAuth.credentials(req) }
|
|
108
|
+
);
|
|
109
|
+
res.json(response);
|
|
110
|
+
} catch (e) {
|
|
111
|
+
logger.error(
|
|
112
|
+
`action=retrieveObjectsByServiceId service=${serviceId}, error=${e}`
|
|
113
|
+
);
|
|
114
|
+
res.status(500).json({ error: e.message });
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
router.get("/clusters", async (req, res) => {
|
|
118
|
+
await requirePermission.requirePermission(
|
|
119
|
+
permissionApi,
|
|
120
|
+
pluginKubernetesCommon.kubernetesClustersReadPermission,
|
|
121
|
+
httpAuth,
|
|
122
|
+
req
|
|
123
|
+
);
|
|
124
|
+
const credentials = await httpAuth.credentials(req);
|
|
125
|
+
const clusterDetails = await this.fetchClusterDetails(clusterSupplier, {
|
|
126
|
+
credentials
|
|
127
|
+
});
|
|
128
|
+
res.json({
|
|
129
|
+
items: clusterDetails.map((cd) => {
|
|
130
|
+
const oidcTokenProvider = cd.authMetadata[pluginKubernetesCommon.ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER];
|
|
131
|
+
const authProvider = cd.authMetadata[pluginKubernetesCommon.ANNOTATION_KUBERNETES_AUTH_PROVIDER];
|
|
132
|
+
const strategy = authStrategyMap[authProvider];
|
|
133
|
+
let auth = {};
|
|
134
|
+
if (strategy) {
|
|
135
|
+
auth = strategy.presentAuthMetadata(cd.authMetadata);
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
name: cd.name,
|
|
139
|
+
title: cd.title,
|
|
140
|
+
dashboardUrl: cd.dashboardUrl,
|
|
141
|
+
authProvider,
|
|
142
|
+
...oidcTokenProvider && { oidcTokenProvider },
|
|
143
|
+
...auth && Object.keys(auth).length !== 0 && { auth }
|
|
144
|
+
};
|
|
145
|
+
})
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
resourcesRoutes.addResourceRoutesToRouter(
|
|
149
|
+
router,
|
|
150
|
+
catalog,
|
|
151
|
+
objectsProvider,
|
|
152
|
+
httpAuth,
|
|
153
|
+
permissionApi
|
|
154
|
+
);
|
|
155
|
+
return router;
|
|
156
|
+
}
|
|
157
|
+
async fetchClusterDetails(clusterSupplier, options) {
|
|
158
|
+
const clusterDetails = await clusterSupplier.getClusters(options);
|
|
159
|
+
this.env.logger.debug(
|
|
160
|
+
`action=loadClusterDetails numOfClustersLoaded=${clusterDetails.length}`
|
|
161
|
+
);
|
|
162
|
+
return clusterDetails;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
exports.KubernetesRouter = KubernetesRouter;
|
|
167
|
+
//# sourceMappingURL=KubernetesRouter.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KubernetesRouter.cjs.js","sources":["../../src/service/KubernetesRouter.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Config } from '@backstage/config';\nimport {\n ANNOTATION_KUBERNETES_AUTH_PROVIDER,\n ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER,\n kubernetesClustersReadPermission,\n kubernetesPermissions,\n kubernetesResourcesReadPermission,\n} from '@backstage/plugin-kubernetes-common';\nimport { PermissionEvaluator } from '@backstage/plugin-permission-common';\nimport { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node';\nimport express from 'express';\nimport Router from 'express-promise-router';\n\nimport { DispatchStrategy } from '../auth';\n\nimport {\n AuthService,\n BackstageCredentials,\n DiscoveryService,\n HttpAuthService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport {\n AuthenticationStrategy,\n AuthMetadata,\n KubernetesClustersSupplier,\n KubernetesFetcher,\n KubernetesObjectsProvider,\n KubernetesServiceLocator,\n} from '@backstage/plugin-kubernetes-node';\nimport { addResourceRoutesToRouter } from '../routes/resourcesRoutes';\nimport { ObjectsByEntityRequest } from '../types/types';\nimport { KubernetesProxy } from './KubernetesProxy';\nimport { requirePermission } from '../auth/requirePermission';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport interface KubernetesEnvironment {\n logger: LoggerService;\n config: Config;\n catalog: CatalogService;\n discovery: DiscoveryService;\n permissions: PermissionEvaluator;\n auth: AuthService;\n httpAuth: HttpAuthService;\n authStrategyMap: { [key: string]: AuthenticationStrategy };\n fetcher: KubernetesFetcher;\n clusterSupplier: KubernetesClustersSupplier;\n serviceLocator: KubernetesServiceLocator;\n objectsProvider: KubernetesObjectsProvider;\n}\n\nexport class KubernetesRouter {\n static create(env: KubernetesEnvironment) {\n return new KubernetesRouter(env);\n }\n\n constructor(protected readonly env: KubernetesEnvironment) {}\n\n public async getRouter() {\n const {\n logger,\n config,\n permissions,\n authStrategyMap,\n clusterSupplier,\n objectsProvider,\n catalog,\n discovery,\n httpAuth,\n } = this.env;\n\n logger.info('Initializing Kubernetes backend');\n\n if (!config.has('kubernetes')) {\n if (process.env.NODE_ENV !== 'development') {\n throw new Error('Kubernetes configuration is missing');\n }\n logger.warn(\n 'Failed to initialize kubernetes backend: kubernetes config is missing',\n );\n return Router();\n }\n\n const proxy = this.buildProxy(\n logger,\n clusterSupplier,\n discovery,\n httpAuth,\n authStrategyMap,\n );\n\n return this.buildRouter(\n objectsProvider,\n clusterSupplier,\n catalog,\n proxy,\n permissions,\n httpAuth,\n authStrategyMap,\n );\n }\n\n private buildProxy(\n logger: LoggerService,\n clusterSupplier: KubernetesClustersSupplier,\n discovery: DiscoveryService,\n httpAuth: HttpAuthService,\n authStrategyMap: { [key: string]: AuthenticationStrategy },\n ): KubernetesProxy {\n const authStrategy = new DispatchStrategy({\n authStrategyMap,\n });\n return new KubernetesProxy({\n logger,\n clusterSupplier,\n authStrategy,\n discovery,\n httpAuth,\n });\n }\n\n private buildRouter(\n objectsProvider: KubernetesObjectsProvider,\n clusterSupplier: KubernetesClustersSupplier,\n catalog: CatalogService,\n proxy: KubernetesProxy,\n permissionApi: PermissionEvaluator,\n httpAuth: HttpAuthService,\n authStrategyMap: { [key: string]: AuthenticationStrategy },\n ): express.Router {\n const logger = this.env.logger;\n const router = Router();\n router.use('/proxy', proxy.createRequestHandler({ permissionApi }));\n router.use(express.json());\n router.use(\n createPermissionIntegrationRouter({\n permissions: kubernetesPermissions,\n }),\n );\n\n // @deprecated\n router.post('/services/:serviceId', async (req, res) => {\n await requirePermission(\n permissionApi,\n kubernetesResourcesReadPermission,\n httpAuth,\n req,\n );\n const serviceId = req.params.serviceId;\n const requestBody: ObjectsByEntityRequest = req.body;\n try {\n const response = await objectsProvider.getKubernetesObjectsByEntity(\n {\n entity: requestBody.entity,\n auth: requestBody.auth || {},\n },\n { credentials: await httpAuth.credentials(req) },\n );\n res.json(response);\n } catch (e) {\n logger.error(\n `action=retrieveObjectsByServiceId service=${serviceId}, error=${e}`,\n );\n res.status(500).json({ error: e.message });\n }\n });\n\n router.get('/clusters', async (req, res) => {\n await requirePermission(\n permissionApi,\n kubernetesClustersReadPermission,\n httpAuth,\n req,\n );\n const credentials = await httpAuth.credentials(req);\n const clusterDetails = await this.fetchClusterDetails(clusterSupplier, {\n credentials,\n });\n res.json({\n items: clusterDetails.map(cd => {\n const oidcTokenProvider =\n cd.authMetadata[ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER];\n const authProvider =\n cd.authMetadata[ANNOTATION_KUBERNETES_AUTH_PROVIDER];\n const strategy = authStrategyMap[authProvider];\n let auth: AuthMetadata = {};\n if (strategy) {\n auth = strategy.presentAuthMetadata(cd.authMetadata);\n }\n\n return {\n name: cd.name,\n title: cd.title,\n dashboardUrl: cd.dashboardUrl,\n authProvider,\n ...(oidcTokenProvider && { oidcTokenProvider }),\n ...(auth && Object.keys(auth).length !== 0 && { auth }),\n };\n }),\n });\n });\n\n addResourceRoutesToRouter(\n router,\n catalog,\n objectsProvider,\n httpAuth,\n permissionApi,\n );\n\n return router;\n }\n\n private async fetchClusterDetails(\n clusterSupplier: KubernetesClustersSupplier,\n options: { credentials: BackstageCredentials },\n ) {\n const clusterDetails = await clusterSupplier.getClusters(options);\n\n this.env.logger.debug(\n `action=loadClusterDetails numOfClustersLoaded=${clusterDetails.length}`,\n );\n\n return clusterDetails;\n }\n}\n"],"names":["Router","DispatchStrategy","KubernetesProxy","express","createPermissionIntegrationRouter","kubernetesPermissions","requirePermission","kubernetesResourcesReadPermission","kubernetesClustersReadPermission","ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER","ANNOTATION_KUBERNETES_AUTH_PROVIDER","addResourceRoutesToRouter"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runPeriodically.cjs.js","sources":["../../src/service/runPeriodically.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Runs a function repeatedly, with a fixed wait between invocations.\n *\n * Supports async functions, and silently ignores exceptions and rejections.\n *\n * @param fn - The function to run. May return a Promise.\n * @param delayMs - The delay between a completed function invocation and the\n * next.\n * @returns A function that, when called, stops the invocation loop.\n */\nexport function runPeriodically(fn: () => any, delayMs: number): () => void {\n let cancel: () => void;\n let cancelled = false;\n const cancellationPromise = new Promise<void>(resolve => {\n cancel = () => {\n resolve();\n cancelled = true;\n };\n });\n\n const startRefresh = async () => {\n while (!cancelled) {\n try {\n await fn();\n } catch {\n // ignore intentionally\n }\n\n await Promise.race([\n new Promise(resolve => setTimeout(resolve, delayMs)),\n cancellationPromise,\n ]);\n }\n };\n startRefresh();\n\n return cancel!;\n}\n"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"runPeriodically.cjs.js","sources":["../../src/service/runPeriodically.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Runs a function repeatedly, with a fixed wait between invocations.\n *\n * Supports async functions, and silently ignores exceptions and rejections.\n *\n * @param fn - The function to run. May return a Promise.\n * @param delayMs - The delay between a completed function invocation and the\n * next.\n * @returns A function that, when called, stops the invocation loop.\n */\nexport function runPeriodically(fn: () => any, delayMs: number): () => void {\n let cancel: () => void;\n let cancelled = false;\n const cancellationPromise = new Promise<void>(resolve => {\n cancel = () => {\n resolve();\n cancelled = true;\n };\n });\n\n const startRefresh = async () => {\n while (!cancelled) {\n try {\n await fn();\n } catch {\n // ignore intentionally\n }\n\n await Promise.race([\n new Promise(resolve => setTimeout(resolve, delayMs)),\n cancellationPromise,\n ]);\n }\n };\n startRefresh();\n\n return cancel!;\n}\n"],"names":[],"mappings":";;AA0BO,SAAS,eAAA,CAAgB,IAAe,OAAA,EAA6B;AAC1E,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI,SAAA,GAAY,KAAA;AAChB,EAAA,MAAM,mBAAA,GAAsB,IAAI,OAAA,CAAc,CAAA,OAAA,KAAW;AACvD,IAAA,MAAA,GAAS,MAAM;AACb,MAAA,OAAA,EAAQ;AACR,MAAA,SAAA,GAAY,IAAA;AAAA,IACd,CAAA;AAAA,EACF,CAAC,CAAA;AAED,EAAA,MAAM,eAAe,YAAY;AAC/B,IAAA,OAAO,CAAC,SAAA,EAAW;AACjB,MAAA,IAAI;AACF,QAAA,MAAM,EAAA,EAAG;AAAA,MACX,CAAA,CAAA,MAAQ;AAAA,MAER;AAEA,MAAA,MAAM,QAAQ,IAAA,CAAK;AAAA,QACjB,IAAI,OAAA,CAAQ,CAAA,OAAA,KAAW,UAAA,CAAW,OAAA,EAAS,OAAO,CAAC,CAAA;AAAA,QACnD;AAAA,OACD,CAAA;AAAA,IACH;AAAA,EACF,CAAA;AACA,EAAA,YAAA,EAAa;AAEb,EAAA,OAAO,MAAA;AACT;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CatalogRelationServiceLocator.cjs.js","sources":["../../src/service-locator/CatalogRelationServiceLocator.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity } from '@backstage/catalog-model';\nimport {\n ClusterDetails,\n KubernetesClustersSupplier,\n KubernetesServiceLocator,\n ServiceLocatorRequestContext,\n} from '@backstage/plugin-kubernetes-node';\n\n// This locator assumes that service is located on the clusters it depends on\n// Therefore it will return the clusters based on the relations it has or none otherwise\nexport class CatalogRelationServiceLocator implements KubernetesServiceLocator {\n private readonly clusterSupplier: KubernetesClustersSupplier;\n\n constructor(clusterSupplier: KubernetesClustersSupplier) {\n this.clusterSupplier = clusterSupplier;\n }\n\n // As this implementation always returns all clusters serviceId is ignored here\n getClustersByEntity(\n entity: Entity,\n requestContext: ServiceLocatorRequestContext,\n ): Promise<{ clusters: ClusterDetails[] }> {\n if (\n entity.relations &&\n entity.relations.some(\n r => r.type === 'dependsOn' && r.targetRef.includes('resource:'),\n )\n ) {\n return this.clusterSupplier\n .getClusters({ credentials: requestContext.credentials })\n .then(clusters => {\n return {\n clusters: clusters.filter(c =>\n this.doesEntityDependOnCluster(entity, c),\n ),\n };\n });\n }\n return Promise.resolve({ clusters: [] });\n }\n\n protected doesEntityDependOnCluster(\n entity: Entity,\n cluster: ClusterDetails,\n ): boolean {\n return entity.relations!.some(\n rel =>\n rel.type === 'dependsOn' &&\n rel.targetRef ===\n `resource:${entity.metadata.namespace ?? 'default'}/${cluster.name}`,\n );\n }\n}\n"],"names":[],"mappings":";;AAyBO,MAAM,
|
|
1
|
+
{"version":3,"file":"CatalogRelationServiceLocator.cjs.js","sources":["../../src/service-locator/CatalogRelationServiceLocator.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity } from '@backstage/catalog-model';\nimport {\n ClusterDetails,\n KubernetesClustersSupplier,\n KubernetesServiceLocator,\n ServiceLocatorRequestContext,\n} from '@backstage/plugin-kubernetes-node';\n\n// This locator assumes that service is located on the clusters it depends on\n// Therefore it will return the clusters based on the relations it has or none otherwise\nexport class CatalogRelationServiceLocator implements KubernetesServiceLocator {\n private readonly clusterSupplier: KubernetesClustersSupplier;\n\n constructor(clusterSupplier: KubernetesClustersSupplier) {\n this.clusterSupplier = clusterSupplier;\n }\n\n // As this implementation always returns all clusters serviceId is ignored here\n getClustersByEntity(\n entity: Entity,\n requestContext: ServiceLocatorRequestContext,\n ): Promise<{ clusters: ClusterDetails[] }> {\n if (\n entity.relations &&\n entity.relations.some(\n r => r.type === 'dependsOn' && r.targetRef.includes('resource:'),\n )\n ) {\n return this.clusterSupplier\n .getClusters({ credentials: requestContext.credentials })\n .then(clusters => {\n return {\n clusters: clusters.filter(c =>\n this.doesEntityDependOnCluster(entity, c),\n ),\n };\n });\n }\n return Promise.resolve({ clusters: [] });\n }\n\n protected doesEntityDependOnCluster(\n entity: Entity,\n cluster: ClusterDetails,\n ): boolean {\n return entity.relations!.some(\n rel =>\n rel.type === 'dependsOn' &&\n rel.targetRef ===\n `resource:${entity.metadata.namespace ?? 'default'}/${cluster.name}`,\n );\n }\n}\n"],"names":[],"mappings":";;AAyBO,MAAM,6BAAA,CAAkE;AAAA,EAC5D,eAAA;AAAA,EAEjB,YAAY,eAAA,EAA6C;AACvD,IAAA,IAAA,CAAK,eAAA,GAAkB,eAAA;AAAA,EACzB;AAAA;AAAA,EAGA,mBAAA,CACE,QACA,cAAA,EACyC;AACzC,IAAA,IACE,MAAA,CAAO,SAAA,IACP,MAAA,CAAO,SAAA,CAAU,IAAA;AAAA,MACf,OAAK,CAAA,CAAE,IAAA,KAAS,eAAe,CAAA,CAAE,SAAA,CAAU,SAAS,WAAW;AAAA,KACjE,EACA;AACA,MAAA,OAAO,IAAA,CAAK,eAAA,CACT,WAAA,CAAY,EAAE,WAAA,EAAa,eAAe,WAAA,EAAa,CAAA,CACvD,IAAA,CAAK,CAAA,QAAA,KAAY;AAChB,QAAA,OAAO;AAAA,UACL,UAAU,QAAA,CAAS,MAAA;AAAA,YAAO,CAAA,CAAA,KACxB,IAAA,CAAK,yBAAA,CAA0B,MAAA,EAAQ,CAAC;AAAA;AAC1C,SACF;AAAA,MACF,CAAC,CAAA;AAAA,IACL;AACA,IAAA,OAAO,QAAQ,OAAA,CAAQ,EAAE,QAAA,EAAU,IAAI,CAAA;AAAA,EACzC;AAAA,EAEU,yBAAA,CACR,QACA,OAAA,EACS;AACT,IAAA,OAAO,OAAO,SAAA,CAAW,IAAA;AAAA,MACvB,CAAA,GAAA,KACE,GAAA,CAAI,IAAA,KAAS,WAAA,IACb,GAAA,CAAI,SAAA,KACF,CAAA,SAAA,EAAY,MAAA,CAAO,QAAA,CAAS,SAAA,IAAa,SAAS,CAAA,CAAA,EAAI,QAAQ,IAAI,CAAA;AAAA,KACxE;AAAA,EACF;AACF;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultiTenantServiceLocator.cjs.js","sources":["../../src/service-locator/MultiTenantServiceLocator.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport {\n ClusterDetails,\n KubernetesClustersSupplier,\n KubernetesServiceLocator,\n ServiceLocatorRequestContext,\n} from '@backstage/plugin-kubernetes-node';\n\n// This locator assumes that every service is located on every cluster\n// Therefore it will always return all clusters provided\nexport class MultiTenantServiceLocator implements KubernetesServiceLocator {\n private readonly clusterSupplier: KubernetesClustersSupplier;\n\n constructor(clusterSupplier: KubernetesClustersSupplier) {\n this.clusterSupplier = clusterSupplier;\n }\n\n // As this implementation always returns all clusters serviceId is ignored here\n getClustersByEntity(\n _entity: Entity,\n requestContext: ServiceLocatorRequestContext,\n ): Promise<{ clusters: ClusterDetails[] }> {\n return this.clusterSupplier\n .getClusters({ credentials: requestContext.credentials })\n .then(clusters => ({ clusters }));\n }\n}\n"],"names":[],"mappings":";;AA0BO,MAAM,
|
|
1
|
+
{"version":3,"file":"MultiTenantServiceLocator.cjs.js","sources":["../../src/service-locator/MultiTenantServiceLocator.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport {\n ClusterDetails,\n KubernetesClustersSupplier,\n KubernetesServiceLocator,\n ServiceLocatorRequestContext,\n} from '@backstage/plugin-kubernetes-node';\n\n// This locator assumes that every service is located on every cluster\n// Therefore it will always return all clusters provided\nexport class MultiTenantServiceLocator implements KubernetesServiceLocator {\n private readonly clusterSupplier: KubernetesClustersSupplier;\n\n constructor(clusterSupplier: KubernetesClustersSupplier) {\n this.clusterSupplier = clusterSupplier;\n }\n\n // As this implementation always returns all clusters serviceId is ignored here\n getClustersByEntity(\n _entity: Entity,\n requestContext: ServiceLocatorRequestContext,\n ): Promise<{ clusters: ClusterDetails[] }> {\n return this.clusterSupplier\n .getClusters({ credentials: requestContext.credentials })\n .then(clusters => ({ clusters }));\n }\n}\n"],"names":[],"mappings":";;AA0BO,MAAM,yBAAA,CAA8D;AAAA,EACxD,eAAA;AAAA,EAEjB,YAAY,eAAA,EAA6C;AACvD,IAAA,IAAA,CAAK,eAAA,GAAkB,eAAA;AAAA,EACzB;AAAA;AAAA,EAGA,mBAAA,CACE,SACA,cAAA,EACyC;AACzC,IAAA,OAAO,IAAA,CAAK,eAAA,CACT,WAAA,CAAY,EAAE,WAAA,EAAa,cAAA,CAAe,WAAA,EAAa,CAAA,CACvD,IAAA,CAAK,CAAA,QAAA,MAAa,EAAE,UAAS,CAAE,CAAA;AAAA,EACpC;AACF;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SingleTenantServiceLocator.cjs.js","sources":["../../src/service-locator/SingleTenantServiceLocator.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity } from '@backstage/catalog-model';\nimport {\n ClusterDetails,\n KubernetesClustersSupplier,\n KubernetesServiceLocator,\n ServiceLocatorRequestContext,\n} from '@backstage/plugin-kubernetes-node';\n\n// This locator assumes that service is located on one cluster\n// Therefore it will always return specified cluster provided in backstage.io/kubernetes-cluster annotation\n// If backstage.io/kubernetes-cluster annotation not provided will always return all cluster provided\nexport class SingleTenantServiceLocator implements KubernetesServiceLocator {\n private readonly clusterSupplier: KubernetesClustersSupplier;\n\n constructor(clusterSupplier: KubernetesClustersSupplier) {\n this.clusterSupplier = clusterSupplier;\n }\n\n // As this implementation always returns all clusters serviceId is ignored here\n getClustersByEntity(\n _entity: Entity,\n requestContext: ServiceLocatorRequestContext,\n ): Promise<{ clusters: ClusterDetails[] }> {\n return this.clusterSupplier\n .getClusters({ credentials: requestContext.credentials })\n .then(clusters => {\n if (\n _entity.metadata?.annotations?.['backstage.io/kubernetes-cluster']\n ) {\n return {\n clusters: clusters.filter(\n c =>\n c.name ===\n _entity.metadata?.annotations?.[\n 'backstage.io/kubernetes-cluster'\n ],\n ),\n };\n }\n return { clusters };\n });\n }\n}\n"],"names":[],"mappings":";;AA0BO,MAAM,
|
|
1
|
+
{"version":3,"file":"SingleTenantServiceLocator.cjs.js","sources":["../../src/service-locator/SingleTenantServiceLocator.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity } from '@backstage/catalog-model';\nimport {\n ClusterDetails,\n KubernetesClustersSupplier,\n KubernetesServiceLocator,\n ServiceLocatorRequestContext,\n} from '@backstage/plugin-kubernetes-node';\n\n// This locator assumes that service is located on one cluster\n// Therefore it will always return specified cluster provided in backstage.io/kubernetes-cluster annotation\n// If backstage.io/kubernetes-cluster annotation not provided will always return all cluster provided\nexport class SingleTenantServiceLocator implements KubernetesServiceLocator {\n private readonly clusterSupplier: KubernetesClustersSupplier;\n\n constructor(clusterSupplier: KubernetesClustersSupplier) {\n this.clusterSupplier = clusterSupplier;\n }\n\n // As this implementation always returns all clusters serviceId is ignored here\n getClustersByEntity(\n _entity: Entity,\n requestContext: ServiceLocatorRequestContext,\n ): Promise<{ clusters: ClusterDetails[] }> {\n return this.clusterSupplier\n .getClusters({ credentials: requestContext.credentials })\n .then(clusters => {\n if (\n _entity.metadata?.annotations?.['backstage.io/kubernetes-cluster']\n ) {\n return {\n clusters: clusters.filter(\n c =>\n c.name ===\n _entity.metadata?.annotations?.[\n 'backstage.io/kubernetes-cluster'\n ],\n ),\n };\n }\n return { clusters };\n });\n }\n}\n"],"names":[],"mappings":";;AA0BO,MAAM,0BAAA,CAA+D;AAAA,EACzD,eAAA;AAAA,EAEjB,YAAY,eAAA,EAA6C;AACvD,IAAA,IAAA,CAAK,eAAA,GAAkB,eAAA;AAAA,EACzB;AAAA;AAAA,EAGA,mBAAA,CACE,SACA,cAAA,EACyC;AACzC,IAAA,OAAO,IAAA,CAAK,eAAA,CACT,WAAA,CAAY,EAAE,WAAA,EAAa,eAAe,WAAA,EAAa,CAAA,CACvD,IAAA,CAAK,CAAA,QAAA,KAAY;AAChB,MAAA,IACE,OAAA,CAAQ,QAAA,EAAU,WAAA,GAAc,iCAAiC,CAAA,EACjE;AACA,QAAA,OAAO;AAAA,UACL,UAAU,QAAA,CAAS,MAAA;AAAA,YACjB,OACE,CAAA,CAAE,IAAA,KACF,OAAA,CAAQ,QAAA,EAAU,cAChB,iCACF;AAAA;AACJ,SACF;AAAA,MACF;AACA,MAAA,OAAO,EAAE,QAAA,EAAS;AAAA,IACpB,CAAC,CAAA;AAAA,EACL;AACF;;;;"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var MultiTenantServiceLocator = require('./MultiTenantServiceLocator.cjs.js');
|
|
4
|
+
var SingleTenantServiceLocator = require('./SingleTenantServiceLocator.cjs.js');
|
|
5
|
+
var CatalogRelationServiceLocator = require('./CatalogRelationServiceLocator.cjs.js');
|
|
6
|
+
|
|
7
|
+
const buildDefaultServiceLocator = ({
|
|
8
|
+
config,
|
|
9
|
+
clusterSupplier
|
|
10
|
+
}) => {
|
|
11
|
+
const method = config.getString(
|
|
12
|
+
"kubernetes.serviceLocatorMethod.type"
|
|
13
|
+
);
|
|
14
|
+
switch (method) {
|
|
15
|
+
case "multiTenant":
|
|
16
|
+
return new MultiTenantServiceLocator.MultiTenantServiceLocator(clusterSupplier);
|
|
17
|
+
case "singleTenant":
|
|
18
|
+
return new SingleTenantServiceLocator.SingleTenantServiceLocator(clusterSupplier);
|
|
19
|
+
case "catalogRelation":
|
|
20
|
+
return new CatalogRelationServiceLocator.CatalogRelationServiceLocator(clusterSupplier);
|
|
21
|
+
case "http":
|
|
22
|
+
throw new Error("not implemented");
|
|
23
|
+
default:
|
|
24
|
+
throw new Error(
|
|
25
|
+
`Unsupported kubernetes.serviceLocatorMethod "${method}"`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
exports.buildDefaultServiceLocator = buildDefaultServiceLocator;
|
|
31
|
+
//# sourceMappingURL=buildDefaultServiceLocator.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildDefaultServiceLocator.cjs.js","sources":["../../src/service-locator/buildDefaultServiceLocator.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { RootConfigService } from '@backstage/backend-plugin-api';\nimport { ServiceLocatorMethod } from '../types';\nimport { KubernetesClustersSupplier } from '@backstage/plugin-kubernetes-node';\nimport { MultiTenantServiceLocator } from './MultiTenantServiceLocator';\nimport { SingleTenantServiceLocator } from './SingleTenantServiceLocator';\nimport { CatalogRelationServiceLocator } from './CatalogRelationServiceLocator';\n\nexport const buildDefaultServiceLocator = ({\n config,\n clusterSupplier,\n}: {\n config: RootConfigService;\n clusterSupplier: KubernetesClustersSupplier;\n}) => {\n const method = config.getString(\n 'kubernetes.serviceLocatorMethod.type',\n ) as ServiceLocatorMethod;\n\n switch (method) {\n case 'multiTenant':\n return new MultiTenantServiceLocator(clusterSupplier);\n case 'singleTenant':\n return new SingleTenantServiceLocator(clusterSupplier);\n case 'catalogRelation':\n return new CatalogRelationServiceLocator(clusterSupplier);\n case 'http':\n throw new Error('not implemented');\n default:\n throw new Error(\n `Unsupported kubernetes.serviceLocatorMethod \"${method}\"`,\n );\n }\n};\n"],"names":["MultiTenantServiceLocator","SingleTenantServiceLocator","CatalogRelationServiceLocator"],"mappings":";;;;;;AAsBO,MAAM,6BAA6B,CAAC;AAAA,EACzC,MAAA;AAAA,EACA;AACF,CAAA,KAGM;AACJ,EAAA,MAAM,SAAS,MAAA,CAAO,SAAA;AAAA,IACpB;AAAA,GACF;AAEA,EAAA,QAAQ,MAAA;AAAQ,IACd,KAAK,aAAA;AACH,MAAA,OAAO,IAAIA,oDAA0B,eAAe,CAAA;AAAA,IACtD,KAAK,cAAA;AACH,MAAA,OAAO,IAAIC,sDAA2B,eAAe,CAAA;AAAA,IACvD,KAAK,iBAAA;AACH,MAAA,OAAO,IAAIC,4DAA8B,eAAe,CAAA;AAAA,IAC1D,KAAK,MAAA;AACH,MAAA,MAAM,IAAI,MAAM,iBAAiB,CAAA;AAAA,IACnC;AACE,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,gDAAgD,MAAM,CAAA,CAAA;AAAA,OACxD;AAAA;AAEN;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/plugin-kubernetes-backend",
|
|
3
|
-
"version": "0.20.0
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "A Backstage backend plugin that integrates towards Kubernetes",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "backend-plugin",
|
|
@@ -50,19 +50,19 @@
|
|
|
50
50
|
"@aws-sdk/credential-providers": "^3.350.0",
|
|
51
51
|
"@aws-sdk/signature-v4": "^3.347.0",
|
|
52
52
|
"@azure/identity": "^4.0.0",
|
|
53
|
-
"@backstage/backend-plugin-api": "1.4.2
|
|
54
|
-
"@backstage/catalog-client": "1.11.0
|
|
55
|
-
"@backstage/catalog-model": "1.7.5",
|
|
56
|
-
"@backstage/config": "1.3.3",
|
|
57
|
-
"@backstage/errors": "1.2.7",
|
|
58
|
-
"@backstage/integration-aws-node": "0.1.17",
|
|
59
|
-
"@backstage/plugin-auth-node": "0.6.6
|
|
60
|
-
"@backstage/plugin-catalog-node": "1.18.0
|
|
61
|
-
"@backstage/plugin-kubernetes-common": "0.9.6",
|
|
62
|
-
"@backstage/plugin-kubernetes-node": "0.3.3
|
|
63
|
-
"@backstage/plugin-permission-common": "0.9.1",
|
|
64
|
-
"@backstage/plugin-permission-node": "0.10.3
|
|
65
|
-
"@backstage/types": "1.2.1",
|
|
53
|
+
"@backstage/backend-plugin-api": "^1.4.2",
|
|
54
|
+
"@backstage/catalog-client": "^1.11.0",
|
|
55
|
+
"@backstage/catalog-model": "^1.7.5",
|
|
56
|
+
"@backstage/config": "^1.3.3",
|
|
57
|
+
"@backstage/errors": "^1.2.7",
|
|
58
|
+
"@backstage/integration-aws-node": "^0.1.17",
|
|
59
|
+
"@backstage/plugin-auth-node": "^0.6.6",
|
|
60
|
+
"@backstage/plugin-catalog-node": "^1.18.0",
|
|
61
|
+
"@backstage/plugin-kubernetes-common": "^0.9.6",
|
|
62
|
+
"@backstage/plugin-kubernetes-node": "^0.3.3",
|
|
63
|
+
"@backstage/plugin-permission-common": "^0.9.1",
|
|
64
|
+
"@backstage/plugin-permission-node": "^0.10.3",
|
|
65
|
+
"@backstage/types": "^1.2.1",
|
|
66
66
|
"@google-cloud/container": "^5.0.0",
|
|
67
67
|
"@jest-mock/express": "^2.0.1",
|
|
68
68
|
"@kubernetes/client-node": "1.1.2",
|
|
@@ -83,12 +83,12 @@
|
|
|
83
83
|
"yn": "^4.0.0"
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
|
-
"@backstage/backend-app-api": "1.2.6
|
|
87
|
-
"@backstage/backend-defaults": "0.
|
|
88
|
-
"@backstage/backend-test-utils": "1.
|
|
89
|
-
"@backstage/cli": "0.34.0
|
|
90
|
-
"@backstage/plugin-permission-backend": "0.7.3
|
|
91
|
-
"@backstage/plugin-permission-backend-module-allow-all-policy": "0.2.11
|
|
86
|
+
"@backstage/backend-app-api": "^1.2.6",
|
|
87
|
+
"@backstage/backend-defaults": "^0.12.0",
|
|
88
|
+
"@backstage/backend-test-utils": "^1.8.0",
|
|
89
|
+
"@backstage/cli": "^0.34.0",
|
|
90
|
+
"@backstage/plugin-permission-backend": "^0.7.3",
|
|
91
|
+
"@backstage/plugin-permission-backend-module-allow-all-policy": "^0.2.11",
|
|
92
92
|
"@types/aws4": "^1.5.1",
|
|
93
93
|
"@types/express": "^4.17.6",
|
|
94
94
|
"@types/luxon": "^3.0.0",
|