@backstage/plugin-kubernetes-backend 0.21.10 → 0.21.11-next.2

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 CHANGED
@@ -1,25 +1,44 @@
1
1
  # @backstage/plugin-kubernetes-backend
2
2
 
3
- ## 0.21.10
3
+ ## 0.21.11-next.2
4
4
 
5
5
  ### Patch Changes
6
6
 
7
- - c14f8be: Reject unsupported service account authentication in catalog-provided cluster definitions.
8
- - 050a4e1: Improved handling of Kubernetes resource response data.
7
+ - 5268d63: Migrated tests from MSW v1 to MSW v2.
8
+ - 97a7e8e: The catalog cluster locator now validates Kubernetes API server URLs to block SSRF targets (non-public addresses, cloud metadata endpoints, and non-HTTPS URLs by default). Operators may list trusted hostnames in `dangerouslyAllowClusterUrls` on the catalog locator method to permit HTTP or non-public addresses for those hosts only (for example local minikube). Catalog entities cannot use the `serviceAccount` auth provider, cannot enable TLS verification skipping unless `dangerouslyAllowSkipTLSVerify` is set on the locator method, and only permitted annotations are passed through as auth metadata. Kubernetes API fetches no longer follow HTTP redirects automatically.
9
+ - Updated dependencies
10
+ - @backstage/config@1.3.9-next.0
11
+ - @backstage/plugin-kubernetes-node@0.4.8-next.2
12
+ - @backstage/catalog-client@1.16.2-next.1
13
+ - @backstage/plugin-catalog-node@2.2.5-next.1
14
+ - @backstage/backend-plugin-api@1.10.1-next.1
15
+ - @backstage/integration-aws-node@0.2.2-next.0
16
+ - @backstage/plugin-permission-common@0.9.11-next.0
17
+ - @backstage/plugin-permission-node@0.11.4-next.1
18
+ - @backstage/plugin-kubernetes-common@0.9.13-next.1
9
19
 
10
- ## 0.21.9
20
+ ## 0.21.11-next.1
11
21
 
12
22
  ### Patch Changes
13
23
 
14
- - 89d4c81: The catalog cluster locator now validates Kubernetes API server URLs to block SSRF targets (non-public addresses, cloud metadata endpoints, and non-HTTPS URLs by default). Operators may list trusted hostnames in `dangerouslyAllowClusterUrls` on the catalog locator method to permit HTTP or non-public addresses for those hosts only (for example local minikube). Catalog entities cannot use the `serviceAccount` auth provider, cannot enable TLS verification skipping unless `dangerouslyAllowSkipTLSVerify` is set on the locator method, and only permitted annotations are passed through as auth metadata. Kubernetes API fetches no longer follow HTTP redirects automatically.
15
-
16
- Kubernetes Secret redaction on resource query endpoints now masks both `data` and `stringData` values regardless of how Secrets are fetched, including via the custom resources query path.
24
+ - 1d7b704: Reject unsupported service account authentication in catalog-provided cluster definitions.
25
+ - 28bdf75: Improved handling of Kubernetes resource response data.
26
+ - Updated dependencies
27
+ - @backstage/plugin-kubernetes-node@0.4.8-next.1
28
+ - @backstage/plugin-permission-node@0.11.4-next.0
29
+ - @backstage/catalog-client@1.16.2-next.0
30
+ - @backstage/backend-plugin-api@1.10.1-next.0
31
+ - @backstage/plugin-catalog-node@2.2.5-next.0
17
32
 
18
- ## 0.21.8
33
+ ## 0.21.10-next.0
19
34
 
20
35
  ### Patch Changes
21
36
 
22
- - 9d950fa: Improved entity resolution on the deprecated services endpoint.
37
+ - b11c9b4: Improved entity resolution on the deprecated services endpoint.
38
+ - d9a57de: Add `KubernetesWatcher` interface for streaming Kubernetes resource changes via an async iterator. The watcher is separated from `KubernetesFetcher` because watching is a long-lived streaming connection that only works with server-side auth providers. Watch supports all event types (ADDED, MODIFIED, DELETED, BOOKMARK, ERROR) with errors yielded as data rather than thrown.
39
+ - Updated dependencies
40
+ - @backstage/plugin-kubernetes-common@0.9.13-next.0
41
+ - @backstage/plugin-kubernetes-node@0.4.8-next.0
23
42
 
24
43
  ## 0.21.7
25
44
 
@@ -67,9 +67,10 @@ class CatalogClusterLocator {
67
67
  }
68
68
  const authProvider = annotations[pluginKubernetesCommon.ANNOTATION_KUBERNETES_AUTH_PROVIDER];
