@jfvilas/plugin-kwirth-backend 0.13.5 → 0.14.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/dist/index.cjs.js +4 -580
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/model/KwirthStaticData.cjs.js +11 -0
- package/dist/model/KwirthStaticData.cjs.js.map +1 -0
- package/dist/plugin.cjs.js +36 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/service/config.cjs.js +215 -0
- package/dist/service/config.cjs.js.map +1 -0
- package/dist/service/permissions.cjs.js +108 -0
- package/dist/service/permissions.cjs.js.map +1 -0
- package/dist/service/router.cjs.js +244 -0
- package/dist/service/router.cjs.js.map +1 -0
- package/dist/version.cjs.js +6 -0
- package/dist/version.cjs.js.map +1 -0
- package/package.json +21 -11
package/dist/index.cjs.js
CHANGED
|
@@ -2,587 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var catalogClient = require('@backstage/catalog-client');
|
|
8
|
-
var pluginKwirthCommon = require('@jfvilas/plugin-kwirth-common');
|
|
9
|
-
var kwirthCommon = require('@jfvilas/kwirth-common');
|
|
10
|
-
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
5
|
+
var router = require('./service/router.cjs.js');
|
|
6
|
+
var plugin = require('./plugin.cjs.js');
|
|
11
7
|
|
|
12
|
-
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
13
8
|
|
|
14
|
-
var express__default = /*#__PURE__*/_interopDefaultCompat(express);
|
|
15
|
-
var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
|
|
16
9
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
static clusterKwirthData = /* @__PURE__ */ new Map();
|
|
20
|
-
static latestVersions;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const loadNamespacePermissions = (channelConfig, logger) => {
|
|
24
|
-
let namespacePermissions = [];
|
|
25
|
-
if (channelConfig.has("namespacePermissions")) {
|
|
26
|
-
logger.info(` Namespace permisson evaluation will be performed.`);
|
|
27
|
-
let permNamespaces = channelConfig.getOptionalConfigArray("namespacePermissions");
|
|
28
|
-
for (let ns of permNamespaces) {
|
|
29
|
-
let namespace = ns.keys()[0];
|
|
30
|
-
let identityRefs = ns.getStringArray(namespace);
|
|
31
|
-
identityRefs = identityRefs.map((g) => g.toLowerCase());
|
|
32
|
-
namespacePermissions.push({ namespace, identityRefs });
|
|
33
|
-
}
|
|
34
|
-
} else {
|
|
35
|
-
logger.info(` No namespace restrictions.`);
|
|
36
|
-
namespacePermissions = [];
|
|
37
|
-
}
|
|
38
|
-
return namespacePermissions;
|
|
39
|
-
};
|
|
40
|
-
const loadPodRules = (config, category) => {
|
|
41
|
-
let rules = [];
|
|
42
|
-
for (let rule of config.getConfigArray(category)) {
|
|
43
|
-
let podsStringArray = rule.getOptionalStringArray("pods") || [".*"];
|
|
44
|
-
let podsRegexArray = [];
|
|
45
|
-
for (let expr of podsStringArray) {
|
|
46
|
-
podsRegexArray.push(new RegExp(expr));
|
|
47
|
-
}
|
|
48
|
-
let refsStringArray = rule.getOptionalStringArray("refs") || [".*"];
|
|
49
|
-
let refsRegexArray = [];
|
|
50
|
-
for (let expr of refsStringArray) {
|
|
51
|
-
refsRegexArray.push(new RegExp(expr));
|
|
52
|
-
}
|
|
53
|
-
let prr = {
|
|
54
|
-
pods: podsRegexArray,
|
|
55
|
-
refs: refsRegexArray
|
|
56
|
-
};
|
|
57
|
-
rules.push(prr);
|
|
58
|
-
}
|
|
59
|
-
return rules;
|
|
60
|
-
};
|
|
61
|
-
const loadPodPermissions = (channelConfig, logger) => {
|
|
62
|
-
let clusterPodPermissions = [];
|
|
63
|
-
if (channelConfig.has("podPermissions")) {
|
|
64
|
-
logger.info(` Pod permisson evaluation will be performed.`);
|
|
65
|
-
let namespaceList = channelConfig.getConfigArray("podPermissions");
|
|
66
|
-
for (let ns of namespaceList) {
|
|
67
|
-
let namespaceName = ns.keys()[0];
|
|
68
|
-
let podPermissions = { namespace: namespaceName };
|
|
69
|
-
if (ns.getConfig(namespaceName).has("allow")) {
|
|
70
|
-
podPermissions.allow = loadPodRules(ns.getConfig(namespaceName), "allow");
|
|
71
|
-
if (ns.getConfig(namespaceName).has("except")) podPermissions.except = loadPodRules(ns.getConfig(namespaceName), "except");
|
|
72
|
-
if (ns.getConfig(namespaceName).has("deny")) podPermissions.deny = loadPodRules(ns.getConfig(namespaceName), "deny");
|
|
73
|
-
if (ns.getConfig(namespaceName).has("unless")) podPermissions.unless = loadPodRules(ns.getConfig(namespaceName), "unless");
|
|
74
|
-
} else {
|
|
75
|
-
podPermissions.allow = [];
|
|
76
|
-
podPermissions.allow.push({
|
|
77
|
-
pods: [new RegExp(".*")],
|
|
78
|
-
refs: [new RegExp(".*")]
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
clusterPodPermissions.push(podPermissions);
|
|
82
|
-
}
|
|
83
|
-
} else {
|
|
84
|
-
logger.info(` No pod permissions will be applied.`);
|
|
85
|
-
}
|
|
86
|
-
return clusterPodPermissions;
|
|
87
|
-
};
|
|
88
|
-
const addChannelPermissions = (channel, logger, cluster, kdata) => {
|
|
89
|
-
let keyname = "kwirth" + channel;
|
|
90
|
-
let keyCamelName = "kwirth" + channel[0].toUpperCase() + channel.substring(1);
|
|
91
|
-
if (cluster.has(keyCamelName)) keyname = keyCamelName;
|
|
92
|
-
if (cluster.has(keyname)) {
|
|
93
|
-
logger.info(`Load permissions for channel ${channel} (config: ${keyname}).`);
|
|
94
|
-
let configChannel = cluster.getConfig(keyname);
|
|
95
|
-
if (configChannel.has("namespacePermissions")) {
|
|
96
|
-
logger.info(` Loading namespace permissions.`);
|
|
97
|
-
kdata.namespacePermissions.set(channel, loadNamespacePermissions(configChannel, logger));
|
|
98
|
-
} else {
|
|
99
|
-
logger.info(` No namespace permissions.`);
|
|
100
|
-
kdata.namespacePermissions.set(channel, []);
|
|
101
|
-
}
|
|
102
|
-
if (configChannel.has("podPermissions")) {
|
|
103
|
-
logger.info(` Loading pod permissions.`);
|
|
104
|
-
kdata.podPermissions.set(channel, loadPodPermissions(configChannel, logger));
|
|
105
|
-
} else {
|
|
106
|
-
logger.info(` No pod permissions.`);
|
|
107
|
-
kdata.podPermissions.set(channel, []);
|
|
108
|
-
}
|
|
109
|
-
} else {
|
|
110
|
-
logger.info(`Cluster ${cluster.getString("name")} will have no channel '${channel}' restrictions.`);
|
|
111
|
-
kdata.namespacePermissions.set(channel, []);
|
|
112
|
-
kdata.podPermissions.set(channel, []);
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
const loadClusters = async (logger, config) => {
|
|
116
|
-
KwirthStaticData.clusterKwirthData.clear();
|
|
117
|
-
let locatingMethods = config.getConfigArray("kubernetes.clusterLocatorMethods");
|
|
118
|
-
for (let method of locatingMethods) {
|
|
119
|
-
let clusters = method.getConfigArray("clusters");
|
|
120
|
-
for (let cluster of clusters) {
|
|
121
|
-
let name = cluster.getString("name");
|
|
122
|
-
if (cluster.has("kwirthHome") && cluster.has("kwirthApiKey")) {
|
|
123
|
-
let kwirthHome = cluster.getOptionalString("kwirthHome");
|
|
124
|
-
let kwirthApiKey = cluster.getOptionalString("kwirthApiKey");
|
|
125
|
-
let title = cluster.has("title") ? cluster.getString("title") : "No name";
|
|
126
|
-
let kwirthClusterData = {
|
|
127
|
-
name,
|
|
128
|
-
kwirthHome,
|
|
129
|
-
kwirthApiKey,
|
|
130
|
-
kwirthData: {
|
|
131
|
-
version: "",
|
|
132
|
-
clusterName: "",
|
|
133
|
-
inCluster: false,
|
|
134
|
-
namespace: "",
|
|
135
|
-
deployment: "",
|
|
136
|
-
lastVersion: "",
|
|
137
|
-
clusterType: kwirthCommon.ClusterTypeEnum.KUBERNETES,
|
|
138
|
-
metricsInterval: 0,
|
|
139
|
-
channels: []
|
|
140
|
-
},
|
|
141
|
-
title,
|
|
142
|
-
namespacePermissions: /* @__PURE__ */ new Map(),
|
|
143
|
-
podPermissions: /* @__PURE__ */ new Map(),
|
|
144
|
-
enabled: false
|
|
145
|
-
};
|
|
146
|
-
logger.info(`Kwirth for ${name} is located at ${kwirthClusterData.kwirthHome}. Testing connection...`);
|
|
147
|
-
let enableCluster = false;
|
|
148
|
-
try {
|
|
149
|
-
let response = await fetch(kwirthClusterData.kwirthHome + "/config/info");
|
|
150
|
-
try {
|
|
151
|
-
let data = await response.text();
|
|
152
|
-
try {
|
|
153
|
-
let kwirthData = JSON.parse(data);
|
|
154
|
-
logger.info(`Kwirth info at cluster '${kwirthClusterData.name}': ${JSON.stringify(kwirthData)}`);
|
|
155
|
-
kwirthClusterData.kwirthData = kwirthData;
|
|
156
|
-
if (kwirthCommon.versionGreatOrEqualThan(kwirthData.version, MIN_KWIRTH_VERSION)) {
|
|
157
|
-
enableCluster = true;
|
|
158
|
-
} else {
|
|
159
|
-
logger.error(`Unsupported Kwirth version on cluster '${name}' (${title}) [${kwirthData.version}]. Min version is ${MIN_KWIRTH_VERSION}`);
|
|
160
|
-
}
|
|
161
|
-
} catch (err) {
|
|
162
|
-
logger.error(`Kwirth at cluster ${kwirthClusterData.name} returned errors: ${err}`);
|
|
163
|
-
logger.info("Returned data is:");
|
|
164
|
-
logger.info(data);
|
|
165
|
-
kwirthClusterData.kwirthData = {
|
|
166
|
-
version: "0.0.0",
|
|
167
|
-
clusterName: "unknown",
|
|
168
|
-
inCluster: false,
|
|
169
|
-
namespace: "unknown",
|
|
170
|
-
deployment: "unknown",
|
|
171
|
-
lastVersion: "0.0.0",
|
|
172
|
-
clusterType: kwirthCommon.ClusterTypeEnum.KUBERNETES,
|
|
173
|
-
metricsInterval: 0,
|
|
174
|
-
channels: []
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
} catch (err) {
|
|
178
|
-
logger.warn(`Error parsing version response from cluster '${kwirthClusterData.name}': ${err}`);
|
|
179
|
-
}
|
|
180
|
-
} catch (err) {
|
|
181
|
-
logger.info(`Kwirth access error: ${err}.`);
|
|
182
|
-
logger.warn(`Kwirth home URL (${kwirthClusterData.kwirthHome}) at cluster '${kwirthClusterData.name}' cannot be accessed right now.`);
|
|
183
|
-
}
|
|
184
|
-
if (enableCluster) {
|
|
185
|
-
[
|
|
186
|
-
kwirthCommon.InstanceMessageChannelEnum.LOG,
|
|
187
|
-
kwirthCommon.InstanceMessageChannelEnum.ALERT,
|
|
188
|
-
"fileman",
|
|
189
|
-
kwirthCommon.InstanceMessageChannelEnum.METRICS
|
|
190
|
-
].map((channel) => addChannelPermissions(channel, logger, cluster, kwirthClusterData));
|
|
191
|
-
KwirthStaticData.clusterKwirthData.set(name, kwirthClusterData);
|
|
192
|
-
} else {
|
|
193
|
-
logger.warn(`Cluster ${name} will be disabled`);
|
|
194
|
-
}
|
|
195
|
-
} else {
|
|
196
|
-
logger.warn(`Cluster ${name} has no Kwirth information (kwirthHome and kwirthApiKey are missing).`);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
logger.info("Kwirth static data has been set including following clusters:");
|
|
201
|
-
for (let c of KwirthStaticData.clusterKwirthData.keys()) {
|
|
202
|
-
logger.info(" " + c);
|
|
203
|
-
}
|
|
204
|
-
for (let c of KwirthStaticData.clusterKwirthData.keys()) {
|
|
205
|
-
console.log(KwirthStaticData.clusterKwirthData.get(c));
|
|
206
|
-
}
|
|
207
|
-
};
|
|
208
|
-
const loadKwirthInfo = async (logger) => {
|
|
209
|
-
try {
|
|
210
|
-
let pkb = await (await fetch("https://registry.npmjs.org/@jfvilas/plugin-kwirth-backend")).json();
|
|
211
|
-
let pkl = await (await fetch("https://registry.npmjs.org/@jfvilas/plugin-kwirth-log")).json();
|
|
212
|
-
let pkm = await (await fetch("https://registry.npmjs.org/@jfvilas/plugin-kwirth-metrics")).json();
|
|
213
|
-
let hubResp = await (await fetch("https://hub.docker.com/v2/repositories/jfvilasoutlook/kwirth/tags?page_size=25&page=1&ordering=last_updated&name=")).json();
|
|
214
|
-
KwirthStaticData.latestVersions = {
|
|
215
|
-
["plugin-kwirth-backend"]: pkb["dist-tags"].latest,
|
|
216
|
-
["plugin-kwirth-log"]: pkl["dist-tags"].latest,
|
|
217
|
-
["plugin-kwirth-metrics"]: pkm["dist-tags"].latest,
|
|
218
|
-
kwirth: hubResp.results[0].name
|
|
219
|
-
};
|
|
220
|
-
logger.info("Latest Kwirth-related artifacts versions are:");
|
|
221
|
-
logger.info(JSON.stringify(KwirthStaticData.latestVersions));
|
|
222
|
-
} catch (err) {
|
|
223
|
-
console.log(err);
|
|
224
|
-
logger.warn("Oops! We couldn't fetch the latest version info. Don't worry \u2014 it's not critical. Kwirth's still here for you.");
|
|
225
|
-
}
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
const checkNamespaceAccess = (channel, cluster, podData, userEntityRef, userGroups) => {
|
|
229
|
-
let allowedToNamespace = false;
|
|
230
|
-
let namespacePermissions = KwirthStaticData.clusterKwirthData.get(cluster.name)?.namespacePermissions;
|
|
231
|
-
if (namespacePermissions?.has(channel)) {
|
|
232
|
-
let rule = namespacePermissions?.get(channel).find((ns) => ns.namespace === podData.namespace);
|
|
233
|
-
if (rule) {
|
|
234
|
-
if (rule.identityRefs.includes(userEntityRef.toLowerCase())) {
|
|
235
|
-
allowedToNamespace = true;
|
|
236
|
-
} else {
|
|
237
|
-
var groupResult = rule.identityRefs.some((identityRef) => userGroups.includes(identityRef));
|
|
238
|
-
if (groupResult) {
|
|
239
|
-
allowedToNamespace = true;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
} else {
|
|
243
|
-
allowedToNamespace = true;
|
|
244
|
-
}
|
|
245
|
-
} else {
|
|
246
|
-
console.log(`Invalid channel: ${channel}`);
|
|
247
|
-
}
|
|
248
|
-
return allowedToNamespace;
|
|
249
|
-
};
|
|
250
|
-
const checkPodPermissionRule = (ppr, entityName, userEntityRef, userGroups) => {
|
|
251
|
-
var refMatch = false;
|
|
252
|
-
for (var podNameRegex of ppr.pods) {
|
|
253
|
-
if (podNameRegex.test(entityName)) {
|
|
254
|
-
for (var refRegex of ppr.refs) {
|
|
255
|
-
refMatch = refRegex.test(userEntityRef.toLowerCase());
|
|
256
|
-
if (refMatch) {
|
|
257
|
-
break;
|
|
258
|
-
} else {
|
|
259
|
-
refMatch = userGroups.some((g) => refRegex.test(g));
|
|
260
|
-
if (refMatch) {
|
|
261
|
-
break;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
if (refMatch) break;
|
|
267
|
-
}
|
|
268
|
-
return refMatch;
|
|
269
|
-
};
|
|
270
|
-
const getPodPermissionSet = (channel, cluster) => {
|
|
271
|
-
if (cluster.podPermissions.has(channel)) {
|
|
272
|
-
return cluster.podPermissions.get(channel);
|
|
273
|
-
} else {
|
|
274
|
-
console.log(`Invalid channel ${channel} for permission set`);
|
|
275
|
-
return void 0;
|
|
276
|
-
}
|
|
277
|
-
};
|
|
278
|
-
const checkPodAccess = (reqPod, podPermissionSet, entityName, userEntityRef, userGroups) => {
|
|
279
|
-
for (var podPermission of podPermissionSet.filter((pp) => pp.namespace === reqPod.namespace)) {
|
|
280
|
-
if (podPermission.allow) {
|
|
281
|
-
var allowMatches = false;
|
|
282
|
-
var exceptMatches = false;
|
|
283
|
-
for (var prr of podPermission.allow) {
|
|
284
|
-
allowMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups);
|
|
285
|
-
}
|
|
286
|
-
if (allowMatches) {
|
|
287
|
-
if (podPermission.except) {
|
|
288
|
-
for (var prr of podPermission.except) {
|
|
289
|
-
exceptMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups);
|
|
290
|
-
if (exceptMatches) {
|
|
291
|
-
break;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
if (allowMatches && !exceptMatches) {
|
|
297
|
-
if (podPermission.deny) {
|
|
298
|
-
var denyMatches = false;
|
|
299
|
-
var unlessMatches = false;
|
|
300
|
-
for (var prr of podPermission.deny) {
|
|
301
|
-
denyMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups);
|
|
302
|
-
if (denyMatches) {
|
|
303
|
-
break;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
if (denyMatches && podPermission.unless) {
|
|
307
|
-
for (var prr of podPermission.unless) {
|
|
308
|
-
unlessMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups);
|
|
309
|
-
if (unlessMatches) {
|
|
310
|
-
break;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
if (!denyMatches || denyMatches && unlessMatches) {
|
|
315
|
-
return true;
|
|
316
|
-
}
|
|
317
|
-
} else {
|
|
318
|
-
return true;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
} else {
|
|
322
|
-
return true;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
return false;
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
const VERSION = "0.13.5";
|
|
329
|
-
|
|
330
|
-
async function createRouter(options) {
|
|
331
|
-
const { configSvc, loggerSvc, userInfoSvc, authSvc, httpAuthSvc, discoverySvc } = options;
|
|
332
|
-
loggerSvc.info("Loading static config");
|
|
333
|
-
if (!configSvc.has("kubernetes.clusterLocatorMethods")) {
|
|
334
|
-
loggerSvc.error(`Kwirth will not start, there is no 'clusterLocatorMethods' defined in app-config.`);
|
|
335
|
-
throw new Error("Kwirth backend will not be available.");
|
|
336
|
-
}
|
|
337
|
-
try {
|
|
338
|
-
loadClusters(loggerSvc, configSvc);
|
|
339
|
-
loadKwirthInfo(loggerSvc);
|
|
340
|
-
} catch (err) {
|
|
341
|
-
let txt = `Errors detected reading static configuration: ${err}`;
|
|
342
|
-
loggerSvc.error(txt);
|
|
343
|
-
throw new Error(txt);
|
|
344
|
-
}
|
|
345
|
-
if (configSvc.subscribe) {
|
|
346
|
-
configSvc.subscribe(() => {
|
|
347
|
-
try {
|
|
348
|
-
loggerSvc.warn("Change detected on app-config, Kwirth will update config.");
|
|
349
|
-
loadClusters(loggerSvc, configSvc);
|
|
350
|
-
} catch (err) {
|
|
351
|
-
loggerSvc.error(`Errors detected reading new configuration: ${err}`);
|
|
352
|
-
}
|
|
353
|
-
});
|
|
354
|
-
} else {
|
|
355
|
-
loggerSvc.info("Kwirth cannot subscribe to config changes.");
|
|
356
|
-
}
|
|
357
|
-
const router = Router__default.default();
|
|
358
|
-
router.use(express__default.default.json());
|
|
359
|
-
const createAuthFetchApi = (token) => {
|
|
360
|
-
return {
|
|
361
|
-
fetch: async (input, init) => {
|
|
362
|
-
init = init || {};
|
|
363
|
-
init.headers = {
|
|
364
|
-
...init.headers,
|
|
365
|
-
Authorization: `Bearer ${token}`
|
|
366
|
-
};
|
|
367
|
-
return fetch(input, init);
|
|
368
|
-
}
|
|
369
|
-
};
|
|
370
|
-
};
|
|
371
|
-
const getValidClustersFromEntity = async (entity) => {
|
|
372
|
-
let clusterList = [];
|
|
373
|
-
for (const clusterName of KwirthStaticData.clusterKwirthData.keys()) {
|
|
374
|
-
let url = KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthHome;
|
|
375
|
-
let apiKeyStr = KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthApiKey;
|
|
376
|
-
let title = KwirthStaticData.clusterKwirthData.get(clusterName)?.title;
|
|
377
|
-
let clusterVersion = KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthData.version || "0.0.0";
|
|
378
|
-
let queryUrl = void 0;
|
|
379
|
-
if (entity.metadata.annotations["backstage.io/kubernetes-id"]) {
|
|
380
|
-
queryUrl = url + `/managecluster/find?label=backstage.io%2fkubernetes-id&entity=${entity.metadata.annotations["backstage.io/kubernetes-id"]}&type=pod&data=containers`;
|
|
381
|
-
} else if (entity.metadata.annotations["backstage.io/kubernetes-label-selector"]) {
|
|
382
|
-
if (kwirthCommon.versionGreaterThan(clusterVersion, "0.4.40")) {
|
|
383
|
-
let escapedLabelSelector = encodeURIComponent(entity.metadata.annotations["backstage.io/kubernetes-label-selector"]);
|
|
384
|
-
queryUrl = url + `/managecluster/find?labelselector=${escapedLabelSelector}&type=pod&data=containers`;
|
|
385
|
-
} else {
|
|
386
|
-
loggerSvc.error(`Version ${clusterVersion} from cluster ${clusterName} is not valid for using ${pluginKwirthCommon.ANNOTATION_BACKSTAGE_KUBERNETES_LABELSELECTOR}`);
|
|
387
|
-
clusterList.push({ name: clusterName, url, title, pods: [], accessKeys: /* @__PURE__ */ new Map() });
|
|
388
|
-
continue;
|
|
389
|
-
}
|
|
390
|
-
} else {
|
|
391
|
-
loggerSvc.error("Received request without labelid/labelselector");
|
|
392
|
-
clusterList.push({ name: clusterName, url, title, pods: [], accessKeys: /* @__PURE__ */ new Map() });
|
|
393
|
-
continue;
|
|
394
|
-
}
|
|
395
|
-
try {
|
|
396
|
-
let fetchResp = await fetch(queryUrl, { headers: { "Authorization": "Bearer " + apiKeyStr } });
|
|
397
|
-
if (fetchResp.status === 200) {
|
|
398
|
-
let jsonResp = await fetchResp.json();
|
|
399
|
-
console.log("jsonResp");
|
|
400
|
-
console.log(jsonResp);
|
|
401
|
-
if (jsonResp) {
|
|
402
|
-
let podData = {
|
|
403
|
-
name: clusterName,
|
|
404
|
-
url,
|
|
405
|
-
title,
|
|
406
|
-
pods: jsonResp,
|
|
407
|
-
accessKeys: /* @__PURE__ */ new Map()
|
|
408
|
-
};
|
|
409
|
-
clusterList.push(podData);
|
|
410
|
-
} else {
|
|
411
|
-
loggerSvc.warn(`Invalid data received from cluster ${clusterName}`);
|
|
412
|
-
clusterList.push({ name: clusterName, url, title, pods: [], accessKeys: /* @__PURE__ */ new Map() });
|
|
413
|
-
}
|
|
414
|
-
} else {
|
|
415
|
-
loggerSvc.warn(`Invalid response from cluster ${clusterName}: ${fetchResp.status}`);
|
|
416
|
-
let text = await fetchResp.text();
|
|
417
|
-
if (text) loggerSvc.warn(text);
|
|
418
|
-
clusterList.push({ name: clusterName, url, title, pods: [], accessKeys: /* @__PURE__ */ new Map() });
|
|
419
|
-
}
|
|
420
|
-
} catch (err) {
|
|
421
|
-
loggerSvc.warn(`Cannot access cluster ${clusterName} (URL: ${queryUrl}): ${err}`);
|
|
422
|
-
clusterList.push({ name: clusterName, url, title, pods: [], accessKeys: /* @__PURE__ */ new Map() });
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
return clusterList;
|
|
426
|
-
};
|
|
427
|
-
const createAccessKey = async (reqScope, cluster, reqPods, userName) => {
|
|
428
|
-
let resources = reqPods.map((podData) => `${reqScope}:${podData.namespace}::${podData.name}:`).join(";");
|
|
429
|
-
let kwirthHome = KwirthStaticData.clusterKwirthData.get(cluster.name)?.kwirthHome;
|
|
430
|
-
let kwirthApiKey = KwirthStaticData.clusterKwirthData.get(cluster.name)?.kwirthApiKey;
|
|
431
|
-
let payload = {
|
|
432
|
-
description: `Backstage API key for user ${userName}`,
|
|
433
|
-
expire: Date.now() + 60 * 60 * 1e3,
|
|
434
|
-
days: 1,
|
|
435
|
-
accessKey: {
|
|
436
|
-
id: "",
|
|
437
|
-
type: "bearer",
|
|
438
|
-
resources
|
|
439
|
-
}
|
|
440
|
-
};
|
|
441
|
-
let fetchResp = await fetch(kwirthHome + "/key", { method: "POST", body: JSON.stringify(payload), headers: { "Content-Type": "application/json", Authorization: "Bearer " + kwirthApiKey } });
|
|
442
|
-
if (fetchResp.status === 200) {
|
|
443
|
-
let data = await fetchResp.json();
|
|
444
|
-
return data.accessKey;
|
|
445
|
-
} else {
|
|
446
|
-
loggerSvc.warn(`Invalid response asking for a key from cluster ${cluster.name}: ${fetchResp.status}`);
|
|
447
|
-
return void 0;
|
|
448
|
-
}
|
|
449
|
-
};
|
|
450
|
-
const addAccessKeys = async (channel, reqScope, foundClusters, entityName, userEntityRef, userGroups) => {
|
|
451
|
-
if (!reqScope) {
|
|
452
|
-
loggerSvc.info(`Invalid scope requested: ${reqScope}`);
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
let principal = userEntityRef.split(":")[1];
|
|
456
|
-
let username = principal.split("/")[1];
|
|
457
|
-
for (let foundCluster of foundClusters) {
|
|
458
|
-
let podList = [];
|
|
459
|
-
if (!KwirthStaticData.clusterKwirthData.get(foundCluster.name)?.kwirthData.channels.some((c) => c.id === channel)) {
|
|
460
|
-
loggerSvc.warn(`Cluster ${foundCluster.name} does not implement channel ${channel} (requested scope: ${reqScope})`);
|
|
461
|
-
continue;
|
|
462
|
-
}
|
|
463
|
-
for (let podData of foundCluster.pods) {
|
|
464
|
-
let allowedToNamespace = checkNamespaceAccess(channel, foundCluster, podData, userEntityRef, userGroups);
|
|
465
|
-
if (allowedToNamespace) {
|
|
466
|
-
let clusterDef = KwirthStaticData.clusterKwirthData.get(foundCluster.name);
|
|
467
|
-
let podPermissionSet = getPodPermissionSet(channel, clusterDef);
|
|
468
|
-
if (!podPermissionSet) {
|
|
469
|
-
loggerSvc.warn(`Pod permission set not found: ${channel}`);
|
|
470
|
-
continue;
|
|
471
|
-
}
|
|
472
|
-
let namespaceRestricted = podPermissionSet.some((pp) => pp.namespace === podData.namespace);
|
|
473
|
-
if (!namespaceRestricted || checkPodAccess(podData, podPermissionSet, entityName, userEntityRef, userGroups)) {
|
|
474
|
-
podList.push(podData);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
if (podList.length > 0) {
|
|
479
|
-
let accessKey = await createAccessKey(reqScope, foundCluster, podList, username);
|
|
480
|
-
if (accessKey) foundCluster.accessKeys.set(reqScope, accessKey);
|
|
481
|
-
} else {
|
|
482
|
-
loggerSvc.info(`No pods on podList for '${reqScope}' on channel '${channel}' in cluster '${foundCluster.name}' for searching for entity: '${entityName}'`);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
};
|
|
486
|
-
const getUserGroups = async (userInfo) => {
|
|
487
|
-
const { token } = await authSvc.getPluginRequestToken({
|
|
488
|
-
onBehalfOf: await authSvc.getOwnServiceCredentials(),
|
|
489
|
-
targetPluginId: "catalog"
|
|
490
|
-
});
|
|
491
|
-
const catalogClient$1 = new catalogClient.CatalogClient({
|
|
492
|
-
discoveryApi: discoverySvc,
|
|
493
|
-
fetchApi: createAuthFetchApi(token)
|
|
494
|
-
});
|
|
495
|
-
const entity = await catalogClient$1.getEntityByRef(userInfo.userEntityRef);
|
|
496
|
-
let userGroupsRefs = [];
|
|
497
|
-
if (entity?.spec.memberOf) userGroupsRefs = entity?.spec.memberOf;
|
|
498
|
-
return userGroupsRefs;
|
|
499
|
-
};
|
|
500
|
-
const processVersion = async (_req, res) => {
|
|
501
|
-
res.status(200).send({ version: VERSION });
|
|
502
|
-
};
|
|
503
|
-
const processInfo = async (_req, res) => {
|
|
504
|
-
res.status(200).send(
|
|
505
|
-
KwirthStaticData.latestVersions
|
|
506
|
-
);
|
|
507
|
-
};
|
|
508
|
-
const processAccess = async (req, res) => {
|
|
509
|
-
if (!req.query["scopes"] || !req.query["channel"]) {
|
|
510
|
-
res.status(400).send(`'scopes' and 'channel' are required`);
|
|
511
|
-
return;
|
|
512
|
-
}
|
|
513
|
-
let reqScopes = req.query["scopes"].toString().split(",");
|
|
514
|
-
let reqChannel = req.query["channel"]?.toString();
|
|
515
|
-
const credentials = await httpAuthSvc.credentials(req, { allow: ["user"] });
|
|
516
|
-
const userInfo = await userInfoSvc.getUserInfo(credentials);
|
|
517
|
-
let userGroupsRefs = await getUserGroups(userInfo);
|
|
518
|
-
loggerSvc.info(`Checking reqScopes '${req.query["scopes"]}' scopes for working with pod: '${req.body.metadata.namespace + "/" + req.body.metadata.name}' for user '${userInfo.userEntityRef}'`);
|
|
519
|
-
let foundClusters = await getValidClustersFromEntity(req.body);
|
|
520
|
-
for (let reqScopeStr of reqScopes) {
|
|
521
|
-
let reqScope = reqScopeStr;
|
|
522
|
-
await addAccessKeys(reqChannel, reqScope, foundClusters, req.body.metadata.name, userInfo.userEntityRef, userGroupsRefs);
|
|
523
|
-
if (reqScope === kwirthCommon.InstanceConfigScopeEnum.STREAM) {
|
|
524
|
-
for (let cluster of foundClusters) {
|
|
525
|
-
let accessKey = cluster.accessKeys.get(kwirthCommon.InstanceConfigScopeEnum.STREAM);
|
|
526
|
-
if (accessKey) {
|
|
527
|
-
let url = cluster.url + "/metrics";
|
|
528
|
-
let auth = "Bearer " + kwirthCommon.accessKeySerialize(accessKey);
|
|
529
|
-
let fetchResp = await fetch(url, { headers: { "Authorization": auth } });
|
|
530
|
-
try {
|
|
531
|
-
let data = await fetchResp.json();
|
|
532
|
-
cluster.metrics = data;
|
|
533
|
-
} catch (err) {
|
|
534
|
-
loggerSvc.error(`Cannot get metrics on cluster ${cluster.name}: ` + err);
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
for (let c of foundClusters) {
|
|
541
|
-
c.accessKeys = JSON.stringify(Array.from(c.accessKeys.entries()));
|
|
542
|
-
}
|
|
543
|
-
res.status(200).send(foundClusters);
|
|
544
|
-
};
|
|
545
|
-
router.post(["/access"], (req, res) => {
|
|
546
|
-
processAccess(req, res);
|
|
547
|
-
});
|
|
548
|
-
router.get(["/version"], (req, res) => {
|
|
549
|
-
processVersion(req, res);
|
|
550
|
-
});
|
|
551
|
-
router.get(["/info"], (req, res) => {
|
|
552
|
-
processInfo(req, res);
|
|
553
|
-
});
|
|
554
|
-
return router;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
const kwirthPlugin = backendPluginApi.createBackendPlugin({
|
|
558
|
-
pluginId: "kwirth",
|
|
559
|
-
register(env) {
|
|
560
|
-
env.registerInit({
|
|
561
|
-
deps: {
|
|
562
|
-
discovery: backendPluginApi.coreServices.discovery,
|
|
563
|
-
config: backendPluginApi.coreServices.rootConfig,
|
|
564
|
-
logger: backendPluginApi.coreServices.logger,
|
|
565
|
-
auth: backendPluginApi.coreServices.auth,
|
|
566
|
-
httpAuth: backendPluginApi.coreServices.httpAuth,
|
|
567
|
-
httpRouter: backendPluginApi.coreServices.httpRouter,
|
|
568
|
-
userInfo: backendPluginApi.coreServices.userInfo
|
|
569
|
-
},
|
|
570
|
-
async init({ discovery, config, httpRouter, logger, auth, httpAuth, userInfo }) {
|
|
571
|
-
httpRouter.use(
|
|
572
|
-
await createRouter({
|
|
573
|
-
discoverySvc: discovery,
|
|
574
|
-
configSvc: config,
|
|
575
|
-
loggerSvc: logger,
|
|
576
|
-
authSvc: auth,
|
|
577
|
-
httpAuthSvc: httpAuth,
|
|
578
|
-
userInfoSvc: userInfo
|
|
579
|
-
})
|
|
580
|
-
);
|
|
581
|
-
}
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
});
|
|
585
|
-
|
|
586
|
-
exports.createRouter = createRouter;
|
|
587
|
-
exports.default = kwirthPlugin;
|
|
10
|
+
exports.createRouter = router.createRouter;
|
|
11
|
+
exports.default = plugin.kwirthPlugin;
|
|
588
12
|
//# sourceMappingURL=index.cjs.js.map
|