@backstage/plugin-kubernetes-backend 0.18.7 → 0.19.0-next.1
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 +42 -0
- package/alpha/package.json +1 -1
- package/dist/package.json.cjs.js +2 -2
- package/dist/service/KubernetesFetcher.cjs.js +2 -2
- package/dist/service/KubernetesFetcher.cjs.js.map +1 -1
- package/dist/service/KubernetesProxy.cjs.js +1 -1
- package/dist/service/KubernetesProxy.cjs.js.map +1 -1
- package/package.json +21 -21
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# @backstage/plugin-kubernetes-backend
|
|
2
2
|
|
|
3
|
+
## 0.19.0-next.1
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 71b8704: Bumping @kubernetes/client-node to 1.0.0-rc7 to mitigate CVEs related to the request and tough-cookie packages
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies
|
|
12
|
+
- @backstage/plugin-kubernetes-common@0.9.0-next.0
|
|
13
|
+
- @backstage/plugin-kubernetes-node@0.2.0-next.1
|
|
14
|
+
- @backstage/backend-plugin-api@1.0.2-next.1
|
|
15
|
+
- @backstage/catalog-client@1.8.0-next.0
|
|
16
|
+
- @backstage/catalog-model@1.7.0
|
|
17
|
+
- @backstage/config@1.2.0
|
|
18
|
+
- @backstage/errors@1.2.4
|
|
19
|
+
- @backstage/integration-aws-node@0.1.12
|
|
20
|
+
- @backstage/types@1.1.1
|
|
21
|
+
- @backstage/plugin-auth-node@0.5.4-next.1
|
|
22
|
+
- @backstage/plugin-catalog-node@1.14.0-next.1
|
|
23
|
+
- @backstage/plugin-permission-common@0.8.1
|
|
24
|
+
- @backstage/plugin-permission-node@0.8.5-next.1
|
|
25
|
+
|
|
26
|
+
## 0.18.8-next.0
|
|
27
|
+
|
|
28
|
+
### Patch Changes
|
|
29
|
+
|
|
30
|
+
- Updated dependencies
|
|
31
|
+
- @backstage/plugin-auth-node@0.5.4-next.0
|
|
32
|
+
- @backstage/plugin-catalog-node@1.14.0-next.0
|
|
33
|
+
- @backstage/catalog-client@1.8.0-next.0
|
|
34
|
+
- @backstage/backend-plugin-api@1.0.2-next.0
|
|
35
|
+
- @backstage/catalog-model@1.7.0
|
|
36
|
+
- @backstage/config@1.2.0
|
|
37
|
+
- @backstage/errors@1.2.4
|
|
38
|
+
- @backstage/integration-aws-node@0.1.12
|
|
39
|
+
- @backstage/types@1.1.1
|
|
40
|
+
- @backstage/plugin-kubernetes-common@0.8.3
|
|
41
|
+
- @backstage/plugin-kubernetes-node@0.1.21-next.0
|
|
42
|
+
- @backstage/plugin-permission-common@0.8.1
|
|
43
|
+
- @backstage/plugin-permission-node@0.8.5-next.0
|
|
44
|
+
|
|
3
45
|
## 0.18.7
|
|
4
46
|
|
|
5
47
|
### Patch Changes
|
package/alpha/package.json
CHANGED
package/dist/package.json.cjs.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var name = "@backstage/plugin-kubernetes-backend";
|
|
6
|
-
var version = "0.
|
|
6
|
+
var version = "0.19.0-next.1";
|
|
7
7
|
var description = "A Backstage backend plugin that integrates towards Kubernetes";
|
|
8
8
|
var backstage = {
|
|
9
9
|
role: "backend-plugin",
|
|
@@ -81,7 +81,7 @@ var dependencies = {
|
|
|
81
81
|
"@backstage/types": "workspace:^",
|
|
82
82
|
"@google-cloud/container": "^5.0.0",
|
|
83
83
|
"@jest-mock/express": "^2.0.1",
|
|
84
|
-
"@kubernetes/client-node": "0.
|
|
84
|
+
"@kubernetes/client-node": "1.0.0-rc7",
|
|
85
85
|
"@types/express": "^4.17.6",
|
|
86
86
|
"@types/http-proxy-middleware": "^1.0.0",
|
|
87
87
|
"@types/luxon": "^3.0.0",
|
|
@@ -110,7 +110,7 @@ class KubernetesClientBasedFetcher {
|
|
|
110
110
|
if (podMetrics.ok && podList.ok) {
|
|
111
111
|
return clientNode.topPods(
|
|
112
112
|
{
|
|
113
|
-
listPodForAllNamespaces: () => podList.json()
|
|
113
|
+
listPodForAllNamespaces: () => podList.json()
|
|
114
114
|
},
|
|
115
115
|
{
|
|
116
116
|
getPodMetrics: () => podMetrics.json()
|
|
@@ -171,7 +171,7 @@ class KubernetesClientBasedFetcher {
|
|
|
171
171
|
return fetch__default.default(url, requestInit);
|
|
172
172
|
}
|
|
173
173
|
isServiceAccountAuthentication(authProvider, clusterDetails) {
|
|
174
|
-
return authProvider === "serviceAccount" && !clusterDetails.authMetadata.serviceAccountToken && fs__default.default.pathExistsSync(
|
|
174
|
+
return authProvider === "serviceAccount" && !clusterDetails.authMetadata.serviceAccountToken && fs__default.default.pathExistsSync(pluginKubernetesCommon.SERVICEACCOUNT_CA_PATH);
|
|
175
175
|
}
|
|
176
176
|
isCredentialMissing(authProvider, credential) {
|
|
177
177
|
return authProvider !== "localKubectlProxy" && credential.type === "anonymous";
|
|
@@ -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 {\n bufferFromFileOrString,\n Cluster,\n Config,\n CoreV1Api,\n KubeConfig,\n Metrics,\n topPods,\n} from '@kubernetes/client-node';\nimport lodash, { Dictionary } from 'lodash';\nimport {\n FetchResponseWrapper,\n KubernetesFetcher,\n ObjectFetchParams,\n} from '../types/types';\nimport {\n ANNOTATION_KUBERNETES_AUTH_PROVIDER,\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 '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 groupBy: Dictionary<FetchResult[]> = lodash.groupBy(results, value => {\n return isError(value) ? 'errors' : 'responses';\n });\n\n return {\n errors: groupBy.errors ?? [],\n responses: groupBy.responses ?? [],\n } as FetchResponseWrapper; // TODO would be nice to get rid of this 'as'\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\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,\n apiVersion,\n 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:\n objectType === 'customresources'\n ? items.map((item: JsonObject) => ({\n ...item,\n kind: kind.replace(/(List)$/, ''),\n }))\n : items,\n }),\n )\n : this.handleUnsuccessfulResponse(params.clusterDetails.name, r),\n ),\n );\n\n return Promise.all(fetchResults).then(fetchResultsToResponseWrapper);\n }\n\n fetchPodMetricsByNamespaces(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n namespaces: Set<string>,\n labelSelector?: string,\n ): Promise<FetchResponseWrapper> {\n const fetchResults = Array.from(namespaces).map(async ns => {\n const [podMetrics, podList] = await Promise.all([\n this.fetchResource(\n clusterDetails,\n credential,\n 'metrics.k8s.io',\n 'v1beta1',\n 'pods',\n ns,\n labelSelector,\n ),\n this.fetchResource(\n clusterDetails,\n credential,\n '',\n 'v1',\n 'pods',\n ns,\n labelSelector,\n ),\n ]);\n if (podMetrics.ok && podList.ok) {\n return topPods(\n {\n listPodForAllNamespaces: () =>\n podList.json().then(b => ({ body: b })),\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 return Promise.all(fetchResults).then(fetchResultsToResponseWrapper);\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 fetchResource(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n group: string,\n apiVersion: string,\n plural: string,\n namespace?: string,\n labelSelector?: string,\n ): Promise<Response> {\n const encode = (s: string) => encodeURIComponent(s);\n let resourcePath = group\n ? `/apis/${encode(group)}/${encode(apiVersion)}`\n : `/api/${encode(apiVersion)}`;\n if (namespace) {\n resourcePath += `/namespaces/${encode(namespace)}`;\n }\n resourcePath += `/${encode(plural)}`;\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] = this.fetchArgsInCluster(credential);\n } else if (!this.isCredentialMissing(authProvider, credential)) {\n [url, requestInit] = 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=${encode(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(Config.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 fetchArgs(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n ): [URL, RequestInit] {\n const requestInit: RequestInit = {\n method: 'GET',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n ...(credential.type === 'bearer token' && {\n Authorization: `Bearer ${credential.token}`,\n }),\n },\n };\n\n const url: URL = new URL(clusterDetails.url);\n if (url.protocol === 'https:') {\n requestInit.agent = new https.Agent({\n ca:\n bufferFromFileOrString(\n clusterDetails.caFile,\n clusterDetails.caData,\n ) ?? undefined,\n rejectUnauthorized: !clusterDetails.skipTLSVerify,\n ...(credential.type === 'x509 client certificate' && {\n cert: credential.cert,\n key: credential.key,\n }),\n });\n }\n return [url, requestInit];\n }\n private fetchArgsInCluster(\n credential: KubernetesCredential,\n ): [URL, RequestInit] {\n const requestInit: RequestInit = {\n method: 'GET',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n ...(credential.type === 'bearer token' && {\n Authorization: `Bearer ${credential.token}`,\n }),\n },\n };\n\n const kc = new KubeConfig();\n kc.loadFromCluster();\n // loadFromCluster is guaranteed to populate the cluster/user/context\n const cluster = kc.getCurrentCluster() as Cluster;\n\n const url = new URL(cluster.server);\n if (url.protocol === 'https:') {\n requestInit.agent = new https.Agent({\n ca: fs.readFileSync(cluster.caFile as string),\n });\n }\n return [url, requestInit];\n }\n}\n"],"names":["lodash","topPods","ANNOTATION_KUBERNETES_AUTH_PROVIDER","fetch","fs","Config","https","bufferFromFileOrString","KubeConfig"],"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 {\n bufferFromFileOrString,\n Cluster,\n CoreV1Api,\n KubeConfig,\n Metrics,\n topPods,\n} from '@kubernetes/client-node';\nimport lodash, { Dictionary } from 'lodash';\nimport {\n FetchResponseWrapper,\n KubernetesFetcher,\n ObjectFetchParams,\n} from '../types/types';\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 '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 groupBy: Dictionary<FetchResult[]> = lodash.groupBy(results, value => {\n return isError(value) ? 'errors' : 'responses';\n });\n\n return {\n errors: groupBy.errors ?? [],\n responses: groupBy.responses ?? [],\n } as FetchResponseWrapper; // TODO would be nice to get rid of this 'as'\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\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,\n apiVersion,\n 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:\n objectType === 'customresources'\n ? items.map((item: JsonObject) => ({\n ...item,\n kind: kind.replace(/(List)$/, ''),\n }))\n : items,\n }),\n )\n : this.handleUnsuccessfulResponse(params.clusterDetails.name, r),\n ),\n );\n\n return Promise.all(fetchResults).then(fetchResultsToResponseWrapper);\n }\n\n fetchPodMetricsByNamespaces(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n namespaces: Set<string>,\n labelSelector?: string,\n ): Promise<FetchResponseWrapper> {\n const fetchResults = Array.from(namespaces).map(async ns => {\n const [podMetrics, podList] = await Promise.all([\n this.fetchResource(\n clusterDetails,\n credential,\n 'metrics.k8s.io',\n 'v1beta1',\n 'pods',\n ns,\n labelSelector,\n ),\n this.fetchResource(\n clusterDetails,\n credential,\n '',\n 'v1',\n 'pods',\n ns,\n labelSelector,\n ),\n ]);\n if (podMetrics.ok && podList.ok) {\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 return Promise.all(fetchResults).then(fetchResultsToResponseWrapper);\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 fetchResource(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n group: string,\n apiVersion: string,\n plural: string,\n namespace?: string,\n labelSelector?: string,\n ): Promise<Response> {\n const encode = (s: string) => encodeURIComponent(s);\n let resourcePath = group\n ? `/apis/${encode(group)}/${encode(apiVersion)}`\n : `/api/${encode(apiVersion)}`;\n if (namespace) {\n resourcePath += `/namespaces/${encode(namespace)}`;\n }\n resourcePath += `/${encode(plural)}`;\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] = this.fetchArgsInCluster(credential);\n } else if (!this.isCredentialMissing(authProvider, credential)) {\n [url, requestInit] = 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=${encode(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 fetchArgs(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n ): [URL, RequestInit] {\n const requestInit: RequestInit = {\n method: 'GET',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n ...(credential.type === 'bearer token' && {\n Authorization: `Bearer ${credential.token}`,\n }),\n },\n };\n\n const url: URL = new URL(clusterDetails.url);\n if (url.protocol === 'https:') {\n requestInit.agent = new https.Agent({\n ca:\n bufferFromFileOrString(\n clusterDetails.caFile,\n clusterDetails.caData,\n ) ?? undefined,\n rejectUnauthorized: !clusterDetails.skipTLSVerify,\n ...(credential.type === 'x509 client certificate' && {\n cert: credential.cert,\n key: credential.key,\n }),\n });\n }\n return [url, requestInit];\n }\n private fetchArgsInCluster(\n credential: KubernetesCredential,\n ): [URL, RequestInit] {\n const requestInit: RequestInit = {\n method: 'GET',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n ...(credential.type === 'bearer token' && {\n Authorization: `Bearer ${credential.token}`,\n }),\n },\n };\n\n const kc = new KubeConfig();\n kc.loadFromCluster();\n // loadFromCluster is guaranteed to populate the cluster/user/context\n const cluster = kc.getCurrentCluster() as Cluster;\n\n const url = new URL(cluster.server);\n if (url.protocol === 'https:') {\n requestInit.agent = new https.Agent({\n ca: fs.readFileSync(cluster.caFile as string),\n });\n }\n return [url, requestInit];\n }\n}\n"],"names":["lodash","topPods","ANNOTATION_KUBERNETES_AUTH_PROVIDER","fetch","fs","SERVICEACCOUNT_CA_PATH","https","bufferFromFileOrString","KubeConfig"],"mappings":"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"}
|
|
@@ -144,7 +144,7 @@ class KubernetesProxy {
|
|
|
144
144
|
throw new errors.NotFoundError(`Cluster '${clusterName}' not found`);
|
|
145
145
|
}
|
|
146
146
|
const authProvider = cluster.authMetadata[pluginKubernetesCommon.ANNOTATION_KUBERNETES_AUTH_PROVIDER];
|
|
147
|
-
if (authProvider === "serviceAccount" && fs__default.default.pathExistsSync(
|
|
147
|
+
if (authProvider === "serviceAccount" && fs__default.default.pathExistsSync(pluginKubernetesCommon.SERVICEACCOUNT_CA_PATH) && !cluster.authMetadata.serviceAccountToken) {
|
|
148
148
|
const kc = new clientNode.KubeConfig();
|
|
149
149
|
kc.loadFromCluster();
|
|
150
150
|
const clusterFromKubeConfig = kc.getCurrentCluster();
|
|
@@ -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 kubernetesProxyPermission,\n KubernetesRequestAuth,\n} from '@backstage/plugin-kubernetes-common';\nimport { AuthorizeResult } from '@backstage/plugin-permission-common';\nimport {\n bufferFromFileOrString,\n Cluster,\n Config,\n KubeConfig,\n} from '@kubernetes/client-node';\nimport { createProxyMiddleware, RequestHandler } from 'http-proxy-middleware';\nimport fs from 'fs-extra';\n\nimport { AuthenticationStrategy } from '../auth';\nimport { ClusterDetails, KubernetesClustersSupplier } from '../types/types';\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';\nimport {\n createLegacyAuthAdapters,\n loggerToWinstonLogger,\n} from '@backstage/backend-common';\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\n const legacy = createLegacyAuthAdapters({\n discovery: options.discovery,\n httpAuth: options.httpAuth,\n });\n\n this.httpAuth = legacy.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 // TODO: Add 'log' to LoggerService\n logProvider: () => loggerToWinstonLogger(logger),\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 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 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(Config.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":["createLegacyAuthAdapters","kubernetesProxyPermission","AuthorizeResult","NotAllowedError","createProxyMiddleware","loggerToWinstonLogger","bufferFromFileOrString","ForwardedError","serializeError","NotFoundError","ANNOTATION_KUBERNETES_AUTH_PROVIDER","fs","Config","KubeConfig"],"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 {\n bufferFromFileOrString,\n Cluster,\n KubeConfig,\n} from '@kubernetes/client-node';\nimport { createProxyMiddleware, RequestHandler } from 'http-proxy-middleware';\nimport fs from 'fs-extra';\n\nimport { AuthenticationStrategy } from '../auth';\nimport { ClusterDetails, KubernetesClustersSupplier } from '../types/types';\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';\nimport {\n createLegacyAuthAdapters,\n loggerToWinstonLogger,\n} from '@backstage/backend-common';\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\n const legacy = createLegacyAuthAdapters({\n discovery: options.discovery,\n httpAuth: options.httpAuth,\n });\n\n this.httpAuth = legacy.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 // TODO: Add 'log' to LoggerService\n logProvider: () => loggerToWinstonLogger(logger),\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 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 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":["createLegacyAuthAdapters","kubernetesProxyPermission","AuthorizeResult","NotAllowedError","createProxyMiddleware","loggerToWinstonLogger","bufferFromFileOrString","ForwardedError","serializeError","NotFoundError","ANNOTATION_KUBERNETES_AUTH_PROVIDER","fs","SERVICEACCOUNT_CA_PATH","KubeConfig"],"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.
|
|
3
|
+
"version": "0.19.0-next.1",
|
|
4
4
|
"description": "A Backstage backend plugin that integrates towards Kubernetes",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "backend-plugin",
|
|
@@ -64,22 +64,22 @@
|
|
|
64
64
|
"@aws-sdk/signature-v4": "^3.347.0",
|
|
65
65
|
"@azure/identity": "^4.0.0",
|
|
66
66
|
"@backstage/backend-common": "^0.25.0",
|
|
67
|
-
"@backstage/backend-plugin-api": "
|
|
68
|
-
"@backstage/catalog-client": "
|
|
69
|
-
"@backstage/catalog-model": "
|
|
70
|
-
"@backstage/config": "
|
|
71
|
-
"@backstage/errors": "
|
|
72
|
-
"@backstage/integration-aws-node": "
|
|
73
|
-
"@backstage/plugin-auth-node": "
|
|
74
|
-
"@backstage/plugin-catalog-node": "
|
|
75
|
-
"@backstage/plugin-kubernetes-common": "
|
|
76
|
-
"@backstage/plugin-kubernetes-node": "
|
|
77
|
-
"@backstage/plugin-permission-common": "
|
|
78
|
-
"@backstage/plugin-permission-node": "
|
|
79
|
-
"@backstage/types": "
|
|
67
|
+
"@backstage/backend-plugin-api": "1.0.2-next.1",
|
|
68
|
+
"@backstage/catalog-client": "1.8.0-next.0",
|
|
69
|
+
"@backstage/catalog-model": "1.7.0",
|
|
70
|
+
"@backstage/config": "1.2.0",
|
|
71
|
+
"@backstage/errors": "1.2.4",
|
|
72
|
+
"@backstage/integration-aws-node": "0.1.12",
|
|
73
|
+
"@backstage/plugin-auth-node": "0.5.4-next.1",
|
|
74
|
+
"@backstage/plugin-catalog-node": "1.14.0-next.1",
|
|
75
|
+
"@backstage/plugin-kubernetes-common": "0.9.0-next.0",
|
|
76
|
+
"@backstage/plugin-kubernetes-node": "0.2.0-next.1",
|
|
77
|
+
"@backstage/plugin-permission-common": "0.8.1",
|
|
78
|
+
"@backstage/plugin-permission-node": "0.8.5-next.1",
|
|
79
|
+
"@backstage/types": "1.1.1",
|
|
80
80
|
"@google-cloud/container": "^5.0.0",
|
|
81
81
|
"@jest-mock/express": "^2.0.1",
|
|
82
|
-
"@kubernetes/client-node": "0.
|
|
82
|
+
"@kubernetes/client-node": "1.0.0-rc7",
|
|
83
83
|
"@types/express": "^4.17.6",
|
|
84
84
|
"@types/http-proxy-middleware": "^1.0.0",
|
|
85
85
|
"@types/luxon": "^3.0.0",
|
|
@@ -99,12 +99,12 @@
|
|
|
99
99
|
"yn": "^4.0.0"
|
|
100
100
|
},
|
|
101
101
|
"devDependencies": {
|
|
102
|
-
"@backstage/backend-app-api": "
|
|
103
|
-
"@backstage/backend-defaults": "
|
|
104
|
-
"@backstage/backend-test-utils": "
|
|
105
|
-
"@backstage/cli": "
|
|
106
|
-
"@backstage/plugin-permission-backend": "
|
|
107
|
-
"@backstage/plugin-permission-backend-module-allow-all-policy": "
|
|
102
|
+
"@backstage/backend-app-api": "1.0.2-next.1",
|
|
103
|
+
"@backstage/backend-defaults": "0.5.3-next.1",
|
|
104
|
+
"@backstage/backend-test-utils": "1.0.3-next.1",
|
|
105
|
+
"@backstage/cli": "0.29.0-next.1",
|
|
106
|
+
"@backstage/plugin-permission-backend": "0.5.51-next.1",
|
|
107
|
+
"@backstage/plugin-permission-backend-module-allow-all-policy": "0.2.2-next.1",
|
|
108
108
|
"@types/aws4": "^1.5.1",
|
|
109
109
|
"msw": "^1.0.0",
|
|
110
110
|
"supertest": "^7.0.0",
|