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
3 changes: 2 additions & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface CliExitData {
readonly error: cp.ExecException;
readonly stdout: string;
readonly stderr: string;
readonly cwd?: string;
}

export interface Cli {
Expand Down Expand Up @@ -91,7 +92,7 @@ export class CliChannel implements Cli {
// do not reject it here, because caller in some cases need the error and the streams
// to make a decision
// Filter update message text which starts with `---`
resolve({ error, stdout: stdoutFiltered, stderr });
resolve({ error, stdout: stdoutFiltered, stderr, cwd: opts.cwd });
});
});
}
Expand Down
92 changes: 58 additions & 34 deletions src/odo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import { Platform } from './util/platform';
import * as odo from './odo/config';
import { GlyphChars } from './util/constants';
import { Application } from './odo/application';
import { ComponentType } from './odo/componentType';
import { ComponentType, ComponentTypesJson, S2iComponentType, DevfileComponentType, S2iAdapter, DevfileAdapter, OdoComponentType, ComponentKind } from './odo/componentType';
import { Project } from './odo/project';
import { Component } from './odo/component';
import { ComponentsJson, DevfileComponentAdapter, S2iComponentAdapter } from './odo/component';
import { Url } from './odo/url';
import { Service } from './odo/service';
import { Command } from './odo/command';
Expand All @@ -45,6 +45,7 @@ export interface OpenShiftObject extends QuickPickItem {
path?: string;
builderImage?: BuilderImage;
iconPath?: Uri;
kind?: ComponentKind;
}

export enum ContextType {
Expand Down Expand Up @@ -260,18 +261,21 @@ export class OpenShiftComponent extends OpenShiftObjectImpl {
contextValue: ContextType,
contextPath: Uri = undefined,
compType: string = undefined,
public readonly kind: ComponentKind,
builderImage: BuilderImage = undefined) {
super(parent, name, contextValue, '', Collapsed, contextPath, compType, builderImage);
}
get iconPath(): Uri {
if (this.contextValue === ContextType.COMPONENT_PUSHED || this.contextValue === ContextType.COMPONENT || this.contextValue === ContextType.COMPONENT_NO_CONTEXT) {
let iconPath: Uri;
if (this.compType === odo.SourceType.GIT) {
return Uri.file(path.join(__dirname, "../../images/component", 'git.png'));
} if (this.compType === odo.SourceType.LOCAL) {
return Uri.file(path.join(__dirname, "../../images/component", 'workspace.png'));
} if (this.compType === odo.SourceType.BINARY) {
return Uri.file(path.join(__dirname, "../../images/component", 'binary.png'));
iconPath = Uri.file(path.join(__dirname, "../../images/component", 'git.png'));
} else if (this.compType === odo.SourceType.BINARY) {
iconPath = Uri.file(path.join(__dirname, "../../images/component", 'binary.png'));
} else {
iconPath = Uri.file(path.join(__dirname, "../../images/component", 'workspace.png'));
}
return iconPath;
}
}

Expand All @@ -292,7 +296,11 @@ export class OpenShiftComponent extends OpenShiftObjectImpl {
} else if (this.contextValue === ContextType.COMPONENT_NO_CONTEXT) {
suffix = `${GlyphChars.Space}${GlyphChars.NoContext} no context`;
}
return suffix;
return `${suffix}`;
}

get label(): string {
return `${this.name} (${this.kind})`;
}
}