69
69
  if (authProvider === "serviceAccount") {
70
- throw new Error(
71
- `Invalid cluster '${name}': authProvider 'serviceAccount' is not supported by the catalog cluster locator`
70
+ this.logger.warn(
71
+ `Ignoring kubernetes-cluster Resource "${name}": catalog cluster locator does not support the serviceAccount auth provider`
72
72
  );
73
+ return void 0;
73
74
  }
74
75
  const apiServerUrl = annotations[pluginKubernetesCommon.ANNOTATION_KUBERNETES_API_SERVER];
75
76
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"CatalogClusterLocator.cjs.js","sources":["../../src/cluster-locator/CatalogClusterLocator.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 */\n\nimport {\n AuthService,\n BackstageCredentials,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport {\n ClusterDetails,\n KubernetesClustersSupplier,\n} from '@backstage/plugin-kubernetes-node';\nimport { CATALOG_FILTER_EXISTS } from '@backstage/catalog-client';\nimport {\n ANNOTATION_KUBERNETES_API_SERVER,\n ANNOTATION_KUBERNETES_API_SERVER_CA,\n ANNOTATION_KUBERNETES_AUTH_PROVIDER,\n ANNOTATION_KUBERNETES_SKIP_METRICS_LOOKUP,\n ANNOTATION_KUBERNETES_SKIP_TLS_VERIFY,\n ANNOTATION_KUBERNETES_DASHBOARD_URL,\n ANNOTATION_KUBERNETES_DASHBOARD_APP,\n ANNOTATION_KUBERNETES_DASHBOARD_PARAMETERS,\n} from '@backstage/plugin-kubernetes-common';\nimport { JsonObject } from '@backstage/types';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { filterCatalogClusterAuthMetadata } from './catalogClusterAuthMetadata';\nimport {\n CatalogClusterLocatorOptions,\n readCatalogClusterLocatorOptions,\n} from './CatalogClusterLocatorOptions';\nimport { validateClusterApiServerUrl } from './validateClusterApiServerUrl';\n\nfunction isObject(obj: unknown): obj is JsonObject {\n return typeof obj === 'object' && obj !== null && !Array.isArray(obj);\n}\n\nexport class CatalogClusterLocator implements KubernetesClustersSupplier {\n private catalogService: CatalogService;\n private auth: AuthService;\n private readonly options: CatalogClusterLocatorOptions;\n private readonly logger: LoggerService;\n\n constructor(\n catalogService: CatalogService,\n auth: AuthService,\n options: CatalogClusterLocatorOptions,\n logger: LoggerService,\n ) {\n this.catalogService = catalogService;\n this.auth = auth;\n this.options = options;\n this.logger = logger;\n }\n\n static fromConfig(\n catalogApi: CatalogService,\n auth: AuthService,\n clusterLocatorConfig: Config,\n logger: LoggerService,\n ): CatalogClusterLocator {\n return new CatalogClusterLocator(\n catalogApi,\n auth,\n readCatalogClusterLocatorOptions(clusterLocatorConfig),\n logger,\n );\n }\n\n async getClusters(options?: {\n credentials: BackstageCredentials;\n }): Promise<ClusterDetails[]> {\n const apiServerKey = `metadata.annotations.${ANNOTATION_KUBERNETES_API_SERVER}`;\n const apiServerCaKey = `metadata.annotations.${ANNOTATION_KUBERNETES_API_SERVER_CA}`;\n const authProviderKey = `metadata.annotations.${ANNOTATION_KUBERNETES_AUTH_PROVIDER}`;\n\n const filter: Record<string, symbol | string> = {\n kind: 'Resource',\n 'spec.type': 'kubernetes-cluster',\n [apiServerKey]: CATALOG_FILTER_EXISTS,\n [apiServerCaKey]: CATALOG_FILTER_EXISTS,\n [authProviderKey]: CATALOG_FILTER_EXISTS,\n };\n\n const clusters = await this.catalogService.getEntities(\n {\n filter: [filter],\n },\n {\n credentials:\n options?.credentials ?? (await this.auth.getNoneCredentials()),\n },\n );\n\n const clusterDetails: ClusterDetails[] = [];\n for (const entity of clusters.items) {\n const details = await this.toClusterDetails(entity);\n if (details) {\n clusterDetails.push(details);\n }\n }\n return clusterDetails;\n }\n\n private async toClusterDetails(\n entity: Awaited<ReturnType<CatalogService['getEntities']>>['items'][number],\n ): Promise<ClusterDetails | undefined> {\n const name = entity.metadata.name;\n const annotations = entity.metadata.annotations;\n if (!annotations) {\n this.logger.warn(\n `Ignoring kubernetes-cluster Resource \"${name}\" without annotations`,\n );\n return undefined;\n }\n\n const authProvider = annotations[ANNOTATION_KUBERNETES_AUTH_PROVIDER];\n if (authProvider === 'serviceAccount') {\n throw new Error(\n `Invalid cluster '${name}': authProvider 'serviceAccount' is not supported by the catalog cluster locator`,\n );\n }\n\n const apiServerUrl = annotations[ANNOTATION_KUBERNETES_API_SERVER];\n try {\n await validateClusterApiServerUrl(apiServerUrl, this.options);\n } catch (error) {\n this.logger.warn(\n `Ignoring kubernetes-cluster Resource \"${name}\": ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n return undefined;\n }\n\n const allowSkipTlsVerify =\n this.options.dangerouslyAllowSkipTLSVerify ?? false;\n const skipTLSVerify =\n allowSkipTlsVerify &&\n annotations[ANNOTATION_KUBERNETES_SKIP_TLS_VERIFY] === 'true';\n\n const clusterDetails: ClusterDetails = {\n name,\n title: entity.metadata.title,\n url: apiServerUrl,\n authMetadata: filterCatalogClusterAuthMetadata(annotations),\n caData: annotations[ANNOTATION_KUBERNETES_API_SERVER_CA],\n skipMetricsLookup:\n annotations[ANNOTATION_KUBERNETES_SKIP_METRICS_LOOKUP] === 'true',\n skipTLSVerify,\n dashboardUrl: annotations[ANNOTATION_KUBERNETES_DASHBOARD_URL],\n dashboardApp: annotations[ANNOTATION_KUBERNETES_DASHBOARD_APP],\n dashboardParameters: this.getDashboardParameters(annotations),\n };\n\n return clusterDetails;\n }\n\n private getDashboardParameters(\n annotations: Record<string, string>,\n ): JsonObject | undefined {\n const dashboardParamsString =\n annotations[ANNOTATION_KUBERNETES_DASHBOARD_PARAMETERS];\n if (dashboardParamsString) {\n try {\n const dashboardParams = JSON.parse(dashboardParamsString);\n return isObject(dashboardParams) ? dashboardParams : undefined;\n } catch {\n return undefined;\n }\n }\n return undefined;\n }\n}\n"],"names":["readCatalogClusterLocatorOptions","ANNOTATION_KUBERNETES_API_SERVER","ANNOTATION_KUBERNETES_API_SERVER_CA","ANNOTATION_KUBERNETES_AUTH_PROVIDER","CATALOG_FILTER_EXISTS","validateClusterApiServerUrl","ANNOTATION_KUBERNETES_SKIP_TLS_VERIFY","filterCatalogClusterAuthMetadata","ANNOTATION_KUBERNETES_SKIP_METRICS_LOOKUP","ANNOTATION_KUBERNETES_DASHBOARD_URL","ANNOTATION_KUBERNETES_DASHBOARD_APP","ANNOTATION_KUBERNETES_DASHBOARD_PARAMETERS"],"mappings":"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"}
1
+ {"version":3,"file":"CatalogClusterLocator.cjs.js","sources":["../../src/cluster-locator/CatalogClusterLocator.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 */\n\nimport {\n AuthService,\n BackstageCredentials,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport {\n ClusterDetails,\n KubernetesClustersSupplier,\n} from '@backstage/plugin-kubernetes-node';\nimport { CATALOG_FILTER_EXISTS } from '@backstage/catalog-client';\nimport {\n ANNOTATION_KUBERNETES_API_SERVER,\n ANNOTATION_KUBERNETES_API_SERVER_CA,\n ANNOTATION_KUBERNETES_AUTH_PROVIDER,\n ANNOTATION_KUBERNETES_SKIP_METRICS_LOOKUP,\n ANNOTATION_KUBERNETES_SKIP_TLS_VERIFY,\n ANNOTATION_KUBERNETES_DASHBOARD_URL,\n ANNOTATION_KUBERNETES_DASHBOARD_APP,\n ANNOTATION_KUBERNETES_DASHBOARD_PARAMETERS,\n} from '@backstage/plugin-kubernetes-common';\nimport { JsonObject } from '@backstage/types';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { filterCatalogClusterAuthMetadata } from './catalogClusterAuthMetadata';\nimport {\n CatalogClusterLocatorOptions,\n readCatalogClusterLocatorOptions,\n} from './CatalogClusterLocatorOptions';\nimport { validateClusterApiServerUrl } from './validateClusterApiServerUrl';\n\nfunction isObject(obj: unknown): obj is JsonObject {\n return typeof obj === 'object' && obj !== null && !Array.isArray(obj);\n}\n\nexport class CatalogClusterLocator implements KubernetesClustersSupplier {\n private catalogService: CatalogService;\n private auth: AuthService;\n private readonly options: CatalogClusterLocatorOptions;\n private readonly logger: LoggerService;\n\n constructor(\n catalogService: CatalogService,\n auth: AuthService,\n options: CatalogClusterLocatorOptions,\n logger: LoggerService,\n ) {\n this.catalogService = catalogService;\n this.auth = auth;\n this.options = options;\n this.logger = logger;\n }\n\n static fromConfig(\n catalogApi: CatalogService,\n auth: AuthService,\n clusterLocatorConfig: Config,\n logger: LoggerService,\n ): CatalogClusterLocator {\n return new CatalogClusterLocator(\n catalogApi,\n auth,\n readCatalogClusterLocatorOptions(clusterLocatorConfig),\n logger,\n );\n }\n\n async getClusters(options?: {\n credentials: BackstageCredentials;\n }): Promise<ClusterDetails[]> {\n const apiServerKey = `metadata.annotations.${ANNOTATION_KUBERNETES_API_SERVER}`;\n const apiServerCaKey = `metadata.annotations.${ANNOTATION_KUBERNETES_API_SERVER_CA}`;\n const authProviderKey = `metadata.annotations.${ANNOTATION_KUBERNETES_AUTH_PROVIDER}`;\n\n const filter: Record<string, symbol | string> = {\n kind: 'Resource',\n 'spec.type': 'kubernetes-cluster',\n [apiServerKey]: CATALOG_FILTER_EXISTS,\n [apiServerCaKey]: CATALOG_FILTER_EXISTS,\n [authProviderKey]: CATALOG_FILTER_EXISTS,\n };\n\n const clusters = await this.catalogService.getEntities(\n {\n filter: [filter],\n },\n {\n credentials:\n options?.credentials ?? (await this.auth.getNoneCredentials()),\n },\n );\n\n const clusterDetails: ClusterDetails[] = [];\n for (const entity of clusters.items) {\n const details = await this.toClusterDetails(entity);\n if (details) {\n clusterDetails.push(details);\n }\n }\n return clusterDetails;\n }\n\n private async toClusterDetails(\n entity: Awaited<ReturnType<CatalogService['getEntities']>>['items'][number],\n ): Promise<ClusterDetails | undefined> {\n const name = entity.metadata.name;\n const annotations = entity.metadata.annotations;\n if (!annotations) {\n this.logger.warn(\n `Ignoring kubernetes-cluster Resource \"${name}\" without annotations`,\n );\n return undefined;\n }\n\n const authProvider = annotations[ANNOTATION_KUBERNETES_AUTH_PROVIDER];\n if (authProvider === 'serviceAccount') {\n this.logger.warn(\n `Ignoring kubernetes-cluster Resource \"${name}\": catalog cluster locator does not support the serviceAccount auth provider`,\n );\n return undefined;\n }\n\n const apiServerUrl = annotations[ANNOTATION_KUBERNETES_API_SERVER];\n try {\n await validateClusterApiServerUrl(apiServerUrl, this.options);\n } catch (error) {\n this.logger.warn(\n `Ignoring kubernetes-cluster Resource \"${name}\": ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n return undefined;\n }\n\n const allowSkipTlsVerify =\n this.options.dangerouslyAllowSkipTLSVerify ?? false;\n const skipTLSVerify =\n allowSkipTlsVerify &&\n annotations[ANNOTATION_KUBERNETES_SKIP_TLS_VERIFY] === 'true';\n\n const clusterDetails: ClusterDetails = {\n name,\n title: entity.metadata.title,\n url: apiServerUrl,\n authMetadata: filterCatalogClusterAuthMetadata(annotations),\n caData: annotations[ANNOTATION_KUBERNETES_API_SERVER_CA],\n skipMetricsLookup:\n annotations[ANNOTATION_KUBERNETES_SKIP_METRICS_LOOKUP] === 'true',\n skipTLSVerify,\n dashboardUrl: annotations[ANNOTATION_KUBERNETES_DASHBOARD_URL],\n dashboardApp: annotations[ANNOTATION_KUBERNETES_DASHBOARD_APP],\n dashboardParameters: this.getDashboardParameters(annotations),\n };\n\n return clusterDetails;\n }\n\n private getDashboardParameters(\n annotations: Record<string, string>,\n ): JsonObject | undefined {\n const dashboardParamsString =\n annotations[ANNOTATION_KUBERNETES_DASHBOARD_PARAMETERS];\n if (dashboardParamsString) {\n try {\n const dashboardParams = JSON.parse(dashboardParamsString);\n return isObject(dashboardParams) ? dashboardParams : undefined;\n } catch {\n return undefined;\n }\n }\n return undefined;\n }\n}\n"],"names":["readCatalogClusterLocatorOptions","ANNOTATION_KUBERNETES_API_SERVER","ANNOTATION_KUBERNETES_API_SERVER_CA","ANNOTATION_KUBERNETES_AUTH_PROVIDER","CATALOG_FILTER_EXISTS","validateClusterApiServerUrl","ANNOTATION_KUBERNETES_SKIP_TLS_VERIFY","filterCatalogClusterAuthMetadata","ANNOTATION_KUBERNETES_SKIP_METRICS_LOOKUP","ANNOTATION_KUBERNETES_DASHBOARD_URL","ANNOTATION_KUBERNETES_DASHBOARD_APP","ANNOTATION_KUBERNETES_DASHBOARD_PARAMETERS"],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var version = "0.21.10";
5
+ var version = "0.21.11-next.2";
6
6
  var packageinfo = {
7
7
  version: version};
8
8
 
@@ -0,0 +1,188 @@
1
+ 'use strict';
2
+
3
+ var pluginKubernetesCommon = require('@backstage/plugin-kubernetes-common');
4
+ var fetch = require('node-fetch');
5
+ var https = require('node:https');
6
+ var fs = require('fs-extra');
7
+
8
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
9
+
10
+ function _interopNamespaceCompat(e) {
11
+ if (e && typeof e === 'object' && 'default' in e) return e;
12
+ var n = Object.create(null);
13
+ if (e) {
14
+ Object.keys(e).forEach(function (k) {
15
+ if (k !== 'default') {
16
+ var d = Object.getOwnPropertyDescriptor(e, k);
17
+ Object.defineProperty(n, k, d.get ? d : {
18
+ enumerable: true,
19
+ get: function () { return e[k]; }
20
+ });
21
+ }
22
+ });
23
+ }
24
+ n.default = e;
25
+ return Object.freeze(n);
26
+ }
27
+
28
+ var fetch__default = /*#__PURE__*/_interopDefaultCompat(fetch);
29
+ var https__namespace = /*#__PURE__*/_interopNamespaceCompat(https);
30
+ var fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
31
+
32
+ const statusCodeToErrorType = (statusCode) => {
33
+ switch (statusCode) {
34
+ case 400:
35
+ return "BAD_REQUEST";
36
+ case 401:
37
+ return "UNAUTHORIZED_ERROR";
38
+ case 404:
39
+ return "NOT_FOUND";
40
+ case 500:
41
+ return "SYSTEM_ERROR";
42
+ default:
43
+ return "UNKNOWN_ERROR";
44
+ }
45
+ };
46
+ class KubernetesConnection {
47
+ logger;
48
+ agentFactory;
49
+ agentCache = /* @__PURE__ */ new Map();
50
+ inClusterCache;
51
+ constructor({
52
+ logger,
53
+ agentFactory
54
+ }) {
55
+ this.logger = logger;
56
+ this.agentFactory = agentFactory ?? ((options) => new https__namespace.Agent(options));
57
+ }
58
+ buildResourcePath(group, apiVersion, plural, namespace) {
59
+ const encode = (s) => encodeURIComponent(s);
60
+ let path = group ? `/apis/${encode(group)}/${encode(apiVersion)}` : `/api/${encode(apiVersion)}`;
61
+ if (namespace) {
62
+ path += `/namespaces/${encode(namespace)}`;
63
+ }
64
+ path += `/${encode(plural)}`;
65
+ return path;
66
+ }
67
+ async resolveConnection(clusterDetails, credential) {
68
+ const authProvider = clusterDetails.authMetadata[pluginKubernetesCommon.ANNOTATION_KUBERNETES_AUTH_PROVIDER];
69
+ if (this.isServiceAccountAuthentication(authProvider, clusterDetails)) {
70
+ const [url, requestInit] = await this.fetchArgsInCluster(credential);
71
+ return { ok: true, url, requestInit };
72
+ } else if (!this.isCredentialMissing(authProvider, credential)) {
73
+ const [url, requestInit] = await this.fetchArgs(
74
+ clusterDetails,
75
+ credential
76
+ );
77
+ return { ok: true, url, requestInit };
78
+ }
79
+ return { ok: false, reason: "missing_credentials" };
80
+ }
81
+ async fetchWithConnection(clusterDetails, credential, resourcePath, labelSelector) {
82
+ const result = await this.resolveConnection(clusterDetails, credential);
83
+ if (!result.ok) {
84
+ return Promise.reject(
85
+ new Error(
86
+ `no bearer token or client cert for cluster '${clusterDetails.name}' and not running in Kubernetes`
87
+ )
88
+ );
89
+ }
90
+ const { url, requestInit } = result;
91
+ if (url.pathname === "/") {
92
+ url.pathname = resourcePath;
93
+ } else {
94
+ url.pathname += resourcePath;
95
+ }
96
+ if (labelSelector) {
97
+ url.search = `labelSelector=${encodeURIComponent(labelSelector)}`;
98
+ }
99
+ return fetch__default.default(url, requestInit);
100
+ }
101
+ async handleUnsuccessfulResponse(clusterName, res) {
102
+ const resourcePath = new URL(res.url).pathname;
103
+ this.logger.warn(
104
+ `Received ${res.status} status when fetching "${resourcePath}" from cluster "${clusterName}"; body=[${await res.text()}]`
105
+ );
106
+ return {
107
+ errorType: statusCodeToErrorType(res.status),
108
+ statusCode: res.status,
109
+ resourcePath
110
+ };
111
+ }
112
+ isServiceAccountAuthentication(authProvider, clusterDetails) {
113
+ return authProvider === "serviceAccount" && !clusterDetails.authMetadata.serviceAccountToken && fs__default.default.pathExistsSync(pluginKubernetesCommon.SERVICEACCOUNT_CA_PATH);
114
+ }
115
+ isCredentialMissing(authProvider, credential) {
116
+ return authProvider !== "localKubectlProxy" && credential.type === "anonymous";
117
+ }
118
+ buildRequestHeaders(credential) {
119
+ return {
120
+ Accept: "application/json",
121
+ "Content-Type": "application/json",
122
+ ...credential.type === "bearer token" && {
123
+ Authorization: `Bearer ${credential.token}`
124
+ }
125
+ };
126
+ }
127
+ async fetchArgs(clusterDetails, credential) {
128
+ const { bufferFromFileOrString } = await import('@kubernetes/client-node');
129
+ const requestInit = {
130
+ method: "GET",
131
+ headers: this.buildRequestHeaders(credential),
132
+ redirect: "manual"
133
+ };
134
+ const url = new URL(clusterDetails.url);
135
+ if (url.protocol === "https:") {
136
+ const ca = bufferFromFileOrString(clusterDetails.caFile, clusterDetails.caData) ?? void 0;
137
+ requestInit.agent = this.getOrCreateAgent(clusterDetails, credential, ca);
138
+ }
139
+ return [url, requestInit];
140
+ }
141
+ async fetchArgsInCluster(credential) {
142
+ if (!this.inClusterCache) {
143
+ const { KubeConfig } = await import('@kubernetes/client-node');
144
+ const kc = new KubeConfig();
145
+ kc.loadFromCluster();
146
+ const cluster = kc.getCurrentCluster();
147
+ const url2 = new URL(cluster.server);
148
+ const agent2 = url2.protocol === "https:" ? this.agentFactory({
149
+ ca: fs__default.default.readFileSync(cluster.caFile),
150
+ keepAlive: true
151
+ }) : void 0;
152
+ this.inClusterCache = { url: url2, agent: agent2 };
153
+ }
154
+ const { url, agent } = this.inClusterCache;
155
+ const requestInit = {
156
+ method: "GET",
157
+ headers: this.buildRequestHeaders(credential),
158
+ redirect: "manual",
159
+ ...agent && { agent }
160
+ };
161
+ return [new URL(url.toString()), requestInit];
162
+ }
163
+ buildAgentCacheKey(clusterDetails, credential) {
164
+ const certPart = credential.type === "x509 client certificate" ? `${credential.cert}|${credential.key}` : "";
165
+ return `${clusterDetails.url}|${clusterDetails.skipTLSVerify ?? false}|${clusterDetails.caData ?? ""}|${clusterDetails.caFile ?? ""}|${certPart}`;
166
+ }
167
+ getOrCreateAgent(clusterDetails, credential, ca) {
168
+ const key = this.buildAgentCacheKey(clusterDetails, credential);
169
+ let agent = this.agentCache.get(key);
170
+ if (!agent) {
171
+ agent = this.agentFactory({
172
+ ca,
173
+ rejectUnauthorized: !clusterDetails.skipTLSVerify,
174
+ keepAlive: true,
175
+ ...credential.type === "x509 client certificate" && {
176
+ cert: credential.cert,
177
+ key: credential.key
178
+ }
179
+ });
180
+ this.agentCache.set(key, agent);
181
+ }
182
+ return agent;
183
+ }
184
+ }
185
+
186
+ exports.KubernetesConnection = KubernetesConnection;
187
+ exports.statusCodeToErrorType = statusCodeToErrorType;
188
+ //# sourceMappingURL=KubernetesConnection.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KubernetesConnection.cjs.js","sources":["../../src/service/KubernetesConnection.ts"],"sourcesContent":["/*\n * Copyright 2026 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 type { Cluster } from '@kubernetes/client-node';\nimport {\n ANNOTATION_KUBERNETES_AUTH_PROVIDER,\n SERVICEACCOUNT_CA_PATH,\n KubernetesErrorTypes,\n KubernetesFetchError,\n} from '@backstage/plugin-kubernetes-common';\nimport fetch, { RequestInit, Response } from 'node-fetch';\nimport * as https from 'node:https';\nimport fs from 'fs-extra';\nimport {\n ClusterDetails,\n KubernetesCredential,\n} from '@backstage/plugin-kubernetes-node';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\nexport const statusCodeToErrorType = (\n statusCode: number,\n): KubernetesErrorTypes => {\n switch (statusCode) {\n case 400:\n return 'BAD_REQUEST';\n case 401:\n return 'UNAUTHORIZED_ERROR';\n case 404:\n return 'NOT_FOUND';\n case 500:\n return 'SYSTEM_ERROR';\n default:\n return 'UNKNOWN_ERROR';\n }\n};\n\nexport type ConnectionResult =\n | { ok: true; url: URL; requestInit: RequestInit }\n | { ok: false; reason: 'missing_credentials' };\n\nexport class KubernetesConnection {\n private readonly logger: LoggerService;\n private readonly agentFactory: (options: https.AgentOptions) => https.Agent;\n private readonly agentCache = new Map<string, https.Agent>();\n private inClusterCache:\n | { url: URL; agent: https.Agent | undefined }\n | undefined;\n\n constructor({\n logger,\n agentFactory,\n }: {\n logger: LoggerService;\n agentFactory?: (options: https.AgentOptions) => https.Agent;\n }) {\n this.logger = logger;\n this.agentFactory = agentFactory ?? (options => new https.Agent(options));\n }\n\n buildResourcePath(\n group: string,\n apiVersion: string,\n plural: string,\n namespace?: string,\n ): string {\n const encode = (s: string) => encodeURIComponent(s);\n let path = group\n ? `/apis/${encode(group)}/${encode(apiVersion)}`\n : `/api/${encode(apiVersion)}`;\n if (namespace) {\n path += `/namespaces/${encode(namespace)}`;\n }\n path += `/${encode(plural)}`;\n return path;\n }\n\n async resolveConnection(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n ): Promise<ConnectionResult> {\n const authProvider =\n clusterDetails.authMetadata[ANNOTATION_KUBERNETES_AUTH_PROVIDER];\n\n if (this.isServiceAccountAuthentication(authProvider, clusterDetails)) {\n const [url, requestInit] = await this.fetchArgsInCluster(credential);\n return { ok: true, url, requestInit };\n } else if (!this.isCredentialMissing(authProvider, credential)) {\n const [url, requestInit] = await this.fetchArgs(\n clusterDetails,\n credential,\n );\n return { ok: true, url, requestInit };\n }\n return { ok: false, reason: 'missing_credentials' };\n }\n\n async fetchWithConnection(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n resourcePath: string,\n labelSelector?: string,\n ): Promise<Response> {\n const result = await this.resolveConnection(clusterDetails, credential);\n if (!result.ok) {\n return Promise.reject(\n new Error(\n `no bearer token or client cert for cluster '${clusterDetails.name}' and not running in Kubernetes`,\n ),\n );\n }\n\n const { url, requestInit } = result;\n if (url.pathname === '/') {\n url.pathname = resourcePath;\n } else {\n url.pathname += resourcePath;\n }\n\n if (labelSelector) {\n url.search = `labelSelector=${encodeURIComponent(labelSelector)}`;\n }\n\n return fetch(url, requestInit);\n }\n\n async handleUnsuccessfulResponse(\n clusterName: string,\n res: Response,\n ): Promise<KubernetesFetchError> {\n const resourcePath = new URL(res.url).pathname;\n this.logger.warn(\n `Received ${\n res.status\n } status when fetching \"${resourcePath}\" from cluster \"${clusterName}\"; body=[${await res.text()}]`,\n );\n return {\n errorType: statusCodeToErrorType(res.status),\n statusCode: res.status,\n resourcePath,\n };\n }\n\n private isServiceAccountAuthentication(\n authProvider: string,\n clusterDetails: ClusterDetails,\n ) {\n return (\n authProvider === 'serviceAccount' &&\n !clusterDetails.authMetadata.serviceAccountToken &&\n fs.pathExistsSync(SERVICEACCOUNT_CA_PATH)\n );\n }\n\n private isCredentialMissing(\n authProvider: string,\n credential: KubernetesCredential,\n ) {\n return (\n authProvider !== 'localKubectlProxy' && credential.type === 'anonymous'\n );\n }\n\n private buildRequestHeaders(\n credential: KubernetesCredential,\n ): Record<string, string> {\n return {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n ...(credential.type === 'bearer token' && {\n Authorization: `Bearer ${credential.token}`,\n }),\n };\n }\n\n private async fetchArgs(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n ): Promise<[URL, fetch.RequestInit]> {\n const { bufferFromFileOrString } = await import('@kubernetes/client-node');\n const requestInit: RequestInit = {\n method: 'GET',\n headers: this.buildRequestHeaders(credential),\n redirect: 'manual',\n };\n\n const url: URL = new URL(clusterDetails.url);\n if (url.protocol === 'https:') {\n const ca =\n bufferFromFileOrString(clusterDetails.caFile, clusterDetails.caData) ??\n undefined;\n requestInit.agent = this.getOrCreateAgent(clusterDetails, credential, ca);\n }\n return [url, requestInit];\n }\n\n private async fetchArgsInCluster(\n credential: KubernetesCredential,\n ): Promise<[URL, fetch.RequestInit]> {\n if (!this.inClusterCache) {\n const { KubeConfig } = await import('@kubernetes/client-node');\n const kc = new KubeConfig();\n kc.loadFromCluster();\n const cluster = kc.getCurrentCluster() as Cluster;\n const url = new URL(cluster.server);\n const agent =\n url.protocol === 'https:'\n ? this.agentFactory({\n ca: fs.readFileSync(cluster.caFile as string),\n keepAlive: true,\n })\n : undefined;\n this.inClusterCache = { url, agent };\n }\n\n const { url, agent } = this.inClusterCache;\n const requestInit: RequestInit = {\n method: 'GET',\n headers: this.buildRequestHeaders(credential),\n redirect: 'manual',\n ...(agent && { agent }),\n };\n return [new URL(url.toString()), requestInit];\n }\n\n private buildAgentCacheKey(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n ): string {\n const certPart =\n credential.type === 'x509 client certificate'\n ? `${credential.cert}|${credential.key}`\n : '';\n return `${clusterDetails.url}|${clusterDetails.skipTLSVerify ?? false}|${\n clusterDetails.caData ?? ''\n }|${clusterDetails.caFile ?? ''}|${certPart}`;\n }\n\n private getOrCreateAgent(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n ca: Buffer | string | undefined,\n ): https.Agent {\n const key = this.buildAgentCacheKey(clusterDetails, credential);\n\n let agent = this.agentCache.get(key);\n if (!agent) {\n agent = this.agentFactory({\n ca,\n rejectUnauthorized: !clusterDetails.skipTLSVerify,\n keepAlive: true,\n ...(credential.type === 'x509 client certificate' && {\n cert: credential.cert,\n key: credential.key,\n }),\n });\n this.agentCache.set(key, agent);\n }\n return agent;\n }\n}\n"],"names":["https","ANNOTATION_KUBERNETES_AUTH_PROVIDER","fetch","fs","SERVICEACCOUNT_CA_PATH","url","agent"],"mappings":"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"}
@@ -1,34 +1,5 @@
1
1
  'use strict';
2
2
 
3
- var pluginKubernetesCommon = require('@backstage/plugin-kubernetes-common');
4
- var fetch = require('node-fetch');
5
- var https = require('node:https');
6
- var fs = require('fs-extra');
7
-
8
- function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
9
-
10
- function _interopNamespaceCompat(e) {
11
- if (e && typeof e === 'object' && 'default' in e) return e;
12
- var n = Object.create(null);
13
- if (e) {
14
- Object.keys(e).forEach(function (k) {
15
- if (k !== 'default') {
16
- var d = Object.getOwnPropertyDescriptor(e, k);
17
- Object.defineProperty(n, k, d.get ? d : {
18
- enumerable: true,
19
- get: function () { return e[k]; }
20
- });
21
- }
22
- });
23
- }
24
- n.default = e;
25
- return Object.freeze(n);
26
- }
27
-
28
- var fetch__default = /*#__PURE__*/_interopDefaultCompat(fetch);
29
- var https__namespace = /*#__PURE__*/_interopNamespaceCompat(https);
30
- var fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
31
-
32
3
  const isError = (fr) => fr.hasOwnProperty("errorType");
33
4
  function fetchResultsToResponseWrapper(results) {
34
5
  const errors = [];
@@ -42,26 +13,10 @@ function fetchResultsToResponseWrapper(results) {
42
13
  }
43
14
  return { errors, responses };
44
15
  }
45
- const statusCodeToErrorType = (statusCode) => {
46
- switch (statusCode) {
47
- case 400:
48
- return "BAD_REQUEST";
49
- case 401:
50
- return "UNAUTHORIZED_ERROR";
51
- case 404:
52
- return "NOT_FOUND";
53
- case 500:
54
- return "SYSTEM_ERROR";
55
- default:
56
- return "UNKNOWN_ERROR";
57
- }
58
- };
59
16
  class KubernetesClientBasedFetcher {
60
- logger;
61
- agentCache = /* @__PURE__ */ new Map();
62
- inClusterCache;
63
- constructor({ logger }) {
64
- this.logger = logger;
17
+ connection;
18
+ constructor({ connection }) {
19
+ this.connection = connection;
65
20
  }
66
21
  fetchObjectsForService(params) {
67
22
  const fetchResults = Array.from(params.objectTypesToFetch).concat(params.customResources).map(
@@ -81,7 +36,10 @@ class KubernetesClientBasedFetcher {
81
36
  items
82
37
  )
83
38
  })
84
- ) : this.handleUnsuccessfulResponse(params.clusterDetails.name, r)
39
+ ) : this.connection.handleUnsuccessfulResponse(
40
+ params.clusterDetails.name,
41
+ r
42
+ )
85
43
  )
