Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added images/context/cluster-node-gray.png
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When is this used?

Copy link
Copy Markdown
Contributor Author

@vrubezhny vrubezhny Apr 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When displaying a list of available clusters in Login QuickPick the current active cluster we're logged in is shown with a "red" colored icon, while the others are ahown with a "grey" icon:

image

Note: we actually might be logged in to more than one cluster at the same time, the "red"-icon will be used only for the cluster, that is referenced in the "current-context", so other clusters we might be logged in too will still be shown with a "grey" icon.
The "current" cluster is always displayed at the top of the list.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 10 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,9 @@
"got": "^11.8.6",
"hasha": "^5.2.2",
"istanbul": "^0.4.5",
"js-yaml": "^4.1.0",
"json-schema": "^0.4.0",
"json-to-ast": "^2.1.0",
"js-yaml": "^4.1.0",
"leasot": "^14.4.0",
"lodash": "^4.17.21",
"mkdirp": "^3.0.1",
Expand Down Expand Up @@ -188,8 +188,8 @@
"xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0",
"xterm-addon-serialize": "^0.11.0",
"xterm-addon-webgl": "^0.16.0",
"xterm-addon-web-links": "^0.9.0",
"xterm-addon-webgl": "^0.16.0",
"xterm-headless": "^5.3.0"
},
"overrides": {
Expand Down Expand Up @@ -222,6 +222,7 @@
"onCommand:openshift.explorer.login.clipboard",
"onCommand:openshift.explorer.logout",
"onCommand:openshift.explorer.refresh",
"onCommand:openshift.explorer.describe.kubeconfig",
"onCommand:openshift.componentTypesView.refresh",
"onCommand:openshift.project.create",
"onCommand:openshift.project.set",
Expand Down Expand Up @@ -281,7 +282,7 @@
"icons": {
"current-context": {
"description": "context",
"default": "selection"
"default": "layers-active"
},
"project-node": {
"description": "project node",
Expand Down Expand Up @@ -391,6 +392,11 @@
"light": "images/title/light/icon-issue.svg"
}
},
{
"command": "openshift.explorer.describe.kubeconfig",
"title": "Print effective Kube config",
"category": "OpenShift"
},
{
"command": "openshift.show.feedback",
"title": "Share your feedback",
Expand Down Expand Up @@ -2294,4 +2300,4 @@
"publisherId": "eed56242-9699-4317-8bc7-e9f4b9bdd3ff",
"isPreReleaseVersion": false
}
}
}
61 changes: 44 additions & 17 deletions src/explorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,20 @@
import { Context, KubernetesObject } from '@kubernetes/client-node';
import * as fs from 'fs';
import * as path from 'path';
import * as tmp from 'tmp';
import {
commands,
Disposable,
Event,
EventEmitter,
extensions,
TextDocumentShowOptions,
ThemeIcon,
TreeDataProvider,
TreeItem,
TreeItemCollapsibleState,
TreeView,
Uri,
commands,
extensions,
version,
window,
workspace
Expand All @@ -31,7 +33,7 @@
import { Component } from './openshift/component';
import { getServiceKindStubs, getServices } from './openshift/serviceHelpers';
import { PortForward } from './port-forward';
import { KubeConfigUtils, getKubeConfigFiles, getNamespaceKind, isOpenShiftCluster } from './util/kubeUtils';
import { getKubeConfigFiles, getNamespaceKind, isOpenShiftCluster, KubeConfigInfo } from './util/kubeUtils';
import { LoginUtil } from './util/loginUtil';
import { Platform } from './util/platform';
import { Progress } from './util/progress';
Expand Down Expand Up @@ -106,7 +108,7 @@

private kubeConfigWatchers: FileContentChangeNotifier[];
private kubeContext: Context;
private kubeConfig: KubeConfigUtils;
private kubeConfigInfo: KubeConfigInfo;

private executionContext: ExecutionContext = new ExecutionContext();

Expand All @@ -120,8 +122,8 @@

private constructor() {
try {
this.kubeConfig = new KubeConfigUtils();
this.kubeContext = this.kubeConfig.getContextObject(this.kubeConfig.currentContext);
this.kubeConfigInfo = new KubeConfigInfo();
this.kubeContext = this.kubeConfigInfo.getEffectiveKubeConfig().getContextObject(this.kubeConfigInfo.getEffectiveKubeConfig().currentContext);
} catch {
// ignore config loading error and let odo report it on first call
}
Expand All @@ -137,17 +139,18 @@
}
for (const fsw of this.kubeConfigWatchers) {
fsw.emitter?.on('file-changed', () => {
const ku2 = new KubeConfigUtils();
const newCtx = ku2.getContextObject(ku2.currentContext);
const kci2 = new KubeConfigInfo();
const kc2 = kci2.getEffectiveKubeConfig();
const newCtx = kc2.getContextObject(kc2.currentContext);

Check warning on line 144 in src/explorer.ts

View check run for this annotation

Codecov / codecov/patch

src/explorer.ts#L142-L144

Added lines #L142 - L144 were not covered by tests
if (Boolean(this.kubeContext) !== Boolean(newCtx)
|| (this.kubeContext.cluster !== newCtx.cluster
|| this.kubeContext.user !== newCtx.user
|| this.kubeContext.namespace !== newCtx.namespace)) {
|| (this.kubeContext?.cluster !== newCtx?.cluster
|| this.kubeContext?.user !== newCtx?.user
|| this.kubeContext?.namespace !== newCtx?.namespace)) {
this.refresh();
this.onDidChangeContextEmitter.fire(newCtx?.name); // newCtx can be 'null'
}
this.kubeContext = newCtx;
this.kubeConfig = ku2;
this.kubeConfigInfo = kci2;

Check warning on line 153 in src/explorer.ts

View check run for this annotation

Codecov / codecov/patch

src/explorer.ts#L153

Added line #L153 was not covered by tests
});
}
this.treeView = window.createTreeView<ExplorerItem>('openshiftProjectExplorer', {
Expand Down Expand Up @@ -202,7 +205,7 @@
void commands.executeCommand('setContext', 'isLoggedIn', true);
return {
contextValue: 'openshift.k8sContext',
label: this.kubeConfig.getCluster(element.cluster)?.server,
label: this.kubeConfigInfo.getEffectiveKubeConfig().getCluster(element.cluster)?.server,
collapsibleState: TreeItemCollapsibleState.Collapsed,
iconPath: imagePath('context/cluster-node.png')
};
Expand Down Expand Up @@ -466,7 +469,7 @@
} catch {
// ignore because ether server is not accessible or user is logged out
}
OpenShiftExplorer.getInstance().onDidChangeContextEmitter.fire(new KubeConfigUtils().currentContext);
OpenShiftExplorer.getInstance().onDidChangeContextEmitter.fire(this.kubeConfigInfo.getEffectiveKubeConfig().currentContext);

Check warning on line 472 in src/explorer.ts

View check run for this annotation

Codecov / codecov/patch

src/explorer.ts#L472

Added line #L472 was not covered by tests
} else if ('name' in element) { // we are dealing with context here
// user is logged into cluster from current context
// and project should be shown as child node of current context
Expand Down Expand Up @@ -494,7 +497,7 @@
},
} as KubernetesObject]
} else {
const projectName = this.kubeConfig.extractProjectNameFromCurrentContext() || 'default';
const projectName = this.kubeConfigInfo.extractProjectNameFromCurrentContext() || 'default';
result = [await createOrSetProjectItem(projectName, this.executionContext)];
}

