mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
remove direct kubeconfig usages
Signed-off-by: Jussi Nummelin <jussi.nummelin@gmail.com>
This commit is contained in:
parent
985bb29b96
commit
b051623056
@ -53,7 +53,7 @@ export class MetricsFeature extends Feature {
|
|||||||
|
|
||||||
async install(cluster: Cluster): Promise<boolean> {
|
async install(cluster: Cluster): Promise<boolean> {
|
||||||
// Check if there are storageclasses
|
// Check if there are storageclasses
|
||||||
const storageClient = cluster.contextHandler.kc.makeApiClient(k8s.StorageV1Api)
|
const storageClient = cluster.proxyKubeconfig().makeApiClient(k8s.StorageV1Api)
|
||||||
const scs = await storageClient.listStorageClass();
|
const scs = await storageClient.listStorageClass();
|
||||||
scs.body.items.forEach(sc => {
|
scs.body.items.forEach(sc => {
|
||||||
if(sc.metadata.annotations &&
|
if(sc.metadata.annotations &&
|
||||||
@ -93,9 +93,9 @@ export class MetricsFeature extends Feature {
|
|||||||
|
|
||||||
async uninstall(cluster: Cluster): Promise<boolean> {
|
async uninstall(cluster: Cluster): Promise<boolean> {
|
||||||
return new Promise<boolean>(async (resolve, reject) => {
|
return new Promise<boolean>(async (resolve, reject) => {
|
||||||
const rbacClient = cluster.contextHandler.kc.makeApiClient(RbacAuthorizationV1Api)
|
const rbacClient = cluster.proxyKubeconfig().makeApiClient(RbacAuthorizationV1Api)
|
||||||
try {
|
try {
|
||||||
await this.deleteNamespace(cluster.contextHandler.kc, "lens-metrics")
|
await this.deleteNamespace(cluster.proxyKubeconfig(), "lens-metrics")
|
||||||
await rbacClient.deleteClusterRole("lens-prometheus");
|
await rbacClient.deleteClusterRole("lens-prometheus");
|
||||||
await rbacClient.deleteClusterRoleBinding("lens-prometheus");
|
await rbacClient.deleteClusterRoleBinding("lens-prometheus");
|
||||||
resolve(true);
|
resolve(true);
|
||||||
|
|||||||
@ -37,7 +37,7 @@ export class UserModeFeature extends Feature {
|
|||||||
|
|
||||||
async uninstall(cluster: Cluster): Promise<boolean> {
|
async uninstall(cluster: Cluster): Promise<boolean> {
|
||||||
return new Promise<boolean>(async (resolve, reject) => {
|
return new Promise<boolean>(async (resolve, reject) => {
|
||||||
const rbacClient = cluster.contextHandler.kc.makeApiClient(RbacAuthorizationV1Api)
|
const rbacClient = cluster.proxyKubeconfig().makeApiClient(RbacAuthorizationV1Api)
|
||||||
try {
|
try {
|
||||||
await rbacClient.deleteClusterRole("lens-user");
|
await rbacClient.deleteClusterRole("lens-user");
|
||||||
await rbacClient.deleteClusterRoleBinding("lens-user");
|
await rbacClient.deleteClusterRoleBinding("lens-user");
|
||||||
|
|||||||
@ -92,13 +92,19 @@ export class Cluster implements ClusterInfo {
|
|||||||
return this.kubeconfigManager.getPath()
|
return this.kubeconfigManager.getPath()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public proxyKubeconfig() {
|
||||||
|
const kc = new KubeConfig()
|
||||||
|
kc.loadFromFile(this.kubeconfigManager.getPath())
|
||||||
|
return kc
|
||||||
|
}
|
||||||
|
|
||||||
public async init(kc: KubeConfig) {
|
public async init(kc: KubeConfig) {
|
||||||
|
this.apiUrl = kc.getCurrentCluster().server
|
||||||
this.contextHandler = new ContextHandler(kc, this)
|
this.contextHandler = new ContextHandler(kc, this)
|
||||||
await this.contextHandler.init() // So we get the proxy port reserved
|
await this.contextHandler.init() // So we get the proxy port reserved
|
||||||
this.kubeconfigManager = new KubeconfigManager(this)
|
this.kubeconfigManager = new KubeconfigManager(this)
|
||||||
|
|
||||||
this.url = this.contextHandler.url
|
this.url = this.contextHandler.url
|
||||||
this.apiUrl = kc.getCurrentCluster().server
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public stopServer() {
|
public stopServer() {
|
||||||
@ -131,7 +137,7 @@ export class Cluster implements ClusterInfo {
|
|||||||
|
|
||||||
if (this.accessible) {
|
if (this.accessible) {
|
||||||
this.distribution = this.detectKubernetesDistribution(this.version)
|
this.distribution = this.detectKubernetesDistribution(this.version)
|
||||||
this.features = await fm.getFeatures(this.contextHandler)
|
this.features = await fm.getFeatures(this)
|
||||||
this.isAdmin = await this.isClusterAdmin()
|
this.isAdmin = await this.isClusterAdmin()
|
||||||
this.nodes = await this.getNodeCount()
|
this.nodes = await this.getNodeCount()
|
||||||
this.kubeCtl = new Kubectl(this.version)
|
this.kubeCtl = new Kubectl(this.version)
|
||||||
@ -156,7 +162,7 @@ export class Cluster implements ClusterInfo {
|
|||||||
id: this.id,
|
id: this.id,
|
||||||
workspace: this.workspace,
|
workspace: this.workspace,
|
||||||
url: this.url,
|
url: this.url,
|
||||||
contextName: this.contextHandler.kc.currentContext,
|
contextName: this.contextName,
|
||||||
apiUrl: this.apiUrl,
|
apiUrl: this.apiUrl,
|
||||||
online: this.online,
|
online: this.online,
|
||||||
accessible: this.accessible,
|
accessible: this.accessible,
|
||||||
@ -216,7 +222,7 @@ export class Cluster implements ClusterInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async canI(resourceAttributes: V1ResourceAttributes): Promise<boolean> {
|
public async canI(resourceAttributes: V1ResourceAttributes): Promise<boolean> {
|
||||||
const authApi = this.contextHandler.kc.makeApiClient(AuthorizationV1Api)
|
const authApi = this.proxyKubeconfig().makeApiClient(AuthorizationV1Api)
|
||||||
try {
|
try {
|
||||||
const accessReview = await authApi.createSelfSubjectAccessReview({
|
const accessReview = await authApi.createSelfSubjectAccessReview({
|
||||||
apiVersion: "authorization.k8s.io/v1",
|
apiVersion: "authorization.k8s.io/v1",
|
||||||
@ -278,7 +284,7 @@ export class Cluster implements ClusterInfo {
|
|||||||
if (!this.isAdmin) {
|
if (!this.isAdmin) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
const client = this.contextHandler.kc.makeApiClient(CoreV1Api);
|
const client = this.proxyKubeconfig().makeApiClient(CoreV1Api);
|
||||||
try {
|
try {
|
||||||
const response = await client.listEventForAllNamespaces(false, null, null, null, 1000);
|
const response = await client.listEventForAllNamespaces(false, null, null, null, 1000);
|
||||||
const uniqEventSources = new Set();
|
const uniqEventSources = new Set();
|
||||||
|
|||||||
@ -15,7 +15,7 @@ export class ContextHandler {
|
|||||||
public contextName: string
|
public contextName: string
|
||||||
public id: string
|
public id: string
|
||||||
public url: string
|
public url: string
|
||||||
public kc: KubeConfig
|
|
||||||
public certData: string
|
public certData: string
|
||||||
public authCertData: string
|
public authCertData: string
|
||||||
public cluster: Cluster
|
public cluster: Cluster
|
||||||
@ -37,13 +37,11 @@ export class ContextHandler {
|
|||||||
|
|
||||||
constructor(kc: KubeConfig, cluster: Cluster) {
|
constructor(kc: KubeConfig, cluster: Cluster) {
|
||||||
this.id = cluster.id
|
this.id = cluster.id
|
||||||
this.kc = kc
|
|
||||||
this.kc.setCurrentContext(cluster.contextName)
|
|
||||||
|
|
||||||
this.cluster = cluster
|
this.cluster = cluster
|
||||||
this.clusterUrl = url.parse(kc.getCurrentCluster().server)
|
this.clusterUrl = url.parse(cluster.apiUrl) //url.parse(kc.getCurrentCluster().server)
|
||||||
this.contextName = cluster.contextName;
|
this.contextName = cluster.contextName;
|
||||||
this.defaultNamespace = kc.getContextObject(kc.currentContext).namespace
|
this.defaultNamespace = kc.getContextObject(cluster.contextName).namespace
|
||||||
this.url = `http://${this.id}.localhost:${cluster.port}/`
|
this.url = `http://${this.id}.localhost:${cluster.port}/`
|
||||||
this.kubernetesApi = `http://127.0.0.1:${cluster.port}/${this.id}`
|
this.kubernetesApi = `http://127.0.0.1:${cluster.port}/${this.id}`
|
||||||
|
|
||||||
@ -89,7 +87,7 @@ export class ContextHandler {
|
|||||||
public async getPrometheusService(): Promise<PrometheusService> {
|
public async getPrometheusService(): Promise<PrometheusService> {
|
||||||
const providers = this.prometheusProvider ? prometheusProviders.filter((p, _) => p.id == this.prometheusProvider) : prometheusProviders
|
const providers = this.prometheusProvider ? prometheusProviders.filter((p, _) => p.id == this.prometheusProvider) : prometheusProviders
|
||||||
const prometheusPromises: Promise<PrometheusService>[] = providers.map(async (provider: PrometheusProvider): Promise<PrometheusService> => {
|
const prometheusPromises: Promise<PrometheusService>[] = providers.map(async (provider: PrometheusProvider): Promise<PrometheusService> => {
|
||||||
const apiClient = this.kc.makeApiClient(CoreV1Api)
|
const apiClient = this.cluster.proxyKubeconfig().makeApiClient(CoreV1Api)
|
||||||
return await provider.getPrometheusService(apiClient)
|
return await provider.getPrometheusService(apiClient)
|
||||||
})
|
})
|
||||||
const resolvedPrometheusServices = await Promise.all(prometheusPromises)
|
const resolvedPrometheusServices = await Promise.all(prometheusPromises)
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { ContextHandler } from "./context-handler"
|
import { KubeConfig } from "@kubernetes/client-node"
|
||||||
import logger from "./logger";
|
import logger from "./logger";
|
||||||
import { Cluster } from "./cluster";
|
import { Cluster } from "./cluster";
|
||||||
import { Feature, FeatureStatusMap } from "./feature"
|
import { Feature, FeatureStatusMap } from "./feature"
|
||||||
@ -10,17 +10,19 @@ const ALL_FEATURES: any = {
|
|||||||
'user-mode': new UserModeFeature(null),
|
'user-mode': new UserModeFeature(null),
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getFeatures(clusterContext: ContextHandler): Promise<FeatureStatusMap> {
|
export async function getFeatures(cluster: Cluster): Promise<FeatureStatusMap> {
|
||||||
return new Promise<FeatureStatusMap>(async (resolve, reject) => {
|
return new Promise<FeatureStatusMap>(async (resolve, reject) => {
|
||||||
const result: FeatureStatusMap = {};
|
const result: FeatureStatusMap = {};
|
||||||
logger.debug(`features for ${clusterContext.contextName}`);
|
logger.debug(`features for ${cluster.contextName}`);
|
||||||
for (const key in ALL_FEATURES) {
|
for (const key in ALL_FEATURES) {
|
||||||
logger.debug(`feature ${key}`);
|
logger.debug(`feature ${key}`);
|
||||||
if (ALL_FEATURES.hasOwnProperty(key)) {
|
if (ALL_FEATURES.hasOwnProperty(key)) {
|
||||||
logger.debug("getting feature status...");
|
logger.debug("getting feature status...");
|
||||||
const feature = ALL_FEATURES[key] as Feature;
|
const feature = ALL_FEATURES[key] as Feature;
|
||||||
|
const kc = new KubeConfig()
|
||||||
const status = await feature.featureStatus(clusterContext.kc);
|
kc.loadFromFile(cluster.proxyKubeconfigPath())
|
||||||
|
|
||||||
|
const status = await feature.featureStatus(kc);
|
||||||
result[feature.name] = status
|
result[feature.name] = status
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -35,7 +35,7 @@ export class KubeAuthProxy {
|
|||||||
"--kubeconfig", this.cluster.kubeConfigPath,
|
"--kubeconfig", this.cluster.kubeConfigPath,
|
||||||
"--context", this.cluster.contextName,
|
"--context", this.cluster.contextName,
|
||||||
"--accept-hosts", ".*",
|
"--accept-hosts", ".*",
|
||||||
"--reject-paths", "^[^/]",
|
"--reject-paths", "^[^/]"
|
||||||
]
|
]
|
||||||
if (process.env.DEBUG_PROXY === "true") {
|
if (process.env.DEBUG_PROXY === "true") {
|
||||||
args = args.concat(["-v", "9"])
|
args = args.concat(["-v", "9"])
|
||||||
|
|||||||
@ -27,6 +27,8 @@ export class KubeconfigManager {
|
|||||||
protected createTemporaryKubeconfig(): string {
|
protected createTemporaryKubeconfig(): string {
|
||||||
ensureDir(this.configDir)
|
ensureDir(this.configDir)
|
||||||
const path = `${this.configDir}/${randomFileName("kubeconfig")}`
|
const path = `${this.configDir}/${randomFileName("kubeconfig")}`
|
||||||
|
const originalKc = new KubeConfig()
|
||||||
|
originalKc.loadFromFile(this.cluster.kubeConfigPath)
|
||||||
const kc = {
|
const kc = {
|
||||||
clusters: [
|
clusters: [
|
||||||
{
|
{
|
||||||
@ -43,7 +45,7 @@ export class KubeconfigManager {
|
|||||||
{
|
{
|
||||||
name: this.cluster.contextName,
|
name: this.cluster.contextName,
|
||||||
cluster: this.cluster.contextName,
|
cluster: this.cluster.contextName,
|
||||||
namespace: this.cluster.contextHandler.kc.getContextObject(this.cluster.contextName).namespace,
|
namespace: originalKc.getContextObject(this.cluster.contextName).namespace,
|
||||||
user: "proxy"
|
user: "proxy"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@ -17,7 +17,7 @@ export class NodeShellSession extends ShellSession {
|
|||||||
super(socket, pathToKubeconfig, cluster)
|
super(socket, pathToKubeconfig, cluster)
|
||||||
this.nodeName = nodeName
|
this.nodeName = nodeName
|
||||||
this.podId = `node-shell-${uuid()}`
|
this.podId = `node-shell-${uuid()}`
|
||||||
this.kc = cluster.contextHandler.kc
|
this.kc = cluster.proxyKubeconfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
public async open() {
|
public async open() {
|
||||||
|
|||||||
@ -45,7 +45,7 @@ const apiResources = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
async function getAllowedNamespaces(cluster: Cluster) {
|
async function getAllowedNamespaces(cluster: Cluster) {
|
||||||
const api = cluster.contextHandler.kc.makeApiClient(CoreV1Api)
|
const api = cluster.proxyKubeconfig().makeApiClient(CoreV1Api)
|
||||||
try {
|
try {
|
||||||
const namespaceList = await api.listNamespace()
|
const namespaceList = await api.listNamespace()
|
||||||
const nsAccessStatuses = await Promise.all(
|
const nsAccessStatuses = await Promise.all(
|
||||||
@ -58,9 +58,8 @@ async function getAllowedNamespaces(cluster: Cluster) {
|
|||||||
return namespaceList.body.items
|
return namespaceList.body.items
|
||||||
.filter((ns, i) => nsAccessStatuses[i])
|
.filter((ns, i) => nsAccessStatuses[i])
|
||||||
.map(ns => ns.metadata.name)
|
.map(ns => ns.metadata.name)
|
||||||
} catch (error) {
|
} catch(error) {
|
||||||
const kc = cluster.contextHandler.kc
|
const ctx = cluster.proxyKubeconfig().getContextObject(cluster.contextName)
|
||||||
const ctx = kc.getContextObject(kc.currentContext)
|
|
||||||
if (ctx.namespace) {
|
if (ctx.namespace) {
|
||||||
return [ctx.namespace]
|
return [ctx.namespace]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ function generateKubeConfig(username: string, secret: V1Secret, cluster: Cluster
|
|||||||
{
|
{
|
||||||
'name': cluster.contextName,
|
'name': cluster.contextName,
|
||||||
'cluster': {
|
'cluster': {
|
||||||
'server': cluster.contextHandler.kc.getCurrentCluster().server,
|
'server': cluster.apiUrl,
|
||||||
'certificate-authority-data': secret.data["ca.crt"]
|
'certificate-authority-data': secret.data["ca.crt"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,7 +44,7 @@ class KubeconfigRoute extends LensApi {
|
|||||||
public async routeServiceAccountRoute(request: LensApiRequest) {
|
public async routeServiceAccountRoute(request: LensApiRequest) {
|
||||||
const { params, response, cluster} = request
|
const { params, response, cluster} = request
|
||||||
|
|
||||||
const client = cluster.contextHandler.kc.makeApiClient(CoreV1Api);
|
const client = cluster.proxyKubeconfig().makeApiClient(CoreV1Api);
|
||||||
const secretList = await client.listNamespacedSecret(params.namespace)
|
const secretList = await client.listNamespacedSecret(params.namespace)
|
||||||
const secret = secretList.body.items.find(secret => {
|
const secret = secretList.body.items.find(secret => {
|
||||||
const { annotations } = secret.metadata;
|
const { annotations } = secret.metadata;
|
||||||
|
|||||||
@ -38,7 +38,17 @@ class ApiWatcher {
|
|||||||
clearInterval(this.processor)
|
clearInterval(this.processor)
|
||||||
}
|
}
|
||||||
logger.debug("Stopping watcher for api: " + this.apiUrl)
|
logger.debug("Stopping watcher for api: " + this.apiUrl)
|
||||||
this.watchRequest.abort()
|
try {
|
||||||
|
this.watchRequest.abort()
|
||||||
|
this.sendEvent({
|
||||||
|
type: "STREAM_END",
|
||||||
|
url: this.apiUrl,
|
||||||
|
status: 410,
|
||||||
|
})
|
||||||
|
logger.debug("watch aborted")
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Watch abort errored:" + error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private watchHandler(phase: string, obj: any) {
|
private watchHandler(phase: string, obj: any) {
|
||||||
@ -49,6 +59,7 @@ class ApiWatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private doneHandler(error: Error) {
|
private doneHandler(error: Error) {
|
||||||
|
logger.debug("********** watch.doneHandler")
|
||||||
if (error) logger.warn("watch ended: " + error.toString())
|
if (error) logger.warn("watch ended: " + error.toString())
|
||||||
|
|
||||||
this.sendEvent({
|
this.sendEvent({
|
||||||
@ -82,9 +93,10 @@ class WatchRoute extends LensApi {
|
|||||||
response.setHeader("Content-Type", "text/event-stream")
|
response.setHeader("Content-Type", "text/event-stream")
|
||||||
response.setHeader("Cache-Control", "no-cache")
|
response.setHeader("Cache-Control", "no-cache")
|
||||||
response.setHeader("Connection", "keep-alive")
|
response.setHeader("Connection", "keep-alive")
|
||||||
|
logger.debug("watch using kubeconfig:" + JSON.stringify(cluster.proxyKubeconfig(), null, 2))
|
||||||
|
|
||||||
apis.forEach(apiUrl => {
|
apis.forEach(apiUrl => {
|
||||||
const watcher = new ApiWatcher(apiUrl, cluster.contextHandler.kc, response)
|
const watcher = new ApiWatcher(apiUrl, cluster.proxyKubeconfig() /*cluster.contextHandler.kc*/, response)
|
||||||
watcher.start()
|
watcher.start()
|
||||||
watchers.push(watcher)
|
watchers.push(watcher)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -17,6 +17,7 @@ export function migration(store: any) {
|
|||||||
console.log("num clusters to migrate: ", storedClusters.length)
|
console.log("num clusters to migrate: ", storedClusters.length)
|
||||||
for (const cluster of storedClusters ) {
|
for (const cluster of storedClusters ) {
|
||||||
// TODO Should probably guard this, not to make the whole migration fail if one cluster fails!?
|
// TODO Should probably guard this, not to make the whole migration fail if one cluster fails!?
|
||||||
|
|
||||||
// take the embedded kubeconfig and dump it into a file
|
// take the embedded kubeconfig and dump it into a file
|
||||||
const kubeConfigFile = writeEmbeddedKubeConfig(cluster.id, cluster.kubeConfig)
|
const kubeConfigFile = writeEmbeddedKubeConfig(cluster.id, cluster.kubeConfig)
|
||||||
cluster.kubeConfigPath = kubeConfigFile
|
cluster.kubeConfigPath = kubeConfigFile
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user