Skip to content

Commit 976e968

Browse files
vrubezhnydatho7561
authored andcommitted
Swtich from 'odo' to 'oc' for create/set/get active/delete project and list ptojects
Issue: #3662 Signed-off-by: Victor Rubezhny <vrubezhny@redhat.com>
1 parent 831d478 commit 976e968

File tree

21 files changed

+238
-196
lines changed

21 files changed

+238
-196
lines changed

src/explorer.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import { CommandText } from './base/command';
2525
import * as Helm from './helm/helm';
2626
import { HelmRepo } from './helm/helmChartType';
2727
import { Oc } from './oc/ocWrapper';
28-
import { Odo } from './odo/odoWrapper';
2928
import { Component } from './openshift/component';
3029
import { getServiceKindStubs } from './openshift/serviceHelpers';
3130
import { KubeConfigUtils, getKubeConfigFiles } from './util/kubeUtils';
@@ -35,6 +34,7 @@ import { FileContentChangeNotifier, WatchUtil } from './util/watch';
3534
import { vsCommand } from './vscommand';
3635
import { CustomResourceDefinitionStub } from './webview/common/createServiceTypes';
3736
import { OpenShiftTerminalManager } from './webview/openshift-terminal/openShiftTerminal';
37+
import { LoginUtil } from './util/loginUtil';
3838

3939
type ExplorerItem = KubernetesObject | Helm.HelmRelease | Context | TreeItem | OpenShiftObject | HelmRepo;
4040