Expand Down Expand Up @@ -544,7 +547,7 @@
try {
return this.getPods(element);
} catch {
return [ couldNotGetItem(element.kind, this.kubeConfig.getCluster(this.kubeContext.cluster)?.server) ];
return [ couldNotGetItem(element.kind, this.kubeConfigInfo.getEffectiveKubeConfig().getCluster(this.kubeContext.cluster)?.server) ];
}
} else if ('kind' in element && element.kind === 'project') {
const deployments = {
Expand Down Expand Up @@ -651,7 +654,7 @@
try {
collections = await Oc.Instance.getKubernetesObjects(element.kind, undefined, undefined, this.executionContext);
} catch {
collections = [ couldNotGetItem(element.kind, this.kubeConfig.getCluster(this.kubeContext.cluster)?.server) ];
collections = [ couldNotGetItem(element.kind, this.kubeConfigInfo.getEffectiveKubeConfig().getCluster(this.kubeContext.cluster)?.server) ];
}
break;
}
Expand Down Expand Up @@ -872,4 +875,28 @@
].join('\n');
return `${packageJSON.bugs}/new?labels=kind/bug&title=&body=**Environment**\n${body}\n**Description**`;
}

@vsCommand('openshift.explorer.describe.kubeconfig', true)
static async describeEffectiveConfig(): Promise<void> {
const k8sConfig: string = new KubeConfigInfo().dumpEffectiveKubeConfig();
const tempK8sConfigFile = await new Promise<string>((resolve, reject) => {
tmp.file({ prefix: 'effective.config' ,postfix: '.yaml' }, (err, name) => {

Check warning on line 883 in src/explorer.ts

View check run for this annotation

Codecov / codecov/patch

src/explorer.ts#L881-L883

Added lines #L881 - L883 were not covered by tests
if (err) {
reject(err);

Check warning on line 885 in src/explorer.ts

View check run for this annotation

Codecov / codecov/patch

src/explorer.ts#L885

Added line #L885 was not covered by tests
}
resolve(name);

Check warning on line 887 in src/explorer.ts

View check run for this annotation

Codecov / codecov/patch

src/explorer.ts#L887

Added line #L887 was not covered by tests
});
});
fs.writeFileSync(tempK8sConfigFile, k8sConfig, 'utf-8')
fs.chmodSync(tempK8sConfigFile, 0o400);
const fileUri = Uri.parse(tempK8sConfigFile);
window.showTextDocument(fileUri, { preview: true, readOnly: true } as TextDocumentShowOptions);
const onCloseSubscription = workspace.onDidCloseTextDocument((closedDoc) => {

Check warning on line 894 in src/explorer.ts

View check run for this annotation

Codecov / codecov/patch

src/explorer.ts#L890-L894

Added lines #L890 - L894 were not covered by tests
if (closedDoc.uri.toString() === fileUri.toString()) {
fs.chmodSync(tempK8sConfigFile, 0o600);
fs.unlinkSync(tempK8sConfigFile);
onCloseSubscription.dispose();

Check warning on line 898 in src/explorer.ts

View check run for this annotation

Codecov / codecov/patch

src/explorer.ts#L896-L898

Added lines #L896 - L898 were not covered by tests
}
});
}
}
13 changes: 4 additions & 9 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import { ServerlessFunctionView } from './serverlessFunction/view';
import { startTelemetry } from './telemetry';
import { ToolsConfig } from './tools';
import { TokenStore } from './util/credentialManager';
import { getNamespaceKind, KubeConfigUtils, setKubeConfig } from './util/kubeUtils';
import { getNamespaceKind, KubeConfigInfo } from './util/kubeUtils';
import { setupWorkspaceDevfileContext } from './util/workspace';
import { registerCommands } from './vscommand';
import ClusterViewLoader from './webview/cluster/clusterViewLoader';
Expand Down Expand Up @@ -87,9 +87,6 @@ export async function activate(extensionContext: ExtensionContext): Promise<unkn
// Link from resources to referenced resources
const resourceLinkProvider = new KubernetesResourceLinkProvider();

