mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Updated decc manager
This commit is contained in:
parent
526a38422c
commit
bbcf89bb1e
@ -19,6 +19,7 @@ export interface UserStoreModel {
|
|||||||
seenContexts: string[];
|
seenContexts: string[];
|
||||||
preferences: UserPreferences;
|
preferences: UserPreferences;
|
||||||
token: Token;
|
token: Token;
|
||||||
|
lastLoggedInUser: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserPreferences {
|
export interface UserPreferences {
|
||||||
@ -113,6 +114,8 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
refreshToken: ""
|
refreshToken: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@observable lastLoggedInUser = "";
|
||||||
|
|
||||||
get isNewVersion() {
|
get isNewVersion() {
|
||||||
return semver.gt(getAppVersion(), this.lastSeenAppVersion);
|
return semver.gt(getAppVersion(), this.lastSeenAppVersion);
|
||||||
}
|
}
|
||||||
@ -188,8 +191,11 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
|
|
||||||
isTokenExpired(validTill: number): boolean {
|
isTokenExpired(validTill: number): boolean {
|
||||||
// Create a current UnixTime style date in ms
|
// Create a current UnixTime style date in ms
|
||||||
const timeNow = Date.now();
|
const timeNow = Math.round(Date.now());
|
||||||
if ((new Date(validTill * 1000).getMinutes() - new Date().getMinutes()) / 1000 / 60 < 0) {
|
console.log(`isTokenExpired: timeNow: ${new Date(timeNow).toString()}`);
|
||||||
|
console.log(`isTokenExpired: validTill: ${new Date(validTill).toString()}`);
|
||||||
|
//if ((new Date(validTill).getMinutes() - new Date().getMinutes()) / 1000 / 60 < 0) {
|
||||||
|
if (timeNow > validTill) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -197,6 +203,7 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
|
|
||||||
@action
|
@action
|
||||||
setTokenDetails(token: string, refreshToken: string) {
|
setTokenDetails(token: string, refreshToken: string) {
|
||||||
|
|
||||||
let tokenDecoded = this.decodeToken(token);
|
let tokenDecoded = this.decodeToken(token);
|
||||||
let refreshTokenDecoded = this.decodeToken(refreshToken);
|
let refreshTokenDecoded = this.decodeToken(refreshToken);
|
||||||
|
|
||||||
@ -205,15 +212,21 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
this.token.preferredUserName = tokenDecoded.preferred_username;
|
this.token.preferredUserName = tokenDecoded.preferred_username;
|
||||||
|
|
||||||
// Create a current UnixTime style date in secs
|
// Create a current UnixTime style date in secs
|
||||||
const timeNow = Math.round(Date.now() / 1000);
|
this.token.tokenValidTill = tokenDecoded.exp * 1000;
|
||||||
this.token.tokenValidTill = timeNow + tokenDecoded.exp;
|
this.token.refreshTokenValidTill = refreshTokenDecoded.exp * 1000;
|
||||||
this.token.refreshTokenValidTill = timeNow + refreshTokenDecoded.exp;
|
|
||||||
|
|
||||||
console.info('The saved token object is: ' + JSON.stringify(this.token));
|
console.info('The saved token object is: ' + JSON.stringify(this.token));
|
||||||
|
const tokenSavedAt = new Date();
|
||||||
|
console.log(`keycloak token retrieved at: ${tokenSavedAt.toLocaleTimeString()}`);
|
||||||
|
|
||||||
console.info('Check if token date is expired: ' + this.isTokenExpired(this.token.tokenValidTill));
|
console.info('Check if token date is expired: ' + this.isTokenExpired(this.token.tokenValidTill));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
saveLastLoggedInUser(user: string) {
|
||||||
|
this.lastLoggedInUser = user;
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
protected async fromStore(data: Partial<UserStoreModel> = {}) {
|
protected async fromStore(data: Partial<UserStoreModel> = {}) {
|
||||||
const { lastSeenAppVersion, seenContexts = [], preferences, kubeConfigPath, token } = data
|
const { lastSeenAppVersion, seenContexts = [], preferences, kubeConfigPath, token } = data
|
||||||
@ -235,6 +248,7 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
seenContexts: Array.from(this.seenContexts),
|
seenContexts: Array.from(this.seenContexts),
|
||||||
preferences: this.preferences,
|
preferences: this.preferences,
|
||||||
token: this.token,
|
token: this.token,
|
||||||
|
lastLoggedInUser: this.lastLoggedInUser,
|
||||||
}
|
}
|
||||||
return toJS(model, {
|
return toJS(model, {
|
||||||
recurseEverything: true,
|
recurseEverything: true,
|
||||||
|
|||||||
@ -125,8 +125,12 @@ export class Cluster implements ClusterModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async activate(init = false) {
|
async activate(init = false) {
|
||||||
logger.info(`[CLUSTER]: activate`, this.getMeta());
|
const clusterActivatedAt = new Date();
|
||||||
|
logger.info(`[CLUSTER]: activate@${clusterActivatedAt.toString()}`, this.getMeta());
|
||||||
await this.whenInitialized;
|
await this.whenInitialized;
|
||||||
|
logger.info("[CLUSTER]: activate now initialized")
|
||||||
|
logger.info(`[CLUSTER]: activate eventDisposers length: ${this.eventDisposers.length}`);
|
||||||
|
logger.info(`[CLUSTER]: activate eventDisposers: ${this.eventDisposers}`);
|
||||||
if (!this.eventDisposers.length) {
|
if (!this.eventDisposers.length) {
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
}
|
}
|
||||||
@ -158,7 +162,8 @@ export class Cluster implements ClusterModel {
|
|||||||
|
|
||||||
@action
|
@action
|
||||||
async refresh() {
|
async refresh() {
|
||||||
logger.info(`[CLUSTER]: refresh`, this.getMeta());
|
const clusterRefreshAt = new Date();
|
||||||
|
logger.info(`[CLUSTER]: refresh@${clusterRefreshAt.toString()}`, this.getMeta());
|
||||||
await this.refreshConnectionStatus(); // refresh "version", "online", etc.
|
await this.refreshConnectionStatus(); // refresh "version", "online", etc.
|
||||||
if (this.accessible) {
|
if (this.accessible) {
|
||||||
this.kubeCtl = new Kubectl(this.version)
|
this.kubeCtl = new Kubectl(this.version)
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import "../common/cluster-ipc";
|
import "../common/cluster-ipc";
|
||||||
import type http from "http"
|
import type http from "http"
|
||||||
import { autorun } from "mobx";
|
import { autorun } from "mobx";
|
||||||
import { ClusterStore, clusterStore, getClusterIdFromHost } from "../common/cluster-store"
|
import { ClusterModel, ClusterStore, clusterStore, getClusterIdFromHost } from "../common/cluster-store"
|
||||||
import { Cluster } from "./cluster"
|
import { Cluster } from "./cluster"
|
||||||
import logger from "./logger";
|
import logger from "./logger";
|
||||||
import { apiKubePrefix } from "../common/vars";
|
import { apiKubePrefix } from "../common/vars";
|
||||||
@ -11,6 +11,10 @@ import * as request from "request-promise-native";
|
|||||||
import { v4 as uuid } from "uuid";
|
import { v4 as uuid } from "uuid";
|
||||||
import {kubeconfig} from '../common/utils/k8sTemplates';
|
import {kubeconfig} from '../common/utils/k8sTemplates';
|
||||||
import YAML from 'yaml';
|
import YAML from 'yaml';
|
||||||
|
import { readFile } from "fs-extra"
|
||||||
|
import { getNodeWarningConditions, loadConfig, podHasIssues } from "../common/kube-helpers"
|
||||||
|
import { customRequestPromise } from "../common/request";
|
||||||
|
import orderBy from "lodash/orderBy";
|
||||||
|
|
||||||
const ignoredDECCNamespaces = [
|
const ignoredDECCNamespaces = [
|
||||||
'kube-system', 'kube-public', 'openstack-provider-system', 'system',
|
'kube-system', 'kube-public', 'openstack-provider-system', 'system',
|
||||||
@ -18,145 +22,245 @@ const ignoredDECCNamespaces = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export class DECCManager {
|
export class DECCManager {
|
||||||
constructor(protected keycloakServer: http.Server, protected deccURL: string) {
|
constructor(protected deccURL: string) {
|
||||||
}
|
}
|
||||||
// auto-init clusters
|
|
||||||
// autorun(() => {
|
|
||||||
// clusterStore.clusters.forEach(cluster => {
|
|
||||||
// if (!cluster.initialized) {
|
|
||||||
// logger.info(`[CLUSTER-MANAGER]: init cluster`, cluster.getMeta());
|
|
||||||
// cluster.init(port);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// // auto-stop removed clusters
|
async getNamespaces(): Promise<[]> {
|
||||||
// autorun(() => {
|
const res = await customRequestPromise({
|
||||||
// const removedClusters = Array.from(clusterStore.removedClusters.values());
|
uri: `http://${this.deccURL}/api/v1/namespaces`,
|
||||||
// if (removedClusters.length > 0) {
|
|
||||||
// const meta = removedClusters.map(cluster => cluster.getMeta());
|
|
||||||
// logger.info(`[CLUSTER-MANAGER]: removing clusters`, meta);
|
|
||||||
// removedClusters.forEach(cluster => cluster.disconnect());
|
|
||||||
// clusterStore.removedClusters.clear();
|
|
||||||
// }
|
|
||||||
// }, {
|
|
||||||
// delay: 250
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
getNamespacesForUser() {
|
|
||||||
var namespacesUserCanAccess: Array<Workspace> = workspaceStore.workspacesList;
|
|
||||||
var parsedToken = userStore.decodeToken (userStore.getTokenDetails().token);
|
|
||||||
|
|
||||||
// get all namespaces this id has access to
|
|
||||||
const namespaces = {
|
|
||||||
method: 'GET',
|
|
||||||
url: `http://${this.deccURL}/api/v1/namespaces`,
|
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': 'Bearer ' + userStore.getTokenDetails().token
|
'Authorization': 'Bearer ' + userStore.getTokenDetails().token
|
||||||
},
|
},
|
||||||
json: true
|
json: true,
|
||||||
};
|
resolveWithFullResponse: true,
|
||||||
|
timeout: 10000,
|
||||||
|
});
|
||||||
|
// logger.info(`getNamespaces: res - ${JSON.stringify(res)}`);
|
||||||
|
return res.body;
|
||||||
|
}
|
||||||
|
|
||||||
request(namespaces)
|
async getClustersByNamespace(ns: string): Promise<[]> {
|
||||||
.then(function(response) {
|
const res = await customRequestPromise({
|
||||||
//API call ok....
|
uri: `http://${this.deccURL}/apis/cluster.k8s.io/v1alpha1/namespaces/${ns}/clusters`,
|
||||||
const deccNamespaces = response["items"];
|
headers: {
|
||||||
// logger.info(JSON.stringify(deccNamespaces));
|
'Authorization': 'Bearer ' + userStore.getTokenDetails().token
|
||||||
|
},
|
||||||
|
json: true,
|
||||||
|
resolveWithFullResponse: true,
|
||||||
|
timeout: 10000,
|
||||||
|
});
|
||||||
|
// logger.info(`getClustersByNamespace: res - ${JSON.stringify(res)}`);
|
||||||
|
return res.body;
|
||||||
|
}
|
||||||
|
|
||||||
deccNamespaces.forEach(function(namespace) {
|
async getDECCNamespaces() {
|
||||||
|
try {
|
||||||
|
const res = await this.getNamespaces();
|
||||||
|
// logger.info(`getDECCNamespaces: res - ${JSON.stringify(res)}`);
|
||||||
|
var deccNamespaces = [];
|
||||||
|
res["items"].forEach(function(namespace) {
|
||||||
if (!ignoredDECCNamespaces.includes(namespace.metadata.name)) {
|
if (!ignoredDECCNamespaces.includes(namespace.metadata.name)) {
|
||||||
// console.log("Namespace Name: " + namespace.metadata.name);
|
// logger.info(`getDECCNamespaces: Found namespace: ${namespace.metadata.name}`);
|
||||||
let ns = namespace.metadata.name;
|
deccNamespaces.push(namespace.metadata.name);
|
||||||
//console.log("parsedToken.iam_roles: ", parsedToken.iam_roles);
|
|
||||||
if (parsedToken.iam_roles.includes(`m:kaas:${ns}@reader`) || parsedToken.iam_roles.includes(`m:kaas:${ns}@writer`)) {
|
|
||||||
// add namespace to workspaceStore if not present
|
|
||||||
console.log(`User: ${parsedToken.preferred_username} has access to namespace: ${ns}`);
|
|
||||||
if (!workspaceStore.getByName(ns)) {
|
|
||||||
workspaceStore.saveWorkspace({id: uuid(), name: ns, description: `DECC Namespace: ${ns}`});
|
|
||||||
console.log(`Added new workspace: ${ns}`);
|
|
||||||
namespacesUserCanAccess.push(ns);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
return deccNamespaces;
|
||||||
|
|
||||||
return namespacesUserCanAccess;
|
} catch (err) {
|
||||||
})
|
logger.error(`getDECCNamespaces: ${String(err)}`);
|
||||||
.catch(function (err) {
|
}
|
||||||
// API call failed...
|
}
|
||||||
console.log(err);
|
|
||||||
|
getDECCNamespacesForUser(deccNamespaces, userIAMRoles: string[], username: string) {
|
||||||
|
var deccNamespacesForUser = [];
|
||||||
|
deccNamespaces.forEach(function(ns) {
|
||||||
|
if (userIAMRoles.includes(`m:kaas:${ns}@reader`) || userIAMRoles.includes(`m:kaas:${ns}@writer`)) {
|
||||||
|
// add namespace to workspaceStore if not present
|
||||||
|
//logger.info(`getDECCNamespacesForUser: User ${username} has access to namespace ${ns}`);
|
||||||
|
deccNamespacesForUser.push(ns);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return deccNamespacesForUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDECCClustersForNamespace(ns: string) {
|
||||||
|
try {
|
||||||
|
const res = await this.getClustersByNamespace(ns);
|
||||||
|
//logger.info(`getDECCClustersForNamespace: res - ${JSON.stringify(res)}`);
|
||||||
|
|
||||||
|
var deccClustersForNamespace = [];
|
||||||
|
//API call ok....
|
||||||
|
res["items"].forEach(function(deccCluster) {
|
||||||
|
deccClustersForNamespace.push(deccCluster);
|
||||||
|
});
|
||||||
|
return deccClustersForNamespace;
|
||||||
|
} catch(err) {
|
||||||
|
logger.error(`getDECCClustersForNamespace: ${String(err)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addLensDECCWorkspace(ws: string) {
|
||||||
|
const wsPrefix = `decc`;
|
||||||
|
|
||||||
|
if (!workspaceStore.getByName(`${wsPrefix}-${ws}`)) {
|
||||||
|
workspaceStore.saveWorkspace({id: uuid(), name: `${wsPrefix}-${ws}`, description: `DECC Namespace: ${ws}`});
|
||||||
|
logger.info(`Added new workspace: ${wsPrefix}-${ws}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addLensClusterToDECCWorkspace(deccCluster, idToken: string, refreshToken: string, username: string, workspace: Workspace) {
|
||||||
|
// check if cluster is already in the cluster store
|
||||||
|
var clusterPresent = false;
|
||||||
|
const clusterPrefix = `decc`
|
||||||
|
|
||||||
|
clusterStore.getByWorkspaceId(workspace.id).forEach(cluster => {
|
||||||
|
if (cluster.preferences.clusterName === `${username}@${clusterPrefix}-${deccCluster.metadata.name}`) {
|
||||||
|
clusterPresent = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if ("status" in deccCluster && !clusterPresent) {
|
||||||
|
let ucpDashboard = `https://${deccCluster.status.providerStatus.ucpDashboard.split(":", 2).reverse()[0].substring(2)}:443`;
|
||||||
|
//logger.info(`addLensClusterToDECCWorkspace: ucpDashboard - ${ucpDashboard}`);
|
||||||
|
|
||||||
|
const jsConfig = kubeconfig({
|
||||||
|
username: username,
|
||||||
|
clusterName: `${clusterPrefix}-${deccCluster.metadata.name}`,
|
||||||
|
clientId: deccCluster.status.providerStatus.oidc.clientId,
|
||||||
|
idpCertificateAuthorityData: deccCluster.status.providerStatus.oidc.certificate,
|
||||||
|
idpIssuerUrl: deccCluster.status.providerStatus.oidc.issuerUrl,
|
||||||
|
server: ucpDashboard,
|
||||||
|
apiCertificate: deccCluster.status.providerStatus.apiServerCertificate,
|
||||||
|
idToken: idToken,
|
||||||
|
refreshToken: refreshToken
|
||||||
|
});
|
||||||
|
|
||||||
|
//console.log(`Generated kubeconfig: ${YAML.stringify(jsConfig)}`)
|
||||||
|
|
||||||
|
let newClusters: ClusterModel[] = [];
|
||||||
|
let newCluster = new Cluster({
|
||||||
|
id: deccCluster.metadata.uid,
|
||||||
|
contextName: `${username}@${clusterPrefix}-${deccCluster.metadata.name}`,
|
||||||
|
preferences: {
|
||||||
|
clusterName: `${username}@${clusterPrefix}-${deccCluster.metadata.name}`,
|
||||||
|
httpsProxy: undefined,
|
||||||
|
},
|
||||||
|
kubeConfigPath: ClusterStore.embedCustomKubeConfig(deccCluster.metadata.uid, YAML.stringify(jsConfig)),
|
||||||
|
workspace: workspace.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
newClusters.push(newCluster);
|
||||||
|
clusterStore.addCluster(...newClusters);
|
||||||
|
logger.info(`addLensClusterToDECCWorkspace: Created Cluster Name: ${username}@${clusterPrefix}-${deccCluster.metadata.name}, Cluster UCP Dashboard URL: ${ucpDashboard}`)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
addLensClustersToDECCWorkspace(deccClusters, idToken: string, refreshToken: string, username: string, wsName: string) {
|
||||||
|
const wsPrefix = `decc`;
|
||||||
|
const workspace = workspaceStore.getByName(`${wsPrefix}-${wsName}`);
|
||||||
|
|
||||||
|
//logger.info(`addLensClustersToDECCWorkspace: Processing clusters in Workspace ${workspace.name} for User ${username}`);
|
||||||
|
|
||||||
|
deccClusters.forEach(cluster => {
|
||||||
|
this.addLensClusterToDECCWorkspace(cluster, idToken, refreshToken, username, workspace)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addClustersToWorkspace() {
|
deleteLensDECCClustersByWorkspace(ws) {
|
||||||
var parsedToken = userStore.decodeToken (userStore.getTokenDetails().token);
|
logger.info(`deleteLensDECCClustersByWorkspace: Removing all Lens DECC clusters for Workspace ${ws.name}`)
|
||||||
console.log(`deccURL: ${this.deccURL}`);
|
clusterStore.removeByWorkspaceId(ws.id);
|
||||||
|
}
|
||||||
|
|
||||||
workspaceStore.workspacesList.forEach(function(ws) {
|
deleteLensDECCWorkspace(workspaceId) {
|
||||||
console.log(`Adding clusters for ws: ${ws.name}`);
|
workspaceStore.removeWorkspace(workspaceId);
|
||||||
let clusters = {
|
}
|
||||||
method: 'GET',
|
|
||||||
url: `http://a09bfce9ea3074e25b8e5e7b1df576fd-1162277427.eu-west-2.elb.amazonaws.com/apis/cluster.k8s.io/v1alpha1/namespaces/${ws.name}/clusters`,
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Bearer ' + userStore.getTokenDetails().token
|
|
||||||
},
|
|
||||||
json: true
|
|
||||||
};
|
|
||||||
|
|
||||||
request(clusters)
|
deleteLensDECCWorkspaces(userDECCNamespaces) {
|
||||||
.then(function(response) {
|
const wsPrefix = `decc`;
|
||||||
//API call ok....
|
workspaceStore.workspacesList.forEach(ws => {
|
||||||
const deccClusters = response["items"];
|
let strippedPrefixWorkspaceName = ws.name.slice(5);
|
||||||
deccClusters.forEach(function(deccCluster: object) {
|
//logger.info(`deleteLensDECCWorkspaces: Existing Workspace ${ws.name} being checked against ${userDECCNamespaces.toString()}`)
|
||||||
// check if cluster is already in the cluster store
|
if (ws.name != "default") {
|
||||||
let clusterPresent = false;
|
//logger.info(`deleteLensDECCWorkspaces: Stripped Workspace ${strippedPrefixWorkspaceName} being checked against ${userDECCNamespaces.toString()}`)
|
||||||
clusterStore.getByWorkspaceId(ws.id).forEach(wsCluster => {
|
if (!userDECCNamespaces.includes(`${strippedPrefixWorkspaceName}`)) {
|
||||||
if (wsCluster.contextName === `${parsedToken.preferred_username}@${deccCluster.metadata.name}`) {
|
logger.info(`deleteLensDECCWorkspaces: User does not have access to existing Workspace ${ws.name} - Deleting`)
|
||||||
clusterPresent = true;
|
this.deleteLensDECCClustersByWorkspace(ws);
|
||||||
}
|
this.deleteLensDECCWorkspace(ws.id);
|
||||||
});
|
}
|
||||||
|
}
|
||||||
if ("status" in deccCluster && !clusterPresent) {
|
|
||||||
// clusterUCPURL = cluster.status.
|
|
||||||
|
|
||||||
let ucpDashboard = `https://${deccCluster.status.providerStatus.ucpDashboard.split(":", 2).reverse()[0].substring(2)}:443`;
|
|
||||||
console.log (`ucpDashboard: ${ucpDashboard}`);
|
|
||||||
|
|
||||||
const jsConfig = kubeconfig({
|
|
||||||
username: parsedToken.preferred_username,
|
|
||||||
clusterName: deccCluster.metadata.name,
|
|
||||||
clientId: deccCluster.status.providerStatus.oidc.clientId,
|
|
||||||
idpCertificateAuthorityData: deccCluster.status.providerStatus.oidc.certificate,
|
|
||||||
idpIssuerUrl: deccCluster.status.providerStatus.oidc.issuerUrl,
|
|
||||||
server: ucpDashboard,
|
|
||||||
apiCertificate: deccCluster.status.providerStatus.apiServerCertificate,
|
|
||||||
idToken: userStore.getTokenDetails().token,
|
|
||||||
refreshToken: userStore.getTokenDetails().refreshToken
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`Generated kubeconfig: ${YAML.stringify(jsConfig)}`)
|
|
||||||
|
|
||||||
console.log(`Cluster Name: ${deccCluster.metadata.name}, Cluster UCP Dashboard URL: ${deccCluster.status.providerStatus.ucpDashboard}`)
|
|
||||||
let newCluster = new Cluster({
|
|
||||||
id: uuid(),
|
|
||||||
contextName: `${parsedToken.preferred_username}@${deccCluster.metadata.name}`,
|
|
||||||
preferences: {
|
|
||||||
clusterName: deccCluster.metadata.name,
|
|
||||||
httpsProxy: undefined,
|
|
||||||
},
|
|
||||||
kubeConfigPath: ClusterStore.embedCustomKubeConfig(deccCluster.metadata.uid, YAML.stringify(jsConfig)),
|
|
||||||
workspace: ws.name,
|
|
||||||
});
|
|
||||||
|
|
||||||
clusterStore.addCluster(newCluster);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(function (err: string) {
|
|
||||||
// API call failed...
|
|
||||||
console.log(err);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshLensDECCClusterKubeconfigs(idToken:string , refreshToken: string, username: string, workspace: string) {
|
||||||
|
//logger.info(`refreshLensDECCClusterKubeconfigs: Processing Workspace Name ${workspace}`);
|
||||||
|
const wsPrefix = `decc`;
|
||||||
|
const ws = workspaceStore.getByName(`${wsPrefix}-${workspace}`);
|
||||||
|
|
||||||
|
if (ws === undefined) { return }
|
||||||
|
|
||||||
|
//logger.info(`refreshLensDECCClusterKubeconfigs: Processing Workspace ${JSON.stringify(ws)}`)
|
||||||
|
clusterStore.getByWorkspaceId(ws.id).forEach(cluster => {
|
||||||
|
const currentKubeConfig = loadConfig(cluster.kubeConfigPath); //readFile(cluster.kubeConfigPath, "utf8");
|
||||||
|
// console.log(`refreshClusterKubeConfigs: Read kubeconfig from file: ${cluster.kubeConfigPath}. Contents: ${YAML.stringify(kubeConfig)}`);
|
||||||
|
// console.log(`refreshClusterKubeConfigs: kubeconfig users[0]: ${YAML.stringify(kubeConfig.users[0])}`);
|
||||||
|
// console.log(`refreshClusterKubeConfigs: kubeconfig users[0] id-token: ${YAML.stringify(kubeConfig.users[0].authProvider.config["id-token"])}`);
|
||||||
|
// console.log(`refreshClusterKubeConfigs: kubeconfig users[0] refresh-token: ${YAML.stringify(kubeConfig.users[0].authProvider.config["refresh-token"])}`);
|
||||||
|
const jsConfig = kubeconfig({
|
||||||
|
username: username,
|
||||||
|
clusterName: currentKubeConfig.clusters[0].name,
|
||||||
|
clientId: currentKubeConfig.users[0].authProvider.config["client-id"],
|
||||||
|
idpCertificateAuthorityData: currentKubeConfig.users[0].authProvider.config["idp-certificate-authority-data"],
|
||||||
|
idpIssuerUrl: currentKubeConfig.users[0].authProvider.config["idp-issuer-url"],
|
||||||
|
server: currentKubeConfig.clusters[0].server,
|
||||||
|
apiCertificate: currentKubeConfig.clusters[0].caData,
|
||||||
|
idToken: idToken,
|
||||||
|
refreshToken: refreshToken
|
||||||
|
});
|
||||||
|
|
||||||
|
ClusterStore.embedCustomKubeConfig(cluster.id, YAML.stringify(jsConfig));
|
||||||
|
logger.info(`refreshLensDECCClusterKubeconfigs: Updated Cluster ${cluster.preferences.clusterName} kubeconfig with new token values`);
|
||||||
|
cluster.refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async createDECCLensEnv() {
|
||||||
|
const idToken = userStore.token.token;
|
||||||
|
const parsedIdToken = userStore.decodeToken (userStore.token.token);
|
||||||
|
const refreshToken = userStore.token.refreshToken;
|
||||||
|
const username = parsedIdToken.preferred_username;
|
||||||
|
const userIAMRoles = parsedIdToken.iam_roles;
|
||||||
|
|
||||||
|
// get all available DECC Namespaces
|
||||||
|
const deccNamespaces = await this.getDECCNamespaces();
|
||||||
|
// logger.info(`createDECCLensEnv: The following namespaces exist in DECC - ${deccNamespaces.toString()}`);
|
||||||
|
|
||||||
|
// get all DECC Namespaces the user has access to
|
||||||
|
const userDECCNamespaces: string[] = await this.getDECCNamespacesForUser(deccNamespaces, userIAMRoles, username);
|
||||||
|
if (userDECCNamespaces.length > 0) {
|
||||||
|
userDECCNamespaces.sort();
|
||||||
|
logger.info(`createDECCLensEnv: The following namespaces exist in DECC for User ${username} - ${userDECCNamespaces.toString()}`);
|
||||||
|
|
||||||
|
// lets remove workspaces this user does not have access to
|
||||||
|
this.deleteLensDECCWorkspaces(userDECCNamespaces);
|
||||||
|
|
||||||
|
userDECCNamespaces.forEach(async (ns) => {
|
||||||
|
try {
|
||||||
|
let deccClustersByNamespace = await this.getDECCClustersForNamespace(ns);
|
||||||
|
//logger.info(`createDECCLensEnv: The following clusters exist in Namespace ${ns} - ${JSON.stringify(deccClustersByNamespace)}`);
|
||||||
|
|
||||||
|
// refresh tokens for any existing clusters
|
||||||
|
this.refreshLensDECCClusterKubeconfigs(idToken, refreshToken, username, ns);
|
||||||
|
|
||||||
|
// now lets add the workspace in Lens
|
||||||
|
this.addLensDECCWorkspace(ns);
|
||||||
|
|
||||||
|
// now lets add the clusters to the workspace
|
||||||
|
this.addLensClustersToDECCWorkspace(deccClustersByNamespace, idToken, refreshToken, username, ns);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`createDECCLensEnv: ${String(err)}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,11 +50,6 @@ const keycloakWinURL = process.env.NODE_ENV === 'development'
|
|||||||
: `file://${__static}/keycloak_index.html`
|
: `file://${__static}/keycloak_index.html`
|
||||||
const { ipcMain } = require('electron')
|
const { ipcMain } = require('electron')
|
||||||
|
|
||||||
const ignoredDECCNamespaces = [
|
|
||||||
'kube-system', 'kube-public', 'openstack-provider-system', 'system',
|
|
||||||
'kaas', 'lcm-system', 'istio-system', 'kube-node-lease', 'stacklight'
|
|
||||||
];
|
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
await shellSync();
|
await shellSync();
|
||||||
logger.info(`🚀 Starting Lens from "${workingDir}"`)
|
logger.info(`🚀 Starting Lens from "${workingDir}"`)
|
||||||
@ -103,7 +98,7 @@ async function main() {
|
|||||||
}).listen(3000);
|
}).listen(3000);
|
||||||
|
|
||||||
// create cluster manager
|
// create cluster manager
|
||||||
deccManager = new DECCManager(keycloakServer, 'a09bfce9ea3074e25b8e5e7b1df576fd-1162277427.eu-west-2.elb.amazonaws.com');
|
//deccManager = new DECCManager(keycloakServer, 'a09bfce9ea3074e25b8e5e7b1df576fd-1162277427.eu-west-2.elb.amazonaws.com');
|
||||||
|
|
||||||
|
|
||||||
// create window manager and open app
|
// create window manager and open app
|
||||||
@ -141,22 +136,31 @@ app.on("will-quit", async (event) => {
|
|||||||
ipcMain.on('keycloak-token', (event, idToken, refreshToken) => {
|
ipcMain.on('keycloak-token', (event, idToken, refreshToken) => {
|
||||||
logger.info('test keycloak close main win');
|
logger.info('test keycloak close main win');
|
||||||
userStore.setTokenDetails(idToken, refreshToken);
|
userStore.setTokenDetails(idToken, refreshToken);
|
||||||
logger.info('saved id token and refreshToken to userStore');
|
//logger.info('saved id token and refreshToken to userStore');
|
||||||
|
|
||||||
logger.info('the idToken is: ' + userStore.getTokenDetails().token);
|
//logger.info('the idToken is: ' + userStore.getTokenDetails().token);
|
||||||
|
|
||||||
var parsedToken = userStore.decodeToken (idToken);
|
var parsedToken = userStore.decodeToken (idToken);
|
||||||
|
// create cluster manager
|
||||||
|
deccManager = new DECCManager('a09bfce9ea3074e25b8e5e7b1df576fd-1162277427.eu-west-2.elb.amazonaws.com');
|
||||||
|
deccManager.createDECCLensEnv();
|
||||||
|
|
||||||
deccManager.getNamespacesForUser();
|
// deccManager.refreshClusterKubeConfigs();
|
||||||
deccManager.addClustersToWorkspace();
|
// deccManager.getNamespacesForUser();
|
||||||
|
// deccManager.addClustersToWorkspace();
|
||||||
|
|
||||||
//TODO: refresh token!
|
//TODO: refresh token!
|
||||||
windowManager.showMain();
|
windowManager.showMain();
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('keycloak-token-update', (event, token) => {
|
ipcMain.on('keycloak-token-update', (event, idToken, refreshToken) => {
|
||||||
logger.error('token refresh receivied:' + token);
|
logger.info('token refresh receivied:' + idToken);
|
||||||
//TODO: handle refresh token!
|
if(userStore.isTokenExpired(userStore.token.tokenValidTill)) {
|
||||||
|
userStore.setTokenDetails(idToken, refreshToken);
|
||||||
|
logger.info('saved new id token and refreshToken to userStore');
|
||||||
|
logger.info('the idToken is: ' + userStore.getTokenDetails().token);
|
||||||
|
//deccManager.refreshClusterKubeConfigs();
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('keycloak-logout', (event, data) => {
|
ipcMain.on('keycloak-logout', (event, data) => {
|
||||||
|
|||||||
@ -7,6 +7,11 @@ import { preferencesURL } from "../renderer/components/+preferences/preferences.
|
|||||||
import { whatsNewURL } from "../renderer/components/+whats-new/whats-new.route";
|
import { whatsNewURL } from "../renderer/components/+whats-new/whats-new.route";
|
||||||
import { clusterSettingsURL } from "../renderer/components/+cluster-settings/cluster-settings.route";
|
import { clusterSettingsURL } from "../renderer/components/+cluster-settings/cluster-settings.route";
|
||||||
import logger from "./logger";
|
import logger from "./logger";
|
||||||
|
import { landingURL } from "../renderer/components/+landing-page/landing-page.route";
|
||||||
|
|
||||||
|
const keycloakWinURL = process.env.NODE_ENV === 'development'
|
||||||
|
? `http://localhost:3000/keycloak_index.html`
|
||||||
|
: `file://${__static}/keycloak_index.html`
|
||||||
|
|
||||||
export function initMenu(windowManager: WindowManager) {
|
export function initMenu(windowManager: WindowManager) {
|
||||||
autorun(() => buildMenu(windowManager), {
|
autorun(() => buildMenu(windowManager), {
|
||||||
@ -37,6 +42,10 @@ export function buildMenu(windowManager: WindowManager) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showKeycloak() {
|
||||||
|
windowManager.showKeycloak()
|
||||||
|
}
|
||||||
|
|
||||||
function showAbout(browserWindow: BrowserWindow) {
|
function showAbout(browserWindow: BrowserWindow) {
|
||||||
const appInfo = [
|
const appInfo = [
|
||||||
`${appName}: ${app.getVersion()}`,
|
`${appName}: ${app.getVersion()}`,
|
||||||
@ -121,7 +130,15 @@ export function buildMenu(windowManager: WindowManager) {
|
|||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{ role: 'quit' }
|
{ role: 'quit' }
|
||||||
])
|
]),
|
||||||
|
{ type: 'separator' },
|
||||||
|
{
|
||||||
|
label: 'Logout',
|
||||||
|
click() {
|
||||||
|
//navigate(keycloakWinURL)
|
||||||
|
windowManager.showLogout()
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
mt.push(fileMenu)
|
mt.push(fileMenu)
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { BrowserWindow, dialog, ipcMain, shell, webContents } from "electron"
|
|||||||
import windowStateKeeper from "electron-window-state"
|
import windowStateKeeper from "electron-window-state"
|
||||||
import { observable } from "mobx";
|
import { observable } from "mobx";
|
||||||
import { initMenu } from "./menu";
|
import { initMenu } from "./menu";
|
||||||
|
import { userStore } from "../common/user-store";
|
||||||
|
|
||||||
export class WindowManager {
|
export class WindowManager {
|
||||||
protected mainView: BrowserWindow;
|
protected mainView: BrowserWindow;
|
||||||
@ -95,6 +96,16 @@ export class WindowManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async showLogout() {
|
||||||
|
try {
|
||||||
|
userStore.saveLastLoggedInUser(userStore.token.preferredUserName);
|
||||||
|
await this.mainView.loadURL(`http://localhost:${this.keycloakPort}?logout=true`)
|
||||||
|
this.mainView.show();
|
||||||
|
} catch (err) {
|
||||||
|
dialog.showErrorBox("ERROR!", err.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async showMain() {
|
public async showMain() {
|
||||||
try {
|
try {
|
||||||
//await this.showSplash();
|
//await this.showSplash();
|
||||||
|
|||||||
@ -27,15 +27,14 @@ if(logoutUser){
|
|||||||
console.log("keycloak object: "+ JSON.stringify(keycloak));
|
console.log("keycloak object: "+ JSON.stringify(keycloak));
|
||||||
ipcRenderer.send('keycloak-token', keycloak.idToken, keycloak.refreshToken);
|
ipcRenderer.send('keycloak-token', keycloak.idToken, keycloak.refreshToken);
|
||||||
|
|
||||||
//TODO: check if token refresh is possible here
|
//TODO: check if token refresh is possible here
|
||||||
/*
|
setInterval(() => {
|
||||||
setInterval(() => {
|
let tokenRefreshAt = new Date();
|
||||||
console.log("interval");
|
console.log(`keycloak interval: ${tokenRefreshAt.toString()}`);
|
||||||
keycloak.updateToken(10).error(() => keycloak.logout());
|
keycloak.updateToken(10).error(() => keycloak.logout());
|
||||||
console.log(keycloak.token);
|
console.log(keycloak.token);
|
||||||
ipcRenderer.send('keycloak-token-update', keycloak.token);
|
ipcRenderer.send('keycloak-token-update', keycloak.idToken, keycloak.refreshToken);
|
||||||
}, 10000);
|
}, 10000);
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}).error(function(error) {
|
}).error(function(error) {
|
||||||
console.log('error: ' + JSON.stringify(error));
|
console.log('error: ' + JSON.stringify(error));
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
"moduleResolution": "Node",
|
"moduleResolution": "Node",
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"strict": false,
|
"strict": false,
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": false,
|
||||||
"noUnusedLocals": false,
|
"noUnusedLocals": false,
|
||||||
"noImplicitReturns": false,
|
"noImplicitReturns": false,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user