@@ -223,12 +223,13 @@ export class OpenShiftExplorer implements TreeDataProvider<ExplorerItem>, Dispos
223223
let result: ExplorerItem[] = [];
224224
if (!element) {
225225
try {
226-
await Odo.Instance.getProjects();
227-
result = [this.kubeContext];
228-
if (this.kubeContext) {
229-
const config = getKubeConfigFiles();
230-
void commands.executeCommand('setContext', 'canCreateNamespace', await Oc.Instance.canCreateNamespace());
231-
result.unshift({ label: process.env.KUBECONFIG ? 'Custom KubeConfig' : 'Default KubeConfig', description: config.join(':') })
226+
if (!await LoginUtil.Instance.requireLogin()) {
227+
result = [this.kubeContext];
228+
if (this.kubeContext) {
229+
const config = getKubeConfigFiles();
230+
void commands.executeCommand('setContext', 'canCreateNamespace', await Oc.Instance.canCreateNamespace());
231+
result.unshift({ label: process.env.KUBECONFIG ? 'Custom KubeConfig' : 'Default KubeConfig', description: config.join(':') })
232+
}
232233
}
233234
} catch (err) {
234235
// ignore because ether server is not accessible or user is logged out
@@ -244,7 +245,7 @@ export class OpenShiftExplorer implements TreeDataProvider<ExplorerItem>, Dispos
244245
// * example is sandbox context created when login to sandbox first time
245246
// (3) there is namespace set in context and namespace exists in the cluster
246247
// (4) there is namespace set in context and namespace does not exist in the cluster
247-
const namespaces = await Odo.Instance.getProjects();
248+
const namespaces = await Oc.Instance.getProjects();
248249
const helmContext = {
249250
kind: 'helm',
250251
metadata: {

src/oc/ocWrapper.ts

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { CommandOption, CommandText } from '../base/command';
1010
import { CliChannel } from '../cli';
1111
import { Platform } from '../util/platform';
1212
import { ClusterType, KubernetesConsole } from './types';
13+
import { Project } from './project';
14+
import { KubeConfigUtils } from '../util/kubeUtils';
1315

1416
/**
1517
* A wrapper around the `oc` CLI tool.
@@ -437,6 +439,110 @@ export class Oc {
437439
return result.stdout;
438440
}
439441

442+
public async deleteProject(projectName: string): Promise<string> {
443+
const obj = await this.isOpenShiftCluster() ? 'project' : 'namespace';
444+
return await CliChannel.getInstance().executeTool(
445+
new CommandText('oc', `delete ${obj} ${projectName}`)
446+
)
447+
.then((result) => result.stdout);
448+
}
449+
450+
public async createProject(projectName: string): Promise<string> {
451+
const cmd = await this.isOpenShiftCluster() ? 'new-project' : 'create namespace';
452+
453+
return await CliChannel.getInstance().executeTool(
454+
new CommandText('oc', `${cmd} ${projectName}`)
455+
)
456+
.then(async (result) => {
457+
// oc doesn't handle switching to the newly created namespace/project
458+
await this.setProject(projectName);
459+
return result.stdout;
460+
});
461+
}
462+
463+
/**
464+
* Changes which project is currently being used.
465+
*
466+
* On non-OpenShift, namespaces are used instead of projects
467+
*
468+
* @param newProject the new project to use
469+
*/
470+
public async setProject(projectName: string): Promise<void> {
471+
if(await this.isOpenShiftCluster()) {
472+
await CliChannel.getInstance().executeTool(
473+
new CommandText('oc', `project ${projectName}`),
474+
);
475+
} else {
476+
await CliChannel.getInstance().executeTool(
477+
new CommandText('oc', 'config set-context', [
478+
new CommandOption('--current'),
479+
new CommandOption('--namespace', projectName)
480+
])
481+
);
482+
}
483+
}
484+
485+
public async getProjects(): Promise<Project[]> {
486+
return this._listProjects();
487+
}
488+
489+
/**
490+
* Returns the active project or null if no project is active
491+
*
492+
* @returns the active project or null if no project is active
493+
*/
494+
public async getActiveProject(): Promise<string> {
495+
const projects = await this._listProjects();
496+
if (!projects.length) {
497+
return null;
498+
}
499+
let activeProject = projects.find((project) => project.active);
500+
if (activeProject) return activeProject.name;
501+
502+
// If not found - use Kube Config current context or 'default'
503+
const kcu = new KubeConfigUtils();
504+
const currentContext = kcu.findContext(kcu.currentContext);
505+
if (currentContext) {
506+
const active = currentContext.namespace || 'default';
507+
activeProject = projects.find((project) => project.name ===active);
508+
}
509+
return activeProject ? activeProject.name : null;
510+
}
511+
512+
private async _listProjects(): Promise<Project[]> {
513+
const onlyOneProject = 'you have one project on this server:';
514+
const namespaces: Project[] = [];
515+
return await CliChannel.getInstance().executeTool(
516+
new CommandText('oc', 'projects')
517+
)
518+
.then( (result) => {
519+
const lines = result.stdout && result.stdout.split(/\r?\n/g);
520+
for (let line of lines) {
521+
line = line.trim();
522+
if (line === '') continue;
523+
if (line.toLocaleLowerCase().startsWith(onlyOneProject)) {
524+
const matches = line.match(/You\shave\sone\sproject\son\sthis\sserver:\s"([a-zA-Z0-9]+[a-zA-Z0-9.-]*)"./);
525+
if (matches) {
526+
namespaces.push({name: matches[1], active: true});
527+
break; // No more projects are to be listed
528+
}
529+
} else {
530+
const words: string[] = line.split(' ');
531+
if (words.length > 0 && words.length <= 2) {
532+
// The list of projects may have eithe 1 (project name) or 2 words
533+
// (an asterisk char, indicating that the project is active, and project name).
534+
// Otherwise, it's either a header or a footer text
535+
const active = words.length === 2 && words[0].trim() === '*';
536+
const projectName = words[words.length - 1] // The last word of array
537+
namespaces.push( {name: projectName, active });
538+
}
539+
}
540+
}
541+
return namespaces;
542+
})
543+
.catch((error) => namespaces);
544+
}
545+
440546
/**
441547
* Returns the oc command to list all resources of the given type in the given (or current) namespace
442548
*
File renamed without changes.

src/odo/odoWrapper.ts

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import { Command } from './command';
1818
import { AnalyzeResponse, ComponentTypeAdapter, ComponentTypeDescription, DevfileComponentType, Registry } from './componentType';
1919
import { ComponentDescription, StarterProject } from './componentTypeDescription';
2020
import { BindableService } from './odoTypes';
21-
import { Project } from './project';
2221

2322
/**
2423
* Wraps the `odo` cli tool.
@@ -78,21 +77,6 @@ export class Odo {
7877
void commands.executeCommand('setContext', 'isLoggedIn', false);
7978
}
8079

81-
public async getProjects(): Promise<Project[]> {
82-
return this._listProjects();
83-
}
84-
85-
/**
86-
* Changes which project is currently being used.
87-
*
88-
* On non-OpenShift, namespaces are used instead of projects
89-
*
90-
* @param newProject the new project to use
91-
*/
92-
public async setProject(newProject: string): Promise<void> {
93-
await this.execute(new CommandText('odo', `set namespace ${newProject}`), undefined, true);
94-
}
95-
9680
public getKubeconfigEnv(): { KUBECONFIG?: string } {
9781
const addEnv: { KUBECONFIG?: string } = {};
9882
let kc: KubeConfig;
@@ -183,22 +167,6 @@ export class Odo {
183167
return result;
184168
}
185169

186-
public async deleteProject(projectName: string): Promise<void> {
187-
await this.execute(
188-
new CommandText('odo', `delete namespace ${projectName}`, [
189-
new CommandOption('-f'),
190-
new CommandOption('-w'),
191-
]),
192-
);
193-
}
194-
195-
public async createProject(projectName: string): Promise<void> {
196-
await Odo.instance.execute(
197-
new CommandText('odo', `create namespace ${projectName}`, [new CommandOption('-w')]),
198-
);
199-
// odo handles switching to the newly created namespace/project
200-
}
201-
202170
public async createComponentFromFolder(
203171
type: string,
204172
registryName: string,
@@ -376,31 +344,6 @@ export class Odo {
376344
);
377345
}
378346

379-
/**
380-
* Returns the active project or null if no project is active
381-
*
382-
* @returns the active project or null if no project is active
383-
*/
384-
public async getActiveProject(): Promise<string> {
385-
const projects = await this._listProjects();
386-
if (!projects.length) {
387-
return null;
388-
}
389-
const activeProject = projects.find((project) => project.active);
390-
return activeProject ? activeProject.name : null;
391-
}
392-
393-
private async _listProjects(): Promise<Project[]> {
394-
const response = await this.execute(
395-
new CommandText('odo', 'list project', [new CommandOption('-o', 'json', false)]),
396-
);
397-
const responseObj = JSON.parse(response.stdout);
398-
if (!responseObj?.namespaces) {
399-
return [];
400-
}
401-
return responseObj.namespaces as Project[];
402-
}
403-
404347
/**
405348
* Deletes all the odo configuration files associated with the component (`.odo`, `devfile.yaml`) located at the given path.
406349
*

src/openshift/cluster.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { CliChannel } from '../cli';
1010
import { OpenShiftExplorer } from '../explorer';
1111
import { Oc } from '../oc/ocWrapper';
1212
import { Command } from '../odo/command';
13-
import { Odo } from '../odo/odoWrapper';
1413
import * as NameValidator from '../openshift/nameValidator';
1514
import { TokenStore } from '../util/credentialManager';
1615
import { Filters } from '../util/filters';
@@ -108,7 +107,7 @@ export class Cluster extends OpenShiftItem {
108107
async (progress) => {
109108
const [consoleInfo, namespace] = await Promise.all([
110109
Oc.Instance.getConsoleInfo(),
111-
Odo.Instance.getActiveProject(),
110+
Oc.Instance.getActiveProject(),
112111
]);
113112
progress.report({ increment: 100, message: 'Starting default browser' });
114113
// eg. https://console-openshift-console.apps-crc.testing/catalog/ns/default?catalogType=OperatorBackedService

src/openshift/component.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ export class Component extends OpenShiftItem {
216216
}
217217

218218
private static async checkForPodman(): Promise<boolean> {
219-
if (await Component.odo.isPodmanPresent()) {
219+
if (await Odo.Instance.isPodmanPresent()) {
220220
return true;
221221
}
222222
void window.showErrorMessage('Podman is not present in the system, please install podman on your machine and try again.', 'Install podman')
@@ -378,7 +378,7 @@ export class Component extends OpenShiftItem {
378378
@vsCommand('openshift.component.openInBrowser')
379379
@clusterRequired()
380380
static async openInBrowser(component: ComponentWorkspaceFolder): Promise<string | null | undefined> {
381-
const componentDescription = await Component.odo.describeComponent(component.contextPath, !!Component.getComponentDevState(component).runOn);
381+
const componentDescription = await Odo.Instance.describeComponent(component.contextPath, !!Component.getComponentDevState(component).runOn);
382382
if (componentDescription.devForwardedPorts?.length === 1) {
383383
const fp = componentDescription.devForwardedPorts[0];
384384
await commands.executeCommand('vscode.open', Uri.parse(`http://${fp.localAddress}:${fp.localPort}`));
@@ -502,7 +502,7 @@ export class Component extends OpenShiftItem {
502502
let componentType: ComponentTypeAdapter;
503503
let componentTypeCandidates: ComponentTypeAdapter[];
504504
if (!useExistingDevfile && (!opts || !opts.devFilePath || opts.devFilePath.length === 0)) {
505-
const componentTypes = await Component.odo.getComponentTypes();
505+
const componentTypes = await Odo.Instance.getComponentTypes();
506506
progressIndicator.busy = true;
507507
progressIndicator.placeholder = opts?.componentTypeName ? `Checking if '${opts.componentTypeName}' Component type is available` : 'Loading available Component types';
508508
progressIndicator.show();
@@ -534,7 +534,7 @@ export class Component extends OpenShiftItem {
534534
progressIndicator.placeholder = 'Loading Starter Projects for selected Component Type'
535535
progressIndicator.show();
536536

537-
const starterProjects: StarterProject[] = await this.odo.getStarterProjects(componentType);
537+
const starterProjects: StarterProject[] = await Odo.Instance.getStarterProjects(componentType);
538538
progressIndicator.hide();
539539
if (starterProjects?.length && starterProjects.length > 0) {
540540
const create = await window.showQuickPick(['Yes', 'No'], { placeHolder: `Initialize Component using ${starterProjects.length === 1 ? '\''.concat(starterProjects[0].name.concat('\' ')) : ''}Starter Project?` });
@@ -574,7 +574,7 @@ export class Component extends OpenShiftItem {
574574
try {
575575
await Progress.execFunctionWithProgress(
576576
`Creating new Component '${componentName}'`,
577-
() => Component.odo.createComponentFromFolder(
577+
() => Odo.Instance.createComponentFromFolder(
578578
componentType?.name, // in case of using existing devfile
579579
componentType?.registryName,
580580
componentName,
@@ -706,7 +706,7 @@ export class Component extends OpenShiftItem {
706706
}
707707

708708
static async startOdoAndConnectDebugger(component: ComponentWorkspaceFolder, config: DebugConfiguration): Promise<string> {
709-
const componentDescription = await Component.odo.describeComponent(component.contextPath, !!Component.getComponentDevState(component).runOn);
709+
const componentDescription = await Odo.Instance.describeComponent(component.contextPath, !!Component.getComponentDevState(component).runOn);
710710
if (componentDescription.devForwardedPorts?.length > 0) {
711711
// try to find debug port
712712
const debugPortsCandidates:number[] = [];
@@ -792,7 +792,7 @@ export class Component extends OpenShiftItem {
792792
const CANCEL = 'Cancel';
793793
const response = await window.showWarningMessage(`Are you sure you want to delete the configuration for the component ${context.contextPath}?\nOpenShift Toolkit will no longer recognize the project as a component.`, DELETE_CONFIGURATION, CANCEL);
794794
if (response === DELETE_CONFIGURATION) {
795-
await Component.odo.deleteComponentConfiguration(context.contextPath);
795+
await Odo.Instance.deleteComponentConfiguration(context.contextPath);
796796
void commands.executeCommand('openshift.componentsView.refresh');
797797
}
798798
}

src/openshift/openshiftItem.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
import { commands, QuickPickItem, window } from 'vscode';
77
import { inputValue } from '../util/inputValue';
88
import { Oc } from '../oc/ocWrapper';
9-
import { Odo } from '../odo/odoWrapper';
10-
import { Project } from '../odo/project';
9+
import { Project } from '../oc/project';
1110
import { ServerlessFunctionView } from '../serverlessFunction/view';
1211
import * as NameValidator from './nameValidator';
1312

@@ -24,8 +23,6 @@ export class QuickPickCommand implements QuickPickItem {
2423
}
2524

2625
export default class OpenShiftItem {
27-
protected static readonly odo = Odo.Instance;
28-
2926
protected static readonly serverlessView: ServerlessFunctionView = ServerlessFunctionView.getInstance();
3027

3128
static async getName(message: string, offset?: string, defaultValue = ''): Promise<string> {

0 commit comments

Comments
 (0)