// pick kube config in case multiple are configured
const setKubeConfigPromise = setKubeConfig();

const crcStatusItem = window.createStatusBarItem(StatusBarAlignment.Left);
crcStatusItem.command = 'openshift.explorer.stopCluster';

Expand Down Expand Up @@ -276,8 +273,9 @@ export async function activate(extensionContext: ExtensionContext): Promise<unkn
updateContextStatusBarItem(activeNamespaceStatusBarItem, 'project-node', '', '', false);
}

const kcu: KubeConfigUtils = new KubeConfigUtils();
const currentContext: KcuContext = kcu.findContext(context ? context : kcu.currentContext);
const k8sConfigInfo = new KubeConfigInfo();
const k8sConfig = k8sConfigInfo.getEffectiveKubeConfig();
const currentContext: KcuContext = k8sConfigInfo.findContext(context ? context : k8sConfig.currentContext);
updateContextStatusBarItem(activeContextStatusBarItem, 'current-context', currentContext?.name, `${currentContext?.name}\nCluster: ${currentContext?.cluster}`,
!isContextInfoStatusBarDisabled());
}
Expand All @@ -298,9 +296,6 @@ export async function activate(extensionContext: ExtensionContext): Promise<unkn

void OdoPreference.Instance.getRegistries(); // Initializes '~/.odo/preference.json', if not initialized yet

// Wait for finishing Kube Config setup
await setKubeConfigPromise;

return {
verifyBundledBinaries
};
Expand Down
7 changes: 4 additions & 3 deletions src/k8s/console.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { commands, Disposable, Uri, window } from 'vscode';
import { CliChannel } from '../cli';
import { Oc } from '../oc/ocWrapper';
import { ClusterType } from '../oc/types';
import { KubeConfigUtils } from '../util/kubeUtils';
import { KubeConfigInfo } from '../util/kubeUtils';
import { vsCommand } from '../vscommand';

export class Console implements Disposable {
Expand All @@ -24,8 +24,9 @@ export class Console implements Disposable {

static cli = CliChannel.getInstance()
static getCurrentProject(): string {
const k8sConfig = new KubeConfigUtils();
const project = (k8sConfig.contexts).find((ctx) => ctx.name === k8sConfig.currentContext).namespace;
const k8sConfigInfo = new KubeConfigInfo();
const k8sConfig = k8sConfigInfo.getEffectiveKubeConfig();
const project = k8sConfig.contexts?.find((ctx) => ctx.name === k8sConfig.currentContext).namespace;
return project;
}

Expand Down
Loading
Loading