@backstage-community/plugin-ocm-backend 5.2.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/config.d.ts ADDED
@@ -0,0 +1,71 @@
1
+ /*
2
+ * Copyright 2024 The Backstage Authors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { SchedulerServiceTaskScheduleDefinitionConfig } from '@backstage/backend-plugin-api';
17
+
18
+ export interface Config {
19
+ catalog?: {
20
+ providers?: {
21
+ ocm?: {
22
+ /**
23
+ * Key is reflected as provider ID. Defines and claims plugin instance ownership of entities
24
+ */
25
+ [key: string]: (
26
+ | {
27
+ /**
28
+ * KubernetesPluginRef
29
+ */
30
+ /**
31
+ * Match the cluster name in kubernetes plugin config
32
+ */
33
+ kubernetesPluginRef: string;
34
+ }
35
+ | {
36
+ /**
37
+ * HubClusterConfig
38
+ */
39
+ /**
40
+ * Url of the hub cluster API endpoint
41
+ */
42
+ url: string;
43
+ /**
44
+ * Name that the hub cluster will assume in Backstage Catalog (in OCM this is always local-cluster which can be confusing)
45
+ */
46
+ name: string;
47
+ /**
48
+ * Service Account Token which is used for querying data from the hub
49
+ * @visibility secret
50
+ */
51
+ serviceAccountToken?: string;
52
+ /**
53
+ * Skip TLS certificate verification presented by the API server, defaults to false
54
+ */
55
+ skipTLSVerify?: boolean;
56
+ /**
57
+ * Base64-encoded CA bundle in PEM format used for verifying the TLS cert presented by the API server
58
+ */
59
+ caData?: string;
60
+ }
61
+ ) & {
62
+ /**
63
+ * Owner reference to created cluster entities in the catalog
64
+ */
65
+ owner?: string;
66
+ schedule?: SchedulerServiceTaskScheduleDefinitionConfig;
67
+ };
68
+ };
69
+ };
70
+ };
71
+ }
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ var backendPluginApi = require('@backstage/backend-plugin-api');
4
+ require('@backstage/catalog-model');
5
+ require('@backstage/errors');
6
+ require('@backstage-community/plugin-ocm-common');
7
+ require('@kubernetes/client-node');
8
+ require('semver');
9
+ var module$1 = require('./providers/module.cjs.js');
10
+ var router = require('./service/router.cjs.js');
11
+
12
+ const bundle = backendPluginApi.createBackendFeatureLoader({
13
+ async loader() {
14
+ return [module$1.catalogModuleOCMEntityProvider, router.ocmPlugin];
15
+ }
16
+ });
17
+
18
+ exports.bundle = bundle;
19
+ //# sourceMappingURL=bundle.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle.cjs.js","sources":["../src/bundle.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { createBackendFeatureLoader } from '@backstage/backend-plugin-api';\n\nimport { catalogModuleOCMEntityProvider } from './providers';\nimport { ocmPlugin } from './service/router';\n\nexport const bundle = createBackendFeatureLoader({\n async loader() {\n return [catalogModuleOCMEntityProvider, ocmPlugin];\n },\n});\n"],"names":["createBackendFeatureLoader","catalogModuleOCMEntityProvider","ocmPlugin"],"mappings":";;;;;;;;;;;AAqBO,MAAM,SAASA,2CAA2B,CAAA;AAAA,EAC/C,MAAM,MAAS,GAAA;AACb,IAAO,OAAA,CAACC,yCAAgCC,gBAAS,CAAA,CAAA;AAAA,GACnD;AACF,CAAC;;;;"}
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ const CONSOLE_CLAIM = "consoleurl.cluster.open-cluster-management.io";
4
+ const HUB_CLUSTER_NAME_IN_OCM = "local-cluster";
5
+ const ANNOTATION_KUBERNETES_API_SERVER = "kubernetes.io/api-server";
6
+
7
+ exports.ANNOTATION_KUBERNETES_API_SERVER = ANNOTATION_KUBERNETES_API_SERVER;
8
+ exports.CONSOLE_CLAIM = CONSOLE_CLAIM;
9
+ exports.HUB_CLUSTER_NAME_IN_OCM = HUB_CLUSTER_NAME_IN_OCM;
10
+ //# sourceMappingURL=constants.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.cjs.js","sources":["../src/constants.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nexport const CONSOLE_CLAIM = 'consoleurl.cluster.open-cluster-management.io';\nexport const HUB_CLUSTER_NAME_IN_OCM = 'local-cluster';\nexport const ANNOTATION_KUBERNETES_API_SERVER = 'kubernetes.io/api-server';\n"],"names":[],"mappings":";;AAeO,MAAM,aAAgB,GAAA,gDAAA;AACtB,MAAM,uBAA0B,GAAA,gBAAA;AAChC,MAAM,gCAAmC,GAAA;;;;;;"}
@@ -0,0 +1,80 @@
1
+ 'use strict';
2
+
3
+ var backendPluginApi = require('@backstage/backend-plugin-api');
4
+
5
+ const KUBERNETES_PLUGIN_CONFIG = "kubernetes.clusterLocatorMethods";
6
+ const OCM_PREFIX = "catalog.providers.ocm";
7
+ const KUBERNETES_PLUGIN_KEY = "kubernetesPluginRef";
8
+ const OWNER_KEY = "owner";
9
+ const isValidUrl = (url) => {
10
+ try {
11
+ new URL(url);
12
+ } catch (error) {
13
+ return false;
14
+ }
15
+ return true;
16
+ };
17
+ const deferToKubernetesPlugin = (config) => {
18
+ if (config.has(KUBERNETES_PLUGIN_KEY)) {
19
+ return true;
20
+ }
21
+ return false;
22
+ };
23
+ const getHubClusterFromKubernetesConfig = (id, config, globalConfig) => {
24
+ const name = config.getOptionalString(KUBERNETES_PLUGIN_KEY);
25
+ const _logTemplate = `Hub cluster ${OCM_PREFIX}.${id}.${KUBERNETES_PLUGIN_KEY}=${name}`;
26
+ const hub = globalConfig.getConfigArray(KUBERNETES_PLUGIN_CONFIG).flatMap((method) => method.getOptionalConfigArray("clusters") || []).find((cluster) => cluster.getString("name") === name);
27
+ if (!hub) {
28
+ throw new Error(
29
+ `${_logTemplate} not defined in kubernetes in ${KUBERNETES_PLUGIN_CONFIG}.clusters`
30
+ );
31
+ }
32
+ if (hub.getString("authProvider") !== "serviceAccount") {
33
+ throw new Error(`${_logTemplate} has to authenticate via 'serviceAccount'`);
34
+ }
35
+ return hub;
36
+ };
37
+ const getHubClusterFromOcmConfig = (id, config) => {
38
+ const requiredValues = ["name", "url"];
39
+ requiredValues.forEach((key) => {
40
+ if (!config.has(key)) {
41
+ throw new Error(
42
+ `Value must be specified in config at '${OCM_PREFIX}.${id}.${key}'`
43
+ );
44
+ }
45
+ });
46
+ return config;
47
+ };
48
+ const getHubClusterFromConfig = (id, config, globalConfig) => {
49
+ const hub = deferToKubernetesPlugin(config) ? getHubClusterFromKubernetesConfig(id, config, globalConfig) : getHubClusterFromOcmConfig(id, config);
50
+ const url = hub.getString("url");
51
+ if (!isValidUrl(url)) {
52
+ throw new Error(`"${url}" is not a valid url`);
53
+ }
54
+ return {
55
+ id,
56
+ url,
57
+ hubResourceName: hub.getString("name"),
58
+ serviceAccountToken: hub.getOptionalString("serviceAccountToken"),
59
+ skipTLSVerify: hub.getOptionalBoolean("skipTLSVerify") || false,
60
+ caData: hub.getOptionalString("caData"),
61
+ owner: config.getOptionalString(OWNER_KEY) ?? "unknown",
62
+ schedule: config.has("schedule") ? backendPluginApi.readSchedulerServiceTaskScheduleDefinitionFromConfig(
63
+ config.getConfig("schedule")
64
+ ) : void 0
65
+ };
66
+ };
67
+ const readOcmConfigs = (config) => {
68
+ const ocmConfigs = config.getOptionalConfig(OCM_PREFIX);
69
+ if (!ocmConfigs) {
70
+ return [];
71
+ }
72
+ return ocmConfigs.keys().map((id) => getHubClusterFromConfig(id, ocmConfigs.getConfig(id), config));
73
+ };
74
+
75
+ exports.deferToKubernetesPlugin = deferToKubernetesPlugin;
76
+ exports.getHubClusterFromConfig = getHubClusterFromConfig;
77
+ exports.getHubClusterFromKubernetesConfig = getHubClusterFromKubernetesConfig;
78
+ exports.getHubClusterFromOcmConfig = getHubClusterFromOcmConfig;
79
+ exports.readOcmConfigs = readOcmConfigs;
80
+ //# sourceMappingURL=config.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.cjs.js","sources":["../../src/helpers/config.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { readSchedulerServiceTaskScheduleDefinitionFromConfig } from '@backstage/backend-plugin-api';\nimport type { Config } from '@backstage/config';\n\nimport { OcmConfig } from '../types';\n\nconst KUBERNETES_PLUGIN_CONFIG = 'kubernetes.clusterLocatorMethods';\nconst OCM_PREFIX = 'catalog.providers.ocm';\nconst KUBERNETES_PLUGIN_KEY = 'kubernetesPluginRef';\nconst OWNER_KEY = 'owner';\n\nconst isValidUrl = (url: string): boolean => {\n try {\n // eslint-disable-next-line no-new\n new URL(url);\n } catch (error) {\n return false;\n }\n return true;\n};\n\nexport const deferToKubernetesPlugin = (config: Config): boolean => {\n if (config.has(KUBERNETES_PLUGIN_KEY)) {\n return true;\n }\n return false;\n};\n\nexport const getHubClusterFromKubernetesConfig = (\n id: string,\n config: Config,\n globalConfig: Config,\n): Config => {\n const name = config.getOptionalString(KUBERNETES_PLUGIN_KEY);\n const _logTemplate = `Hub cluster ${OCM_PREFIX}.${id}.${KUBERNETES_PLUGIN_KEY}=${name}`;\n\n const hub = globalConfig\n .getConfigArray(KUBERNETES_PLUGIN_CONFIG)\n .flatMap(method => method.getOptionalConfigArray('clusters') || [])\n .find(cluster => cluster.getString('name') === name);\n if (!hub) {\n throw new Error(\n `${_logTemplate} not defined in kubernetes in ${KUBERNETES_PLUGIN_CONFIG}.clusters`,\n );\n }\n\n if (hub.getString('authProvider') !== 'serviceAccount') {\n throw new Error(`${_logTemplate} has to authenticate via 'serviceAccount'`);\n }\n return hub;\n};\n\nexport const getHubClusterFromOcmConfig = (\n id: string,\n config: Config,\n): Config => {\n // Check if required values are valid\n const requiredValues = ['name', 'url'];\n requiredValues.forEach(key => {\n if (!config.has(key)) {\n throw new Error(\n `Value must be specified in config at '${OCM_PREFIX}.${id}.${key}'`,\n );\n }\n });\n return config;\n};\n\nexport const getHubClusterFromConfig = (\n id: string,\n config: Config,\n globalConfig: Config,\n): OcmConfig => {\n const hub = deferToKubernetesPlugin(config)\n ? getHubClusterFromKubernetesConfig(id, config, globalConfig)\n : getHubClusterFromOcmConfig(id, config);\n\n const url = hub.getString('url');\n if (!isValidUrl(url)) {\n throw new Error(`\"${url}\" is not a valid url`);\n }\n\n return {\n id,\n url,\n hubResourceName: hub.getString('name'),\n serviceAccountToken: hub.getOptionalString('serviceAccountToken'),\n skipTLSVerify: hub.getOptionalBoolean('skipTLSVerify') || false,\n caData: hub.getOptionalString('caData'),\n owner: config.getOptionalString(OWNER_KEY) ?? 'unknown',\n schedule: config.has('schedule')\n ? readSchedulerServiceTaskScheduleDefinitionFromConfig(\n config.getConfig('schedule'),\n )\n : undefined,\n };\n};\n\nexport const readOcmConfigs = (config: Config): OcmConfig[] => {\n const ocmConfigs = config.getOptionalConfig(OCM_PREFIX);\n\n if (!ocmConfigs) {\n return [];\n }\n\n return ocmConfigs\n .keys()\n .map(id => getHubClusterFromConfig(id, ocmConfigs.getConfig(id), config));\n};\n"],"names":["readSchedulerServiceTaskScheduleDefinitionFromConfig"],"mappings":"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"}
@@ -0,0 +1,106 @@
1
+ 'use strict';
2
+
3
+ var clientNode = require('@kubernetes/client-node');
4
+
5
+ const hubApiClient = (clusterConfig, logger) => {
6
+ const kubeConfig = new clientNode.KubeConfig();
7
+ if (!clusterConfig.serviceAccountToken) {
8
+ logger.info("Using default kubernetes config");
9
+ kubeConfig.loadFromDefault();
10
+ return kubeConfig.makeApiClient(clientNode.CustomObjectsApi);
11
+ }
12
+ logger.info("Loading kubernetes config from config file");
13
+ const user = {
14
+ name: "backstage",
15
+ token: clusterConfig.serviceAccountToken
16
+ };
17
+ const context = {
18
+ name: clusterConfig.hubResourceName,
19
+ user: user.name,
20
+ cluster: clusterConfig.hubResourceName
21
+ };
22
+ kubeConfig.loadFromOptions({
23
+ clusters: [
24
+ {
25
+ server: clusterConfig.url,
26
+ name: clusterConfig.hubResourceName,
27
+ skipTLSVerify: clusterConfig.skipTLSVerify,
28
+ caData: clusterConfig.caData
29
+ }
30
+ ],
31
+ users: [user],
32
+ contexts: [context],
33
+ currentContext: context.name
34
+ });
35
+ return kubeConfig.makeApiClient(clientNode.CustomObjectsApi);
36
+ };
37
+ const kubeApiResponseHandler = (call) => {
38
+ return call.then((r) => {
39
+ return r.body;
40
+ }).catch((r) => {
41
+ if (!r.body) {
42
+ throw Object.assign(new Error(r.message), {
43
+ // If there is no body, there is no status code, default to 500
44
+ statusCode: 500,
45
+ name: r.message
46
+ });
47
+ } else if (typeof r.body === "string") {
48
+ throw Object.assign(new Error(r.body), {
49
+ statusCode: r.body.code || r.statusCode,
50
+ name: r.body
51
+ });
52
+ }
53
+ throw Object.assign(new Error(r.body.reason), {
54
+ // Name and statusCode are required by the backstage error handler
55
+ statusCode: r.body.code || r.statusCode,
56
+ name: r.body.reason,
57
+ ...r.body
58
+ });
59
+ });
60
+ };
61
+ const getManagedCluster = (api, name) => {
62
+ return kubeApiResponseHandler(
63
+ api.getClusterCustomObject(
64
+ "cluster.open-cluster-management.io",
65
+ "v1",
66
+ "managedclusters",
67
+ name
68
+ )
69
+ );
70
+ };
71
+ const listManagedClusters = (api) => {
72
+ return kubeApiResponseHandler(
73
+ api.listClusterCustomObject(
74
+ "cluster.open-cluster-management.io",
75
+ "v1",
76
+ "managedclusters"
77
+ )
78
+ );
79
+ };
80
+ const getManagedClusterInfo = (api, name) => {
81
+ return kubeApiResponseHandler(
82
+ api.getNamespacedCustomObject(
83
+ "internal.open-cluster-management.io",
84
+ "v1beta1",
85
+ name,
86
+ "managedclusterinfos",
87
+ name
88
+ )
89
+ );
90
+ };
91
+ const listManagedClusterInfos = (api) => {
92
+ return kubeApiResponseHandler(
93
+ api.listClusterCustomObject(
94
+ "internal.open-cluster-management.io",
95
+ "v1beta1",
96
+ "managedclusterinfos"
97
+ )
98
+ );
99
+ };
100
+
101
+ exports.getManagedCluster = getManagedCluster;
102
+ exports.getManagedClusterInfo = getManagedClusterInfo;
103
+ exports.hubApiClient = hubApiClient;
104
+ exports.listManagedClusterInfos = listManagedClusterInfos;
105
+ exports.listManagedClusters = listManagedClusters;
106
+ //# sourceMappingURL=kubernetes.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kubernetes.cjs.js","sources":["../../src/helpers/kubernetes.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\nimport {\n CustomObjectsApi,\n KubeConfig,\n KubernetesListObject,\n} from '@kubernetes/client-node';\n\nimport http from 'http';\n\nimport { ManagedCluster, ManagedClusterInfo, OcmConfig } from '../types';\n\nexport const hubApiClient = (\n clusterConfig: OcmConfig,\n logger: LoggerService,\n): CustomObjectsApi => {\n const kubeConfig = new KubeConfig();\n\n if (!clusterConfig.serviceAccountToken) {\n logger.info('Using default kubernetes config');\n kubeConfig.loadFromDefault();\n return kubeConfig.makeApiClient(CustomObjectsApi);\n }\n\n logger.info('Loading kubernetes config from config file');\n\n const user = {\n name: 'backstage',\n token: clusterConfig.serviceAccountToken,\n };\n\n const context = {\n name: clusterConfig.hubResourceName,\n user: user.name,\n cluster: clusterConfig.hubResourceName,\n };\n\n kubeConfig.loadFromOptions({\n clusters: [\n {\n server: clusterConfig.url,\n name: clusterConfig.hubResourceName,\n skipTLSVerify: clusterConfig.skipTLSVerify,\n caData: clusterConfig.caData,\n },\n ],\n users: [user],\n contexts: [context],\n currentContext: context.name,\n });\n return kubeConfig.makeApiClient(CustomObjectsApi);\n};\n\nconst kubeApiResponseHandler = <T extends Object>(\n call: Promise<{\n response: http.IncomingMessage;\n body: object;\n }>,\n) => {\n return call\n .then(r => {\n return r.body as T;\n })\n .catch(r => {\n if (!r.body) {\n throw Object.assign(new Error(r.message), {\n // If there is no body, there is no status code, default to 500\n statusCode: 500,\n name: r.message,\n });\n } else if (typeof r.body === 'string') {\n throw Object.assign(new Error(r.body), {\n statusCode: r.body.code || r.statusCode,\n name: r.body,\n });\n }\n throw Object.assign(new Error(r.body.reason), {\n // Name and statusCode are required by the backstage error handler\n statusCode: r.body.code || r.statusCode,\n name: r.body.reason,\n ...r.body,\n });\n });\n};\n\nexport const getManagedCluster = (api: CustomObjectsApi, name: string) => {\n return kubeApiResponseHandler<ManagedCluster>(\n api.getClusterCustomObject(\n 'cluster.open-cluster-management.io',\n 'v1',\n 'managedclusters',\n name,\n ),\n );\n};\n\nexport const listManagedClusters = (api: CustomObjectsApi) => {\n return kubeApiResponseHandler<KubernetesListObject<ManagedCluster>>(\n api.listClusterCustomObject(\n 'cluster.open-cluster-management.io',\n 'v1',\n 'managedclusters',\n ),\n );\n};\n\nexport const getManagedClusterInfo = (api: CustomObjectsApi, name: string) => {\n return kubeApiResponseHandler<ManagedClusterInfo>(\n api.getNamespacedCustomObject(\n 'internal.open-cluster-management.io',\n 'v1beta1',\n name,\n 'managedclusterinfos',\n name,\n ),\n );\n};\n\nexport const listManagedClusterInfos = (api: CustomObjectsApi) => {\n return kubeApiResponseHandler<KubernetesListObject<ManagedClusterInfo>>(\n api.listClusterCustomObject(\n 'internal.open-cluster-management.io',\n 'v1beta1',\n 'managedclusterinfos',\n ),\n );\n};\n"],"names":["KubeConfig","CustomObjectsApi"],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ 'use strict';
2
+
3
+ var semver = require('semver');
4
+ var constants = require('../constants.cjs.js');
5
+
6
+ const convertCpus = (cpus) => {
7
+ if (!cpus) {
8
+ return void 0;
9
+ }
10
+ if (cpus.endsWith("m")) {
11
+ return parseInt(cpus.slice(0, cpus.length - 1), 10) / 1e3;
12
+ }
13
+ return parseInt(cpus, 10);
14
+ };
15
+ const parseResources = (resources) => ({
16
+ cpuCores: convertCpus(resources?.cpu),
17
+ memorySize: resources?.memory,
18
+ numberOfPods: parseInt(resources?.pods, 10) || void 0
19
+ });
20
+ const getClaim = (cluster, claimName) => cluster.status?.clusterClaims?.find((value) => value.name === claimName)?.value ?? "";
21
+ const parseClusterStatus = (mc) => {
22
+ const available = mc.status?.conditions.find(
23
+ (value) => value.type === "ManagedClusterConditionAvailable"
24
+ );
25
+ return {
26
+ available: available?.status.toLowerCase() === "true",
27
+ reason: available?.message
28
+ };
29
+ };
30
+ const parseManagedCluster = (mc) => ({
31
+ status: parseClusterStatus(mc),
32
+ consoleUrl: getClaim(mc, constants.CONSOLE_CLAIM),
33
+ kubernetesVersion: getClaim(mc, "kubeversion.open-cluster-management.io"),
34
+ oauthUrl: getClaim(mc, "oauthredirecturis.openshift.io"),
35
+ openshiftId: mc.metadata.labels?.clusterID ?? getClaim(mc, "id.openshift.io"),
36
+ openshiftVersion: mc.metadata.labels?.openshiftVersion ?? getClaim(mc, "version.openshift.io"),
37
+ platform: getClaim(mc, "platform.open-cluster-management.io"),
38
+ region: getClaim(mc, "region.open-cluster-management.io"),
39
+ allocatableResources: parseResources(mc.status?.allocatable || {}),
40
+ availableResources: parseResources(mc.status?.capacity || {})
41
+ });
42
+ const parseUpdateInfo = (clusterInfo) => {
43
+ const { availableUpdates, versionAvailableUpdates } = clusterInfo.status?.distributionInfo.ocp || {};
44
+ if (!availableUpdates || availableUpdates?.length === 0 || !versionAvailableUpdates || versionAvailableUpdates?.length === 0) {
45
+ return {
46
+ update: {
47
+ available: false
48
+ }
49
+ };
50
+ }
51
+ const version = semver.maxSatisfying(availableUpdates, "*");
52
+ return {
53
+ update: {
54
+ available: true,
55
+ version,
56
+ url: versionAvailableUpdates[availableUpdates.indexOf(version)]?.url
57
+ }
58
+ };
59
+ };
60
+ const parseNodeStatus = (clusterInfo) => clusterInfo.status?.nodeList?.map((node) => {
61
+ if (node.conditions.length !== 1) {
62
+ throw new Error("Found more node conditions then one");
63
+ }
64
+ const condition = node.conditions[0];
65
+ return {
66
+ status: condition.status,
67
+ type: condition.type
68
+ };
69
+ }) || [];
70
+ const translateResourceToOCM = (clusterName, hubResourceName) => clusterName === hubResourceName ? constants.HUB_CLUSTER_NAME_IN_OCM : clusterName;
71
+ const translateOCMToResource = (clusterName, hubResourceName) => clusterName === constants.HUB_CLUSTER_NAME_IN_OCM ? hubResourceName : clusterName;
72
+
73
+ exports.getClaim = getClaim;
74
+ exports.parseClusterStatus = parseClusterStatus;
75
+ exports.parseManagedCluster = parseManagedCluster;
76
+ exports.parseNodeStatus = parseNodeStatus;
77
+ exports.parseResources = parseResources;
78
+ exports.parseUpdateInfo = parseUpdateInfo;
79
+ exports.translateOCMToResource = translateOCMToResource;
80
+ exports.translateResourceToOCM = translateResourceToOCM;
81
+ //# sourceMappingURL=parser.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.cjs.js","sources":["../../src/helpers/parser.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { maxSatisfying } from 'semver';\n\nimport type {\n ClusterDetails,\n ClusterNodesStatus,\n ClusterStatus,\n} from '@backstage-community/plugin-ocm-common';\n\nimport { CONSOLE_CLAIM, HUB_CLUSTER_NAME_IN_OCM } from '../constants';\nimport { ClusterClaim, ManagedCluster, ManagedClusterInfo } from '../types';\n\nconst convertCpus = (cpus: string | undefined): number | undefined => {\n if (!cpus) {\n return undefined;\n }\n if (cpus.endsWith('m')) {\n return parseInt(cpus.slice(0, cpus.length - 1), 10) / 1000;\n }\n return parseInt(cpus, 10);\n};\n\nexport const parseResources = (\n resources: Record<string, string>,\n): Record<string, string | number | undefined> => ({\n cpuCores: convertCpus(resources?.cpu),\n memorySize: resources?.memory,\n numberOfPods: parseInt(resources?.pods, 10) || undefined,\n});\n\nexport const getClaim = (\n cluster: { status?: { clusterClaims: ClusterClaim[] } },\n claimName: string,\n): string =>\n cluster.status?.clusterClaims?.find(value => value.name === claimName)\n ?.value ?? '';\n\nexport const parseClusterStatus = (mc: ManagedCluster): ClusterStatus => {\n const available = mc.status?.conditions.find(\n (value: any) => value.type === 'ManagedClusterConditionAvailable',\n );\n\n return {\n available: available?.status.toLowerCase() === 'true',\n reason: available?.message,\n };\n};\n\nexport const parseManagedCluster = (mc: ManagedCluster): ClusterDetails => ({\n status: parseClusterStatus(mc),\n consoleUrl: getClaim(mc, CONSOLE_CLAIM),\n kubernetesVersion: getClaim(mc, 'kubeversion.open-cluster-management.io'),\n oauthUrl: getClaim(mc, 'oauthredirecturis.openshift.io'),\n openshiftId:\n mc.metadata!.labels?.clusterID ?? getClaim(mc, 'id.openshift.io'),\n openshiftVersion:\n mc.metadata!.labels?.openshiftVersion ??\n getClaim(mc, 'version.openshift.io'),\n platform: getClaim(mc, 'platform.open-cluster-management.io'),\n region: getClaim(mc, 'region.open-cluster-management.io'),\n allocatableResources: parseResources(mc.status?.allocatable || {}),\n availableResources: parseResources(mc.status?.capacity || {}),\n});\n\nexport const parseUpdateInfo = (clusterInfo: ManagedClusterInfo) => {\n const { availableUpdates, versionAvailableUpdates } =\n clusterInfo.status?.distributionInfo.ocp || {};\n\n if (\n !availableUpdates ||\n availableUpdates?.length === 0 ||\n !versionAvailableUpdates ||\n versionAvailableUpdates?.length === 0\n ) {\n return {\n update: {\n available: false,\n },\n };\n }\n\n const version = maxSatisfying(availableUpdates, '*');\n\n return {\n update: {\n available: true,\n version,\n url: versionAvailableUpdates[availableUpdates.indexOf(version as string)]\n ?.url,\n },\n };\n};\n\nexport const parseNodeStatus = (clusterInfo: ManagedClusterInfo) =>\n clusterInfo.status?.nodeList?.map(node => {\n if (node.conditions.length !== 1) {\n throw new Error('Found more node conditions then one');\n }\n const condition = node.conditions[0];\n return {\n status: condition.status,\n type: condition.type,\n } as ClusterNodesStatus;\n }) || [];\n\nexport const translateResourceToOCM = (\n clusterName: string,\n hubResourceName: string,\n) => (clusterName === hubResourceName ? HUB_CLUSTER_NAME_IN_OCM : clusterName);\n\nexport const translateOCMToResource = (\n clusterName: string,\n hubResourceName: string,\n) => (clusterName === HUB_CLUSTER_NAME_IN_OCM ? hubResourceName : clusterName);\n"],"names":["CONSOLE_CLAIM","maxSatisfying","HUB_CLUSTER_NAME_IN_OCM"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var bundle = require('./bundle.cjs.js');
6
+ var ManagedClusterProvider = require('./providers/ManagedClusterProvider.cjs.js');
7
+ var module$1 = require('./providers/module.cjs.js');
8
+ var router = require('./service/router.cjs.js');
9
+
10
+
11
+
12
+ exports.default = bundle.bundle;
13
+ exports.ManagedClusterProvider = ManagedClusterProvider.ManagedClusterProvider;
14
+ exports.catalogModuleOCMEntityProvider = module$1.catalogModuleOCMEntityProvider;
15
+ exports.ocmPlugin = router.ocmPlugin;
16
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;"}
@@ -0,0 +1,41 @@
1
+ import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
2
+ import { LoggerService, SchedulerServiceTaskRunner, SchedulerService } from '@backstage/backend-plugin-api';
3
+ import { Config } from '@backstage/config';
4
+ import { EntityProvider, EntityProviderConnection } from '@backstage/plugin-catalog-node';
5
+ import { CustomObjectsApi } from '@kubernetes/client-node';
6
+
7
+ declare const bundle: _backstage_backend_plugin_api.BackendFeature;
8
+
9
+ /**
10
+ * Provides OpenShift cluster resource entities from Open Cluster Management.
11
+ */
12
+ declare class ManagedClusterProvider implements EntityProvider {
13
+ protected readonly client: CustomObjectsApi;
14
+ protected readonly hubResourceName: string;
15
+ protected readonly id: string;
16
+ protected readonly owner: string;
17
+ protected readonly logger: LoggerService;
18
+ private readonly scheduleFn;
19
+ protected connection?: EntityProviderConnection;
20
+ protected constructor(client: CustomObjectsApi, hubResourceName: string, id: string, deps: {
21
+ logger: LoggerService;
22
+ }, owner: string, taskRunner: SchedulerServiceTaskRunner);
23
+ static fromConfig(deps: {
24
+ config: Config;
25
+ logger: LoggerService;
26
+ }, options: {
27
+ schedule: SchedulerServiceTaskRunner;
28
+ } | {
29
+ scheduler: SchedulerService;
30
+ }): ManagedClusterProvider[];
31
+ connect(connection: EntityProviderConnection): Promise<void>;
32
+ private createScheduleFn;
33
+ getProviderName(): string;
34
+ run(): Promise<void>;
35
+ }
36
+
37
+ declare const catalogModuleOCMEntityProvider: _backstage_backend_plugin_api.BackendFeature;
38
+
39
+ declare const ocmPlugin: _backstage_backend_plugin_api.BackendFeature;
40
+
41
+ export { ManagedClusterProvider, catalogModuleOCMEntityProvider, bundle as default, ocmPlugin };
@@ -0,0 +1,148 @@
1
+ 'use strict';
2
+
3
+ var catalogModel = require('@backstage/catalog-model');
4
+ var errors = require('@backstage/errors');
5
+ var pluginOcmCommon = require('@backstage-community/plugin-ocm-common');
6
+ var constants = require('../constants.cjs.js');
7
+ var config = require('../helpers/config.cjs.js');
8
+ var kubernetes = require('../helpers/kubernetes.cjs.js');
9
+ var parser = require('../helpers/parser.cjs.js');
10
+
11
+ class ManagedClusterProvider {
12
+ client;
13
+ hubResourceName;
14
+ id;
15
+ owner;
16
+ logger;
17
+ scheduleFn;
18
+ connection;
19
+ constructor(client, hubResourceName, id, deps, owner, taskRunner) {
20
+ this.client = client;
21
+ this.hubResourceName = hubResourceName;
22
+ this.id = id;
23
+ this.logger = deps.logger;
24
+ this.owner = owner;
25
+ this.scheduleFn = this.createScheduleFn(taskRunner);
26
+ }
27
+ static fromConfig(deps, options) {
28
+ const { config: config$1, logger } = deps;
29
+ return config.readOcmConfigs(config$1).map((providerConfig) => {
30
+ const client = kubernetes.hubApiClient(providerConfig, logger);
31
+ let taskRunner;
32
+ if ("scheduler" in options && providerConfig.schedule) {
33
+ taskRunner = options.scheduler.createScheduledTaskRunner(
34
+ providerConfig.schedule
35
+ );
36
+ } else if ("schedule" in options) {
37
+ taskRunner = options.schedule;
38
+ } else {
39
+ throw new errors.InputError(
40
+ `No schedule provided via config for OCMProvider:${providerConfig.id}.`
41
+ );
42
+ }
43
+ return new ManagedClusterProvider(
44
+ client,
45
+ providerConfig.hubResourceName,
46
+ providerConfig.id,
47
+ deps,
48
+ providerConfig.owner,
49
+ taskRunner
50
+ );
51
+ });
52
+ }
53
+ async connect(connection) {
54
+ this.connection = connection;
55
+ await this.scheduleFn();
56
+ }
57
+ createScheduleFn(taskRunner) {
58
+ return async () => {
59
+ return taskRunner.run({
60
+ id: `run_ocm_refresh_${this.getProviderName()}`,
61
+ fn: async () => {
62
+ try {
63
+ await this.run();
64
+ } catch (error) {
65
+ this.logger.error(
66
+ "Error while syncing cluster resources from Open Cluster Management",
67
+ {
68
+ // Default Error properties:
69
+ name: error.name,
70
+ message: error.message,
71
+ stack: error.stack,
72
+ // Additional status code if available:
73
+ status: error.response?.status
74
+ }
75
+ );
76
+ }
77
+ }
78
+ });
79
+ };
80
+ }
81
+ getProviderName() {
82
+ return `ocm-managed-cluster:${this.id}`;
83
+ }
84
+ async run() {
85
+ if (!this.connection) {
86
+ throw new Error("Not initialized");
87
+ }
88
+ this.logger.info(
89
+ `Providing OpenShift cluster resources from Open Cluster Management`
90
+ );
91
+ const hubConsole = parser.getClaim(
92
+ await kubernetes.getManagedCluster(this.client, constants.HUB_CLUSTER_NAME_IN_OCM),
93
+ constants.CONSOLE_CLAIM
94
+ );
95
+ const resources = (await kubernetes.listManagedClusters(this.client)).items.map((i) => {
96
+ const normalizedName = parser.translateOCMToResource(
97
+ i.metadata.name,
98
+ this.hubResourceName
99
+ );
100
+ return {
101
+ kind: "Resource",
102
+ apiVersion: "backstage.io/v1beta1",
103
+ metadata: {
104
+ name: normalizedName,
105
+ annotations: {
106
+ /**
107
+ * Can also be pulled from ManagedClusterInfo on .spec.masterEndpoint (details in discussion: https://github.com/janus-idp/backstage-plugins/pull/94#discussion_r1093228858)
108
+ */
109
+ [constants.ANNOTATION_KUBERNETES_API_SERVER]: i.spec?.managedClusterClientConfigs?.[0]?.url,
110
+ [pluginOcmCommon.ANNOTATION_CLUSTER_ID]: i.metadata?.labels?.clusterID,
111
+ [catalogModel.ANNOTATION_LOCATION]: this.getProviderName(),
112
+ [catalogModel.ANNOTATION_ORIGIN_LOCATION]: this.getProviderName(),
113
+ [pluginOcmCommon.ANNOTATION_PROVIDER_ID]: this.id
114
+ },
115
+ links: [
116
+ {
117
+ url: parser.getClaim(i, constants.CONSOLE_CLAIM),
118
+ title: "OpenShift Console",
119
+ icon: "dashboard"
120
+ },
121
+ {
122
+ url: `${hubConsole}/multicloud/infrastructure/clusters/details/${i.metadata.name}/`,
123
+ title: "OCM Console"
124
+ },
125
+ {
126
+ url: `https://console.redhat.com/openshift/details/s/${i.metadata.labels.clusterID}`,
127
+ title: "OpenShift Cluster Manager"
128
+ }
129
+ ]
130
+ },
131
+ spec: {
132
+ owner: this.owner,
133
+ type: "kubernetes-cluster"
134
+ }
135
+ };
136
+ });
137
+ await this.connection.applyMutation({
138
+ type: "full",
139
+ entities: resources.map((entity) => ({
140
+ entity,
141
+ locationKey: this.getProviderName()
142
+ }))
143
+ });
144
+ }
145
+ }
146
+
147
+ exports.ManagedClusterProvider = ManagedClusterProvider;
148
+ //# sourceMappingURL=ManagedClusterProvider.cjs.js.map