Expand Down Expand Up @@ -328,12 +336,11 @@ export interface Odo {
getApplicationChildren(application: OpenShiftObject): Promise<OpenShiftObject[]>;
getComponents(application: OpenShiftObject, condition?: (value: OpenShiftObject) => boolean): Promise<OpenShiftObject[]>;
getComponentTypes(): Promise<string[]>;
getComponentTypesJson(): Promise<ComponentType[]>;
getComponentTypesJson(): Promise<ComponentType<S2iComponentType | DevfileComponentType>[]>;
getImageStreamRef(name: string, namespace: string): Promise<ImageStream>;
getComponentChildren(component: OpenShiftObject): Promise<OpenShiftObject[]>;
getRoutes(component: OpenShiftObject): Promise<OpenShiftObject[]>;
getComponentPorts(component: OpenShiftObject): Promise<odo.Port[]>;
getComponentTypeVersions(componentName: string): Promise<string[]>;
getStorageNames(component: OpenShiftObject): Promise<OpenShiftObject[]>;
getServiceTemplates(): Promise<string[]>;
getServiceTemplatePlans(svc: string): Promise<string[]>;
Expand Down Expand Up @@ -431,7 +438,7 @@ class OdoModel {
const execs: Promise<cliInstance.CliExitData>[] = [];
folders.forEach((folder)=> {
try {
execs.push(OdoImpl.Instance.execute(`odo list --path ${folder.uri.fsPath} -o json`, undefined, false));
execs.push(OdoImpl.Instance.execute(`odo describe -o json`, folder.uri.fsPath, false));
} catch (ignore) {
// ignore execution errors
}
Expand All @@ -440,8 +447,9 @@ class OdoModel {
results.forEach((result) => {
if (!result.error) {
try {
const compData = JSON.parse(result.stdout).items[0] as odo.Component;
compData && OdoImpl.data.setContextToSettings(compData);
const compData = JSON.parse(result.stdout) as odo.Component;
compData.status.context = result.cwd;
OdoImpl.data.setContextToSettings(compData);
} catch (err) {
// ignore unexpected parsing errors
}
Expand Down Expand Up @@ -598,32 +606,40 @@ export class OdoImpl implements Odo {
return (await this.getApplicationChildren(application)).filter(condition);
}

public async _getComponents(application: OpenShiftObject): Promise<OpenShiftObject[]> {
public async _getComponents(application: OpenShiftObject): Promise<OpenShiftComponent[]> {
const result: cliInstance.CliExitData = await this.execute(Command.listComponents(application.getParent().getName(), application.getName()), Platform.getUserHomePath());
const componentObject = this.loadItems<Component>(result).map(value => ({ name: value.metadata.name, sourceType: value.spec.sourceType }));

const deployedComponents = componentObject.map<OpenShiftObject>((value) => {
return new OpenShiftComponent(application, value.name, ContextType.COMPONENT_NO_CONTEXT, undefined, value.sourceType);
const componentsJson = this.loadJSON<ComponentsJson>(result.stdout);
const components = [
...componentsJson?.s2iComponents ? componentsJson.s2iComponents.map((item) => new S2iComponentAdapter(item)) : [],
...componentsJson?.devfileComponents ? componentsJson.devfileComponents.map((item) => new DevfileComponentAdapter(item)) : []
];

const deployedComponents = components.map<OpenShiftComponent>((value) => {
return new OpenShiftComponent(application, value.name, ContextType.COMPONENT_NO_CONTEXT, undefined, value.sourceType, value.kind);
});
const targetAppName = application.getName();
const targetPrjName = application.getParent().getName();

OdoImpl.data.getSettings().filter((comp) => comp.spec.app === targetAppName && comp.metadata.namespace === targetPrjName).forEach((comp) => {
const jsonItem = componentObject.find((item)=> item.name === comp.metadata.name);
const jsonItem = components.find((item)=> item.name === comp.metadata.name);
let item: OpenShiftObject;
if (jsonItem) {
item = deployedComponents.find((component) => component.getName() === comp.metadata.name);
}
const builderImage = {
const builderImage = comp.spec.type.includes(':') ?
{
name: comp.spec.type.split(':')[0],
tag: comp.spec.type.split(':')[1]
} : {
name: comp.spec.type,
tag: 'latest'
};
if (item && item.contextValue === ContextType.COMPONENT_NO_CONTEXT) {
item.contextPath = Uri.file(comp.status.context);
item.contextValue = ContextType.COMPONENT_PUSHED;
item.builderImage = builderImage;
} else {
deployedComponents.push(new OpenShiftComponent(application, comp.metadata.name, item ? item.contextValue : ContextType.COMPONENT, Uri.file(comp.status.context), comp.spec.sourceType, builderImage));
deployedComponents.push(new OpenShiftComponent(application, comp.metadata.name, item ? item.contextValue : ContextType.COMPONENT, Uri.file(comp.status.context), comp.spec.sourceType, comp.spec.sourceType ? ComponentKind.S2I : ComponentKind.DEVFILE, builderImage));
}
});

Expand All @@ -632,12 +648,16 @@ export class OdoImpl implements Odo {

public async getComponentTypes(): Promise<string[]> {
const items = await this.getComponentTypesJson();
return items.map((value:ComponentType) => value.metadata.name);
return items.map((value:OdoComponentType) => value.label);
}

public async getComponentTypesJson(): Promise<ComponentType[]> {
public async getComponentTypesJson(): Promise<OdoComponentType[]> {
const result: cliInstance.CliExitData = await this.execute(Command.listCatalogComponentsJson());
return this.loadItems<ComponentType>(result, (json) => json.s2iItems);
const compTypesJson = this.loadJSON<ComponentTypesJson>(result.stdout);
return [
...compTypesJson?.s2iItems ? compTypesJson.s2iItems.map((item) => new S2iAdapter(item)) : [],
...compTypesJson?.devfileItems ? compTypesJson.devfileItems.map((item) => new DevfileAdapter(item)) : []
];
}

public async getImageStreamRef(name: string, namespace: string): Promise<ImageStream> {
Expand Down Expand Up @@ -695,12 +715,6 @@ export class OdoImpl implements Odo {
return this.loadItems<Storage>(result).map<OpenShiftObject>((value) => new OpenShiftStorage(component, value.metadata.name));
}

public async getComponentTypeVersions(componentName: string): Promise<string[]> {
const result: cliInstance.CliExitData = await this.execute(Command.listCatalogComponentsJson());
const items = this.loadItems<ComponentType>(result, (json) => json.s2iItems).filter((value) => value.metadata.name === componentName);
return items.length > 0 ? items[0].spec.allTags : [];
}

public async getServiceTemplates(): Promise<string[]> {
let items: any[] = [];
const result: cliInstance.CliExitData = await this.execute(Command.listCatalogServicesJson(), Platform.getUserHomePath(), false);
Expand Down Expand Up @@ -854,7 +868,7 @@ export class OdoImpl implements Odo {
if (!targetApplication) {
await this.insertAndReveal(application);
}
await this.insertAndReveal(new OpenShiftComponent(application, name, ContextType.COMPONENT, location, 'local', {name: type, tag: version}));
await this.insertAndReveal(new OpenShiftComponent(application, name, ContextType.COMPONENT, location, 'local', version ? ComponentKind.S2I : ComponentKind.DEVFILE, {name: type, tag: version}));
}
let wsFolder: WorkspaceFolder;
if (workspace.workspaceFolders) {
Expand All @@ -880,7 +894,7 @@ export class OdoImpl implements Odo {
if (!targetApplication) {
await this.insertAndReveal(application);
}
await this.insertAndReveal(new OpenShiftComponent(application, name, ContextType.COMPONENT, context, odo.SourceType.GIT, {name: type, tag: version}));
await this.insertAndReveal(new OpenShiftComponent(application, name, ContextType.COMPONENT, context, odo.SourceType.GIT, version ? ComponentKind.S2I : ComponentKind.DEVFILE, version ? {name: type, tag: version} : undefined));
}
workspace.updateWorkspaceFolders(workspace.workspaceFolders? workspace.workspaceFolders.length : 0 , null, { uri: context });
return null;
Expand All @@ -893,7 +907,7 @@ export class OdoImpl implements Odo {
if (!targetApplication) {
await this.insertAndReveal(application);
}
this.insertAndReveal(new OpenShiftComponent(application, name, ContextType.COMPONENT, context, odo.SourceType.BINARY, {name: type, tag: version}));
this.insertAndReveal(new OpenShiftComponent(application, name, ContextType.COMPONENT, context, odo.SourceType.BINARY, version ? ComponentKind.S2I : ComponentKind.DEVFILE, {name: type, tag: version}));
}
workspace.updateWorkspaceFolders(workspace.workspaceFolders? workspace.workspaceFolders.length : 0 , null, { uri: context });
return null;
Expand Down Expand Up @@ -1008,7 +1022,7 @@ export class OdoImpl implements Odo {
comp.contextValue = ContextType.COMPONENT_PUSHED;
this.subject.next(new OdoEventImpl('changed', comp));
} else if (!comp) {
const newComponent = new OpenShiftComponent(app, added.metadata.name, ContextType.COMPONENT, Uri.file(added.status.context), added.spec.sourceType, { name: added.spec.type.split(':')[0], tag: added.spec.type.split(':')[1]});
const newComponent = new OpenShiftComponent(app, added.metadata.name, ContextType.COMPONENT, Uri.file(added.status.context), added.spec.sourceType, added.spec.type.split(':')[1] ? ComponentKind.S2I : ComponentKind.DEVFILE, { name: added.spec.type.split(':')[0], tag: added.spec.type.split(':')[1]});
this.insertAndRefresh(newComponent);
}
} else if (!app) {
Expand Down Expand Up @@ -1048,6 +1062,16 @@ export class OdoImpl implements Odo {
}
return data;
}

private loadJSON<I>(json: string): I {
let data: I;
try {
data = JSON.parse(json,);
} catch (ignore) {
// ignore parse errors and return empty array
}
return data;
}
}

export function getInstance(): Odo {
Expand Down
4 changes: 2 additions & 2 deletions src/odo/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ export class Command {
name: string,
folder: string,
): string {
return `odo create ${type}:${version} ${name} --context ${folder} --app ${app} --project ${project}`;
return `odo create ${type}${version?':':''}${version?version:''} ${name} ${version?'--s2i':''} --context ${folder} --app ${app} --project ${project}`;
}

@verbose
Expand All @@ -230,7 +230,7 @@ export class Command {
git: string,
ref: string,
): string {
return `odo create ${type}:${version} ${name} --git ${git} --ref ${ref} --app ${app} --project ${project}`;
return `odo create ${type}${version?':':''}${version?version:''} ${name} ${version?'--s2i':''} --git ${git} --ref ${ref} --app ${app} --project ${project}`;
}

@verbose
Expand Down
116 changes: 114 additions & 2 deletions src/odo/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/

import { ComponentMetadata } from "./config";
import { ComponentMetadata } from './config';
import { ComponentKind } from './componentType';

export interface Component {
export interface S2iComponent {
kind: 'Component';
apiVersion: string;
metadata: ComponentMetadata;
Expand All @@ -24,3 +25,114 @@ export interface Component {
state: string;
};
}

export interface DevfileComponent {
kind: "DevfileComponent";
apiVersion: string;
metadata: {
name: string;
creationTimestamp: string;
},
spec: {
namespace: string;
application: string;
componentType: string;
},
status: {
state: string;
}
}

export interface ComponentsJson {
kind: string;
apiVersion: string;
metadata: {
creationTimestamp: string;
},
// eslint-disable-next-line camelcase
s2iComponents: S2iComponent[];
// eslint-disable-next-line camelcase
devfileComponents: DevfileComponent[];
}

export interface Component <I> {
label: string;
name: string;
namespace: string;
app: string;
type: string;
sourceType: string;
kind: ComponentKind;
info: I;
}

export type OdoComponent = Component<S2iComponent | DevfileComponent>;

abstract class ComponentAdapter<I> implements Component<I> {
constructor(public readonly info: I, public readonly kind: ComponentKind) {
}

get label(): string {
return `${this.name} (${this.kind})`;
}

abstract get name(): string;
abstract get app(): string;
abstract get namespace(): string;
abstract get type(): string;
abstract get sourceType(): string;
}

export class S2iComponentAdapter extends ComponentAdapter<S2iComponent> implements Component<S2iComponent> {

constructor(public readonly info: S2iComponent) {
super(info, ComponentKind.S2I);
}

get name(): string {
return this.info.metadata.name;
}

get app(): string {
return this.info.spec.app;
};

get namespace(): string {
return this.info.metadata.namespace;
}

get type(): string {
return this.info.spec.type;
}

get sourceType(): string {
return this.info.spec.sourceType;
}
}

export class DevfileComponentAdapter extends ComponentAdapter<DevfileComponent> {

constructor(public readonly info: DevfileComponent) {
super(info, ComponentKind.DEVFILE);
}

get name(): string {
return this.info.metadata.name;
}

get app(): string {
return this.info.spec.application;
}

get namespace(): string {
return this.info.spec.namespace;
}

get type(): string {
return this.info.spec.componentType;
}

get sourceType(): string {
return 'local';
}
}
Loading