86
44
  );
87
45
  return Promise.all(fetchResults).then(fetchResultsToResponseWrapper);
@@ -130,132 +88,29 @@ class KubernetesClientBasedFetcher {
130
88
  })
131
89
  );
132
90
  } else if (podMetrics.ok) {
133
- return this.handleUnsuccessfulResponse(clusterDetails.name, podList);
91
+ return this.connection.handleUnsuccessfulResponse(
92
+ clusterDetails.name,
93
+ podList
94
+ );
134
95
  }
135
- return this.handleUnsuccessfulResponse(clusterDetails.name, podMetrics);
136
- }
137
- async handleUnsuccessfulResponse(clusterName, res) {
138
- const resourcePath = new URL(res.url).pathname;
139
- this.logger.warn(
140
- `Received ${res.status} status when fetching "${resourcePath}" from cluster "${clusterName}"; body=[${await res.text()}]`
96
+ return this.connection.handleUnsuccessfulResponse(
97
+ clusterDetails.name,
98
+ podMetrics
141
99
  );
142
- return {
143
- errorType: statusCodeToErrorType(res.status),
144
- statusCode: res.status,
145
- resourcePath
146
- };
147
- }
148
- buildResourcePath(group, apiVersion, plural, namespace) {
149
- const encode = (s) => encodeURIComponent(s);
150
- let path = group ? `/apis/${encode(group)}/${encode(apiVersion)}` : `/api/${encode(apiVersion)}`;
151
- if (namespace) {
152
- path += `/namespaces/${encode(namespace)}`;
153
- }
154
- path += `/${encode(plural)}`;
155
- return path;
156
100
  }
157
101
  async fetchResource(clusterDetails, credential, resource, namespace, labelSelector) {
158
- const resourcePath = this.buildResourcePath(
102
+ const resourcePath = this.connection.buildResourcePath(
159
103
  resource.group,
160
104
  resource.apiVersion,
161
105
  resource.plural,
162
106
  namespace
163
107
  );
164
- let url;
165
- let requestInit;
166
- const authProvider = clusterDetails.authMetadata[pluginKubernetesCommon.ANNOTATION_KUBERNETES_AUTH_PROVIDER];
167
- if (this.isServiceAccountAuthentication(authProvider, clusterDetails)) {
168
- [url, requestInit] = await this.fetchArgsInCluster(credential);
169
- } else if (!this.isCredentialMissing(authProvider, credential)) {
170
- [url, requestInit] = await this.fetchArgs(clusterDetails, credential);
171
- } else {
172
- return Promise.reject(
173
- new Error(
174
- `no bearer token or client cert for cluster '${clusterDetails.name}' and not running in Kubernetes`
175
- )
176
- );
177
- }
178
- if (url.pathname === "/") {
179
- url.pathname = resourcePath;
180
- } else {
181
- url.pathname += resourcePath;
182
- }
183
- if (labelSelector) {
184
- url.search = `labelSelector=${encodeURIComponent(labelSelector)}`;
185
- }
186
- return fetch__default.default(url, requestInit);
187
- }
188
- isServiceAccountAuthentication(authProvider, clusterDetails) {
189
- return authProvider === "serviceAccount" && !clusterDetails.authMetadata.serviceAccountToken && fs__default.default.pathExistsSync(pluginKubernetesCommon.SERVICEACCOUNT_CA_PATH);
190
- }
191
- isCredentialMissing(authProvider, credential) {
192
- return authProvider !== "localKubectlProxy" && credential.type === "anonymous";
193
- }
194
- buildRequestHeaders(credential) {
195
- return {
196
- Accept: "application/json",
197
- "Content-Type": "application/json",
198
- ...credential.type === "bearer token" && {
199
- Authorization: `Bearer ${credential.token}`
200
- }
201
- };
202
- }
203
- async fetchArgs(clusterDetails, credential) {
204
- const { bufferFromFileOrString } = await import('@kubernetes/client-node');
205
- const requestInit = {
206
- method: "GET",
207
- headers: this.buildRequestHeaders(credential),
208
- redirect: "manual"
209
- };
210
- const url = new URL(clusterDetails.url);
211
- if (url.protocol === "https:") {
212
- const ca = bufferFromFileOrString(clusterDetails.caFile, clusterDetails.caData) ?? void 0;
213
- requestInit.agent = this.getOrCreateAgent(clusterDetails, credential, ca);
214
- }
215
- return [url, requestInit];
216
- }
217
- async fetchArgsInCluster(credential) {
218
- if (!this.inClusterCache) {
219
- const { KubeConfig } = await import('@kubernetes/client-node');
220
- const kc = new KubeConfig();
221
- kc.loadFromCluster();
222
- const cluster = kc.getCurrentCluster();
223
- const url2 = new URL(cluster.server);
224
- const agent2 = url2.protocol === "https:" ? new https__namespace.Agent({
225
- ca: fs__default.default.readFileSync(cluster.caFile),
226
- keepAlive: true
227
- }) : void 0;
228
- this.inClusterCache = { url: url2, agent: agent2 };
229
- }
230
- const { url, agent } = this.inClusterCache;
231
- const requestInit = {
232
- method: "GET",
233
- headers: this.buildRequestHeaders(credential),
234
- redirect: "manual",
235
- ...agent && { agent }
236
- };
237
- return [new URL(url.toString()), requestInit];
238
- }
239
- buildAgentCacheKey(clusterDetails, credential) {
240
- const certPart = credential.type === "x509 client certificate" ? `${credential.cert}|${credential.key}` : "";
241
- return `${clusterDetails.url}|${clusterDetails.skipTLSVerify ?? false}|${clusterDetails.caData ?? ""}|${clusterDetails.caFile ?? ""}|${certPart}`;
242
- }
243
- getOrCreateAgent(clusterDetails, credential, ca) {
244
- const key = this.buildAgentCacheKey(clusterDetails, credential);
245
- let agent = this.agentCache.get(key);
246
- if (!agent) {
247
- agent = new https__namespace.Agent({
248
- ca,
249
- rejectUnauthorized: !clusterDetails.skipTLSVerify,
250
- keepAlive: true,
251
- ...credential.type === "x509 client certificate" && {
252
- cert: credential.cert,
253
- key: credential.key
254
- }
255
- });
256
- this.agentCache.set(key, agent);
257
- }
258
- return agent;
108
+ return this.connection.fetchWithConnection(
109
+ clusterDetails,
110
+ credential,
111
+ resourcePath,
112
+ labelSelector
113
+ );
259
114
  }
260
115
  redactSecretData(items) {
261
116
  return items.map((item) => {
@@ -1 +1 @@
1
- {"version":3,"file":"KubernetesFetcher.cjs.js","sources":["../../src/service/KubernetesFetcher.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 type { Cluster, CoreV1Api, Metrics } from '@kubernetes/client-node';\nimport {\n FetchResponseWrapper,\n KubernetesFetcher,\n ObjectFetchParams,\n ObjectToFetch,\n} from '@backstage/plugin-kubernetes-node';\nimport {\n ANNOTATION_KUBERNETES_AUTH_PROVIDER,\n SERVICEACCOUNT_CA_PATH,\n FetchResponse,\n KubernetesErrorTypes,\n KubernetesFetchError,\n PodStatusFetchResponse,\n} from '@backstage/plugin-kubernetes-common';\nimport fetch, { RequestInit, Response } from 'node-fetch';\nimport * as https from 'node:https';\nimport fs from 'fs-extra';\nimport { JsonObject } from '@backstage/types';\nimport {\n ClusterDetails,\n KubernetesCredential,\n} from '@backstage/plugin-kubernetes-node';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\nexport interface KubernetesClientBasedFetcherOptions {\n logger: LoggerService;\n}\n\ntype FetchResult = FetchResponse | KubernetesFetchError;\n\nconst isError = (fr: FetchResult): fr is KubernetesFetchError =>\n fr.hasOwnProperty('errorType');\n\nfunction fetchResultsToResponseWrapper(\n results: FetchResult[],\n): FetchResponseWrapper {\n const errors: KubernetesFetchError[] = [];\n const responses: FetchResponse[] = [];\n for (const result of results) {\n if (isError(result)) {\n errors.push(result);\n } else {\n responses.push(result);\n }\n }\n return { errors, responses };\n}\n\nconst statusCodeToErrorType = (statusCode: number): KubernetesErrorTypes => {\n switch (statusCode) {\n case 400:\n return 'BAD_REQUEST';\n case 401:\n return 'UNAUTHORIZED_ERROR';\n case 404:\n return 'NOT_FOUND';\n case 500:\n return 'SYSTEM_ERROR';\n default:\n return 'UNKNOWN_ERROR';\n }\n};\n\nexport class KubernetesClientBasedFetcher implements KubernetesFetcher {\n private readonly logger: LoggerService;\n private readonly agentCache = new Map<string, https.Agent>();\n private inClusterCache:\n | { url: URL; agent: https.Agent | undefined }\n | undefined;\n\n constructor({ logger }: KubernetesClientBasedFetcherOptions) {\n this.logger = logger;\n }\n\n fetchObjectsForService(\n params: ObjectFetchParams,\n ): Promise<FetchResponseWrapper> {\n const fetchResults = Array.from(params.objectTypesToFetch)\n .concat(params.customResources)\n .map(({ objectType, group, apiVersion, plural }) =>\n this.fetchResource(\n params.clusterDetails,\n params.credential,\n { group, apiVersion, plural },\n params.namespace,\n params.labelSelector,\n ).then(\n (r: Response): Promise<FetchResult> =>\n r.ok\n ? r.json().then(\n ({ kind, items }): FetchResponse => ({\n type: objectType,\n resources: this.transformResources(\n { objectType, group, apiVersion, plural },\n kind,\n items,\n ),\n }),\n )\n : this.handleUnsuccessfulResponse(params.clusterDetails.name, r),\n ),\n );\n\n return Promise.all(fetchResults).then(fetchResultsToResponseWrapper);\n }\n\n async fetchPodMetricsByNamespaces(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n namespaces: Set<string>,\n labelSelector?: string,\n ): Promise<FetchResponseWrapper> {\n const fetchResults = Array.from(namespaces).map(ns =>\n this.fetchPodMetricsForNamespace(\n clusterDetails,\n credential,\n ns,\n labelSelector,\n ),\n );\n\n return Promise.all(fetchResults).then(fetchResultsToResponseWrapper);\n }\n\n private async fetchPodMetricsForNamespace(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n namespace: string,\n labelSelector?: string,\n ): Promise<FetchResult> {\n const [podMetrics, podList] = await Promise.all([\n this.fetchResource(\n clusterDetails,\n credential,\n { group: 'metrics.k8s.io', apiVersion: 'v1beta1', plural: 'pods' },\n namespace,\n labelSelector,\n ),\n this.fetchResource(\n clusterDetails,\n credential,\n { group: '', apiVersion: 'v1', plural: 'pods' },\n namespace,\n labelSelector,\n ),\n ]);\n if (podMetrics.ok && podList.ok) {\n const { topPods } = await import('@kubernetes/client-node');\n return topPods(\n {\n listPodForAllNamespaces: () => podList.json(),\n } as unknown as CoreV1Api,\n {\n getPodMetrics: () => podMetrics.json(),\n } as unknown as Metrics,\n ).then(\n (resources): PodStatusFetchResponse => ({\n type: 'podstatus',\n resources,\n }),\n );\n } else if (podMetrics.ok) {\n return this.handleUnsuccessfulResponse(clusterDetails.name, podList);\n }\n return this.handleUnsuccessfulResponse(clusterDetails.name, podMetrics);\n }\n\n private async handleUnsuccessfulResponse(\n clusterName: string,\n res: Response,\n ): Promise<KubernetesFetchError> {\n const resourcePath = new URL(res.url).pathname;\n this.logger.warn(\n `Received ${\n res.status\n } status when fetching \"${resourcePath}\" from cluster \"${clusterName}\"; body=[${await res.text()}]`,\n );\n return {\n errorType: statusCodeToErrorType(res.status),\n statusCode: res.status,\n resourcePath,\n };\n }\n\n private buildResourcePath(\n group: string,\n apiVersion: string,\n plural: string,\n namespace?: string,\n ): string {\n const encode = (s: string) => encodeURIComponent(s);\n let path = group\n ? `/apis/${encode(group)}/${encode(apiVersion)}`\n : `/api/${encode(apiVersion)}`;\n if (namespace) {\n path += `/namespaces/${encode(namespace)}`;\n }\n path += `/${encode(plural)}`;\n return path;\n }\n\n private async fetchResource(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n resource: Pick<ObjectToFetch, 'group' | 'apiVersion' | 'plural'>,\n namespace?: string,\n labelSelector?: string,\n ): Promise<Response> {\n const resourcePath = this.buildResourcePath(\n resource.group,\n resource.apiVersion,\n resource.plural,\n namespace,\n );\n\n let url: URL;\n let requestInit: RequestInit;\n const authProvider =\n clusterDetails.authMetadata[ANNOTATION_KUBERNETES_AUTH_PROVIDER];\n\n if (this.isServiceAccountAuthentication(authProvider, clusterDetails)) {\n [url, requestInit] = await this.fetchArgsInCluster(credential);\n } else if (!this.isCredentialMissing(authProvider, credential)) {\n [url, requestInit] = await this.fetchArgs(clusterDetails, credential);\n } else {\n return Promise.reject(\n new Error(\n `no bearer token or client cert for cluster '${clusterDetails.name}' and not running in Kubernetes`,\n ),\n );\n }\n\n if (url.pathname === '/') {\n url.pathname = resourcePath;\n } else {\n url.pathname += resourcePath;\n }\n\n if (labelSelector) {\n url.search = `labelSelector=${encodeURIComponent(labelSelector)}`;\n }\n\n return fetch(url, requestInit);\n }\n\n private isServiceAccountAuthentication(\n authProvider: string,\n clusterDetails: ClusterDetails,\n ) {\n return (\n authProvider === 'serviceAccount' &&\n !clusterDetails.authMetadata.serviceAccountToken &&\n fs.pathExistsSync(SERVICEACCOUNT_CA_PATH)\n );\n }\n\n private isCredentialMissing(\n authProvider: string,\n credential: KubernetesCredential,\n ) {\n return (\n authProvider !== 'localKubectlProxy' && credential.type === 'anonymous'\n );\n }\n\n private buildRequestHeaders(\n credential: KubernetesCredential,\n ): Record<string, string> {\n return {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n ...(credential.type === 'bearer token' && {\n Authorization: `Bearer ${credential.token}`,\n }),\n };\n }\n\n private async fetchArgs(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n ): Promise<[URL, fetch.RequestInit]> {\n const { bufferFromFileOrString } = await import('@kubernetes/client-node');\n const requestInit: RequestInit = {\n method: 'GET',\n headers: this.buildRequestHeaders(credential),\n redirect: 'manual',\n };\n\n const url: URL = new URL(clusterDetails.url);\n if (url.protocol === 'https:') {\n const ca =\n bufferFromFileOrString(clusterDetails.caFile, clusterDetails.caData) ??\n undefined;\n requestInit.agent = this.getOrCreateAgent(clusterDetails, credential, ca);\n }\n return [url, requestInit];\n }\n\n private async fetchArgsInCluster(\n credential: KubernetesCredential,\n ): Promise<[URL, fetch.RequestInit]> {\n if (!this.inClusterCache) {\n const { KubeConfig } = await import('@kubernetes/client-node');\n const kc = new KubeConfig();\n kc.loadFromCluster();\n const cluster = kc.getCurrentCluster() as Cluster;\n const url = new URL(cluster.server);\n const agent =\n url.protocol === 'https:'\n ? new https.Agent({\n ca: fs.readFileSync(cluster.caFile as string),\n keepAlive: true,\n })\n : undefined;\n this.inClusterCache = { url, agent };\n }\n\n const { url, agent } = this.inClusterCache;\n const requestInit: RequestInit = {\n method: 'GET',\n headers: this.buildRequestHeaders(credential),\n redirect: 'manual',\n ...(agent && { agent }),\n };\n return [new URL(url.toString()), requestInit];\n }\n\n private buildAgentCacheKey(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n ): string {\n const certPart =\n credential.type === 'x509 client certificate'\n ? `${credential.cert}|${credential.key}`\n : '';\n return `${clusterDetails.url}|${clusterDetails.skipTLSVerify ?? false}|${\n clusterDetails.caData ?? ''\n }|${clusterDetails.caFile ?? ''}|${certPart}`;\n }\n\n private getOrCreateAgent(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n ca: Buffer | string | undefined,\n ): https.Agent {\n const key = this.buildAgentCacheKey(clusterDetails, credential);\n\n let agent = this.agentCache.get(key);\n if (!agent) {\n agent = new https.Agent({\n ca,\n rejectUnauthorized: !clusterDetails.skipTLSVerify,\n keepAlive: true,\n ...(credential.type === 'x509 client certificate' && {\n cert: credential.cert,\n key: credential.key,\n }),\n });\n this.agentCache.set(key, agent);\n }\n return agent;\n }\n\n private redactSecretData(items: JsonObject[]): JsonObject[] {\n return items.map((item: JsonObject) => {\n const redacted: JsonObject = { ...item };\n if (item.data && typeof item.data === 'object') {\n redacted.data = Object.fromEntries(\n Object.keys(item.data).map(key => [key, '***']),\n );\n }\n if (item.stringData && typeof item.stringData === 'object') {\n redacted.stringData = Object.fromEntries(\n Object.keys(item.stringData).map(key => [key, '***']),\n );\n }\n return redacted;\n });\n }\n\n private transformResources(\n resource: ObjectToFetch,\n kind: string | undefined,\n items: JsonObject[],\n ): JsonObject[] {\n const itemKind = kind?.replace(/(List)$/, '');\n\n // Whether a response holds Secrets is decided from the resource that was\n // requested and from the kind reported by the API server, rather than from\n // the object type, which callers can influence. The request is checked on\n // its own so that masking does not depend on the response at all, and the\n // kind is checked as well to cover requests that reach Secrets by some\n // other shape.\n const containsSecrets =\n resource.objectType === 'secrets' ||\n itemKind === 'Secret' ||\n (resource.group === '' &&\n resource.apiVersion === 'v1' &&\n resource.plural === 'secrets');\n\n const resources = containsSecrets ? this.redactSecretData(items) : items;\n\n if (resource.objectType === 'customresources') {\n if (itemKind === undefined) {\n throw new Error(\n `Missing kind in response when fetching '${resource.plural}'`,\n );\n }\n return resources.map((item: JsonObject) => ({\n ...item,\n kind: itemKind,\n }));\n }\n\n return resources;\n }\n}\n"],"names":["ANNOTATION_KUBERNETES_AUTH_PROVIDER","fetch","fs","SERVICEACCOUNT_CA_PATH","url","agent","https"],"mappings":"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"}
1
+ {"version":3,"file":"KubernetesFetcher.cjs.js","sources":["../../src/service/KubernetesFetcher.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 type { CoreV1Api, Metrics } from '@kubernetes/client-node';\nimport {\n FetchResponseWrapper,\n KubernetesFetcher,\n ObjectFetchParams,\n ObjectToFetch,\n} from '@backstage/plugin-kubernetes-node';\nimport {\n FetchResponse,\n KubernetesFetchError,\n PodStatusFetchResponse,\n} from '@backstage/plugin-kubernetes-common';\nimport { Response } from 'node-fetch';\nimport { JsonObject } from '@backstage/types';\nimport {\n ClusterDetails,\n KubernetesCredential,\n} from '@backstage/plugin-kubernetes-node';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { KubernetesConnection } from './KubernetesConnection';\n\nexport interface KubernetesClientBasedFetcherOptions {\n logger: LoggerService;\n connection: KubernetesConnection;\n}\n\ntype FetchResult = FetchResponse | KubernetesFetchError;\n\nconst isError = (fr: FetchResult): fr is KubernetesFetchError =>\n fr.hasOwnProperty('errorType');\n\nfunction fetchResultsToResponseWrapper(\n results: FetchResult[],\n): FetchResponseWrapper {\n const errors: KubernetesFetchError[] = [];\n const responses: FetchResponse[] = [];\n for (const result of results) {\n if (isError(result)) {\n errors.push(result);\n } else {\n responses.push(result);\n }\n }\n return { errors, responses };\n}\n\nexport class KubernetesClientBasedFetcher implements KubernetesFetcher {\n private readonly connection: KubernetesConnection;\n\n constructor({ connection }: KubernetesClientBasedFetcherOptions) {\n this.connection = connection;\n }\n\n fetchObjectsForService(\n params: ObjectFetchParams,\n ): Promise<FetchResponseWrapper> {\n const fetchResults = Array.from(params.objectTypesToFetch)\n .concat(params.customResources)\n .map(({ objectType, group, apiVersion, plural }) =>\n this.fetchResource(\n params.clusterDetails,\n params.credential,\n { group, apiVersion, plural },\n params.namespace,\n params.labelSelector,\n ).then(\n (r: Response): Promise<FetchResult> =>\n r.ok\n ? r.json().then(\n ({ kind, items }): FetchResponse => ({\n type: objectType,\n resources: this.transformResources(\n { objectType, group, apiVersion, plural },\n kind,\n items,\n ),\n }),\n )\n : this.connection.handleUnsuccessfulResponse(\n params.clusterDetails.name,\n r,\n ),\n ),\n );\n\n return Promise.all(fetchResults).then(fetchResultsToResponseWrapper);\n }\n\n async fetchPodMetricsByNamespaces(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n namespaces: Set<string>,\n labelSelector?: string,\n ): Promise<FetchResponseWrapper> {\n const fetchResults = Array.from(namespaces).map(ns =>\n this.fetchPodMetricsForNamespace(\n clusterDetails,\n credential,\n ns,\n labelSelector,\n ),\n );\n\n return Promise.all(fetchResults).then(fetchResultsToResponseWrapper);\n }\n\n private async fetchPodMetricsForNamespace(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n namespace: string,\n labelSelector?: string,\n ): Promise<FetchResult> {\n const [podMetrics, podList] = await Promise.all([\n this.fetchResource(\n clusterDetails,\n credential,\n { group: 'metrics.k8s.io', apiVersion: 'v1beta1', plural: 'pods' },\n namespace,\n labelSelector,\n ),\n this.fetchResource(\n clusterDetails,\n credential,\n { group: '', apiVersion: 'v1', plural: 'pods' },\n namespace,\n labelSelector,\n ),\n ]);\n if (podMetrics.ok && podList.ok) {\n const { topPods } = await import('@kubernetes/client-node');\n return topPods(\n {\n listPodForAllNamespaces: () => podList.json(),\n } as unknown as CoreV1Api,\n {\n getPodMetrics: () => podMetrics.json(),\n } as unknown as Metrics,\n ).then(\n (resources): PodStatusFetchResponse => ({\n type: 'podstatus',\n resources,\n }),\n );\n } else if (podMetrics.ok) {\n return this.connection.handleUnsuccessfulResponse(\n clusterDetails.name,\n podList,\n );\n }\n return this.connection.handleUnsuccessfulResponse(\n clusterDetails.name,\n podMetrics,\n );\n }\n\n private async fetchResource(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n resource: Pick<ObjectToFetch, 'group' | 'apiVersion' | 'plural'>,\n namespace?: string,\n labelSelector?: string,\n ): Promise<Response> {\n const resourcePath = this.connection.buildResourcePath(\n resource.group,\n resource.apiVersion,\n resource.plural,\n namespace,\n );\n\n return this.connection.fetchWithConnection(\n clusterDetails,\n credential,\n resourcePath,\n labelSelector,\n );\n }\n\n private redactSecretData(items: JsonObject[]): JsonObject[] {\n return items.map((item: JsonObject) => {\n const redacted: JsonObject = { ...item };\n if (item.data && typeof item.data === 'object') {\n redacted.data = Object.fromEntries(\n Object.keys(item.data).map(key => [key, '***']),\n );\n }\n if (item.stringData && typeof item.stringData === 'object') {\n redacted.stringData = Object.fromEntries(\n Object.keys(item.stringData).map(key => [key, '***']),\n );\n }\n return redacted;\n });\n }\n\n private transformResources(\n resource: ObjectToFetch,\n kind: string | undefined,\n items: JsonObject[],\n ): JsonObject[] {\n const itemKind = kind?.replace(/(List)$/, '');\n\n // Whether a response holds Secrets is decided from the resource that was\n // requested and from the kind reported by the API server, rather than from\n // the object type, which callers can influence. The request is checked on\n // its own so that masking does not depend on the response at all, and the\n // kind is checked as well to cover requests that reach Secrets by some\n // other shape.\n const containsSecrets =\n resource.objectType === 'secrets' ||\n itemKind === 'Secret' ||\n (resource.group === '' &&\n resource.apiVersion === 'v1' &&\n resource.plural === 'secrets');\n\n const resources = containsSecrets ? this.redactSecretData(items) : items;\n\n if (resource.objectType === 'customresources') {\n if (itemKind === undefined) {\n throw new Error(\n `Missing kind in response when fetching '${resource.plural}'`,\n );\n }\n return resources.map((item: JsonObject) => ({\n ...item,\n kind: itemKind,\n }));\n }\n\n return resources;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var KubernetesFetcher = require('./KubernetesFetcher.cjs.js');
4
+ var KubernetesConnection = require('./KubernetesConnection.cjs.js');
4
5
  var buildDefaultAuthStrategyMap = require('../auth/buildDefaultAuthStrategyMap.cjs.js');
5
6
  var luxon = require('luxon');
6
7
  var index = require('../cluster-locator/index.cjs.js');
@@ -16,9 +17,13 @@ class KubernetesInitializer {
16
17
  static create(opts) {
17
18
  return new KubernetesInitializer(opts);
18
19
  }
19
- async defaultFetcher() {
20
+ defaultConnection() {
21
+ return new KubernetesConnection.KubernetesConnection({ logger: this.opts.logger });
22
+ }
23
+ async defaultFetcher(connection) {
20
24
  return new KubernetesFetcher.KubernetesClientBasedFetcher({
21
- logger: this.opts.logger
25
+ logger: this.opts.logger,
26
+ connection
22
27
  });
23
28
  }
24
29
  async defaultAuthStrategy() {
@@ -96,9 +101,10 @@ class KubernetesInitializer {
96
101
  };
97
102
  }
98
103
  async init() {
104
+ const connection = this.defaultConnection();
99
105
  const fetcher = await this.opts.fetcher?.({
100
- getDefault: () => this.defaultFetcher()
101
- }) ?? await this.defaultFetcher();
106
+ getDefault: () => this.defaultFetcher(connection)
107
+ }) ?? await this.defaultFetcher(connection);
102
108
  const authStrategyMap = this.opts.authStrategyMap ?? await this.defaultAuthStrategy();
103
109
  const clusterSupplier = await this.opts.clusterSupplier?.({
104
110
  getDefault: () => this.defaultClusterSupplier({ authStrategyMap })
@@ -1 +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\ntype 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\nexport class KubernetesInitializer {\n private readonly opts: Opts;\n\n constructor(opts: Opts) {\n this.opts = opts;\n }\n\n static create(opts: Opts) {\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 ?? DEFAULT_OBJECTS,\n };\n }\n\n async init() {\n const fetcher =\n (await this.opts.fetcher?.({\n 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 fetcher,\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
+ {"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 { KubernetesConnection } from './KubernetesConnection';\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\ntype 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\nexport class KubernetesInitializer {\n private readonly opts: Opts;\n\n constructor(opts: Opts) {\n this.opts = opts;\n }\n\n static create(opts: Opts) {\n return new KubernetesInitializer(opts);\n }\n\n private defaultConnection() {\n return new KubernetesConnection({ logger: this.opts.logger });\n }\n\n private async defaultFetcher(connection: KubernetesConnection) {\n return new KubernetesClientBasedFetcher({\n logger: this.opts.logger,\n connection,\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 ?? DEFAULT_OBJECTS,\n };\n }\n\n async init() {\n const connection = this.defaultConnection();\n\n const fetcher =\n (await this.opts.fetcher?.({\n getDefault: () => this.defaultFetcher(connection),\n })) ?? (await this.defaultFetcher(connection));\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 fetcher,\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":["KubernetesConnection","KubernetesClientBasedFetcher","buildDefaultAuthStrategyMap","Duration","getCombinedClusterSupplier","DispatchStrategy","buildDefaultServiceLocator","KubernetesFanOutHandler","ALL_OBJECTS","DEFAULT_OBJECTS"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-kubernetes-backend",
3
- "version": "0.21.10",
3
+ "version": "0.21.11-next.2",
4
4
  "description": "A Backstage backend plugin that integrates towards Kubernetes",
5
5
  "backstage": {
6
6
  "role": "backend-plugin",
@@ -43,24 +43,24 @@
43
43
  "prepack": "backstage-cli package prepack",
44
44
  "postpack": "backstage-cli package postpack",
45
45
  "start": "backstage-cli package start",
46
- "test": "backstage-cli package test"
46
+ "test": "NODE_OPTIONS=--experimental-vm-modules backstage-cli package test"
47
47
  },
48
48
  "dependencies": {
49
49
  "@aws-crypto/sha256-js": "^5.0.0",
50
50
  "@aws-sdk/credential-providers": "^3.350.0",
51
51
  "@azure/identity": "^4.0.0",
52
- "@backstage/backend-plugin-api": "^1.10.0",
53
- "@backstage/catalog-client": "^1.16.1",
54
- "@backstage/catalog-model": "^1.10.0",
55
- "@backstage/config": "^1.3.8",
56
- "@backstage/errors": "^1.3.1",
57
- "@backstage/integration-aws-node": "^0.2.1",
58
- "@backstage/plugin-catalog-node": "^2.2.4",
59
- "@backstage/plugin-kubernetes-common": "^0.9.12",
60
- "@backstage/plugin-kubernetes-node": "^0.4.7",
61
- "@backstage/plugin-permission-common": "^0.9.10",
62
- "@backstage/plugin-permission-node": "^0.11.3",
63
- "@backstage/types": "^1.2.2",
52
+ "@backstage/backend-plugin-api": "1.10.1-next.1",
53
+ "@backstage/catalog-client": "1.16.2-next.1",
54
+ "@backstage/catalog-model": "1.10.0",
55
+ "@backstage/config": "1.3.9-next.0",
56
+ "@backstage/errors": "1.3.1",
57
+ "@backstage/integration-aws-node": "0.2.2-next.0",
58
+ "@backstage/plugin-catalog-node": "2.2.5-next.1",
59
+ "@backstage/plugin-kubernetes-common": "0.9.13-next.1",
60
+ "@backstage/plugin-kubernetes-node": "0.4.8-next.2",
61
+ "@backstage/plugin-permission-common": "0.9.11-next.0",
62
+ "@backstage/plugin-permission-node": "0.11.4-next.1",
63
+ "@backstage/types": "1.2.2",
64
64
  "@google-cloud/container": "^5.0.0",
65
65
  "@jest-mock/express": "^2.0.1",
66
66
  "@kubernetes/client-node": "1.4.0",
@@ -73,16 +73,18 @@
73
73
  "ipaddr.js": "^2.3.0",
74
74
  "lodash": "^4.17.21",
75
75
  "luxon": "^3.0.0",
76
- "node-fetch": "^2.7.0"
76
+ "node-fetch": "^2.7.0",
77
+ "split2": "^4.2.0"
77
78
  },
78
79
  "devDependencies": {
79
- "@backstage/backend-defaults": "^0.17.8",
80
- "@backstage/backend-test-utils": "^1.11.6",
81
- "@backstage/cli": "^0.36.5",
82
- "@backstage/plugin-permission-backend": "^0.7.15",
83
- "@backstage/plugin-permission-backend-module-allow-all-policy": "^0.2.22",
80
+ "@backstage/backend-defaults": "0.17.9-next.1",
81
+ "@backstage/backend-test-utils": "1.11.7-next.1",
82
+ "@backstage/cli": "0.36.6-next.1",
83
+ "@backstage/plugin-permission-backend": "0.7.16-next.1",
84
+ "@backstage/plugin-permission-backend-module-allow-all-policy": "0.2.23-next.1",
84
85
  "@types/express": "^4.17.6",
85
- "msw": "^1.0.0",
86
+ "@types/split2": "^4.2.0",
87
+ "msw": "^2.0.0",
86
88
  "supertest": "^7.0.0",
87
89
  "ws": "^8.20.1"
88
90
  },