Skip to content

Commit 3ac01b9

Browse files
authored
Show cluster and registry nodes at top level in component types view (#2002)
* Show cluster and registry nodes at top level in component types view This PR fixes #1999. Signed-off-by: Denis Golovin [email protected]
1 parent 020e930 commit 3ac01b9

File tree

4 files changed

+97
-14
lines changed

4 files changed

+97
-14
lines changed

src/component.ts

+52-13
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,34 @@ import {
2626
ComponentTypesJson,
2727
DevfileComponentType,
2828
ImageStreamTag,
29+
isCluster,
2930
isDevfileComponent,
3031
isImageStreamTag,
32+
isRegistry,
3133
isS2iComponent,
34+
Registry,
3235
S2iComponentType
3336
} from './odo/componentType';
3437
import {
3538
isStarterProject,
3639
StarterProject
3740
} from './odo/componentTypeDescription';
3841
import { vsCommand, VsCommandError } from './vscommand';
42+
import { Cluster } from '@kubernetes/client-node/dist/config_types';
43+
import { KubeConfig } from '@kubernetes/client-node';
3944

40-
type ComponentType = S2iComponentType | DevfileComponentType | ImageStreamTag | StarterProject;
45+
type ComponentType = S2iComponentType | DevfileComponentType | ImageStreamTag | StarterProject | Cluster | Registry;
4146

4247
export enum ContextType {
4348
S2I_COMPONENT_TYPE = 's2iComponentType',
4449
DEVFILE_COMPONENT_TYPE = 'devfileComponentType',
4550
S2I_IMAGE_STREAM_TAG = 's2iImageStreamTag',
4651
DEVFILE_STARTER_PROJECT = 'devfileStarterProject',
52+
DEVFILE_REGISTRY = 'devfileRegistry',
53+
OFFLINE_CLUSTER = 'clusterOffline',
54+
ONLINE_LOGGEDOFF_CLUSER = 'clusterLoggedoffOnline',
55+
ONLINE_LOOGEDIN_CLUSER = 'clusterLoggedinOnline',
56+
OFFLINE_OR_LOGGEDOUT_CLUSTER = 'clusterOfflineOrLoggedout',
4757
}
4858

4959
export class ComponentTypesView implements TreeDataProvider<ComponentType> {
@@ -59,6 +69,7 @@ export class ComponentTypesView implements TreeDataProvider<ComponentType> {
5969
.onDidChangeTreeDataEmitter.event;
6070

6171
readonly odo: Odo = getInstance();
72+
private registries: Registry[];
6273

6374
createTreeView(id: string): TreeView<ComponentType> {
6475
if (!this.treeView) {
@@ -78,6 +89,19 @@ export class ComponentTypesView implements TreeDataProvider<ComponentType> {
7889

7990
// eslint-disable-next-line class-methods-use-this
8091
getTreeItem(element: ComponentType): TreeItem | Thenable<TreeItem> {
92+
if(isCluster(element)) {
93+
return {
94+
label: `${element.server}`,
95+
collapsibleState: TreeItemCollapsibleState.Collapsed,
96+
}
97+
}
98+
if (isRegistry(element)) {
99+
return {
100+
label: element.Name,
101+
description: element.URL,
102+
collapsibleState: TreeItemCollapsibleState.Collapsed,
103+
}
104+
}
81105
if(isS2iComponent(element)) {
82106
return {
83107
label: `${element.metadata.name} (s2i)`,
@@ -91,7 +115,7 @@ export class ComponentTypesView implements TreeDataProvider<ComponentType> {
91115
}
92116
if(isImageStreamTag(element)) {
93117
return {
94-
label: element.name,
118+
label: element.annotations['openshift.io/display-name']? element.annotations['openshift.io/display-name'] : element.name,
95119
contextValue: ContextType.S2I_IMAGE_STREAM_TAG,
96120
tooltip: element.annotations.description,
97121
description: element.annotations.description,
@@ -114,14 +138,14 @@ export class ComponentTypesView implements TreeDataProvider<ComponentType> {
114138
}
115139
}
116140
return {
117-
label: `${element.Name} (devfile)`,
141+
label: `${element.DisplayName} (devfile)`,
118142
contextValue: ContextType.DEVFILE_COMPONENT_TYPE,
119143
iconPath: {
120144
dark: Uri.file(path.join(__dirname, '..','..','images', 'component', 'component-type-dark.png')),
121145
light: Uri.file(path.join(__dirname, '..','..','images', 'component', 'component-type-light.png'))
122146
},
123147
tooltip: element.Description,
124-
collapsibleState: TreeItemCollapsibleState.Collapsed,
148+
collapsibleState: this.registries.length > 1? TreeItemCollapsibleState.None : TreeItemCollapsibleState.Collapsed,
125149
};
126150
}
127151

@@ -136,19 +160,39 @@ export class ComponentTypesView implements TreeDataProvider<ComponentType> {
136160
return data;
137161
}
138162

163+
private async getRegistries(): Promise<Registry[]> {
164+
if(!this.registries) {
165+
this.registries =await this.odo.getRegistries();
166+
}
167+
return this.registries;
168+
}
169+
139170
// eslint-disable-next-line class-methods-use-this
140171
async getChildren(parent: ComponentType): Promise<ComponentType[]> {
141172
let children: ComponentType[];
173+
142174
if (!parent) {
175+
const config = new KubeConfig();
176+
config.loadFromDefault();
177+
const cluster = config.getCurrentCluster();
178+
this.registries = await this.getRegistries();
179+
children = [cluster,...this.registries];
180+
} else if (isCluster(parent)) {
143181
const result: CliExitData = await this.odo.execute(Command.listCatalogComponentsJson());
144-
children = this.loadItems<ComponentTypesJson, ComponentType>(result, (data) => {
182+
const builders = this.loadItems<ComponentTypesJson, ComponentType>(result, (data) => {
145183
if (data.s2iItems) { // filter hidden tags
146184
data.s2iItems.forEach((s2iItem) => s2iItem.spec.imageStreamTags = s2iItem.spec.imageStreamTags.filter(tag => s2iItem.spec.nonHiddenTags.includes(tag.name)));
147185
} else { // when not logged or disconnected form cluster s2i items are not available
148186
data.s2iItems = [];
149187
}
150-
return [...data.s2iItems, ...data.devfileItems];
188+
return data.s2iItems;
151189
});
190+
children = [];
191+
builders.forEach((builder: S2iComponentType) => children.splice(children.length,0, ...builder.spec.imageStreamTags));
192+
} else if (isRegistry(parent) ) {
193+
const result: CliExitData = await this.odo.execute(Command.listCatalogComponentsJson());
194+
children = this.loadItems<ComponentTypesJson, DevfileComponentType>(result, (data) => data.devfileItems);
195+
children = children.filter((element:DevfileComponentType) => element.Registry.Name === parent.Name);
152196

153197
} else if (isS2iComponent(parent)) {
154198
children = parent.spec.imageStreamTags.map((tag:ImageStreamTag) => {
@@ -158,18 +202,12 @@ export class ComponentTypesView implements TreeDataProvider<ComponentType> {
158202
} else if (isDevfileComponent(parent)){
159203
const result: CliExitData = await this.odo.execute(Command.describeCatalogComponent(parent.Name));
160204
children = this.loadItems<ComponentTypeDescription, StarterProject>(result, (data) => data.Data.starterProjects);
205+
161206
children = children.map((starter:StarterProject) => {
162207
starter.typeName = parent.Name;;
163208
return starter;
164209
});
165210
}
166-
if (!parent) {
167-
children = children.sort((a, b) => {
168-
const aTi = this.getTreeItem(a) as TreeItem;
169-
const bTi = this.getTreeItem(b) as TreeItem;
170-
return aTi.label.localeCompare(bTi.label);
171-
});
172-
}
173211
return children;
174212
}
175213

@@ -179,6 +217,7 @@ export class ComponentTypesView implements TreeDataProvider<ComponentType> {
179217
}
180218

181219
refresh(): void {
220+
this.registries = undefined;
182221
this.onDidChangeTreeDataEmitter.fire();
183222
}
184223

src/odo.ts

+21-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { Platform } from './util/platform';
2020
import * as odo from './odo/config';
2121
import { GlyphChars } from './util/constants';
2222
import { Application } from './odo/application';
23-
import { ComponentType, ComponentTypesJson, ComponentKind, ComponentTypeAdapter } from './odo/componentType';
23+
import { ComponentType, ComponentTypesJson, ComponentKind, ComponentTypeAdapter, Registry, RegistryList } from './odo/componentType';
2424
import { Project } from './odo/project';
2525
import { ComponentsJson, DevfileComponentAdapter, S2iComponentAdapter } from './odo/component';
2626
import { Url } from './odo/url';
@@ -31,6 +31,7 @@ import { ImageStream } from './odo/imageStream';
3131
import { VsCommandError } from './vscommand';
3232
import { Storage } from './odo/storage';
3333
import bs = require('binary-search');
34+
import { CliExitData } from './cli';
3435

3536
const {Collapsed} = TreeItemCollapsibleState;
3637

@@ -368,6 +369,7 @@ export interface Odo {
368369
createComponentCustomUrl(component: OpenShiftObject, name: string, port: string, secure?: boolean): Promise<OpenShiftObject>;
369370
getOpenShiftObjectByContext(context: string): OpenShiftObject;
370371
loadItems<I>(result: cliInstance.CliExitData, fetch: (data) => I[]): I[];
372+
getRegistries(): Promise<Registry[]>;
371373
readonly subject: Subject<OdoEvent>;
372374
}
373375

@@ -472,6 +474,7 @@ class OdoModel {
472474
}
473475

474476
export class OdoImpl implements Odo {
477+
475478
public static data: OdoModel = new OdoModel();
476479

477480
public static ROOT: OpenShiftObject = new OpenShiftRoot();
@@ -1065,6 +1068,23 @@ export class OdoImpl implements Odo {
10651068
}
10661069
return data;
10671070
}
1071+
1072+
public loadItemsFrom<I,O>(result: CliExitData, fetch: (data:I) => O[] ): O[] {
1073+
let data: O[] = [];
1074+
try {
1075+
const items = fetch(JSON.parse(result.stdout));
1076+
if (items) data = items;
1077+
} catch (ignore) {
1078+
// ignore parse errors and return empty array
1079+
}
1080+
return data;
1081+
}
1082+
1083+
public async getRegistries(): Promise<Registry[]> {
1084+
const result = await this.execute(Command.listRegistries());
1085+
return this.loadItemsFrom<RegistryList, Registry>(result, (data) => data.registries);
1086+
}
1087+
10681088
}
10691089

10701090
export function getInstance(): Odo {

src/odo/command.ts

+4
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ export class Command {
5151
return `odo list --app ${app} --project ${project} -o json`;
5252
}
5353

54+
static listRegistries(): string {
55+
return 'odo registry list -o json';
56+
}
57+
5458
static listCatalogComponents(): string {
5559
return 'odo catalog list components';
5660
}

src/odo/componentType.ts

+20
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Licensed under the MIT License. See LICENSE file in the project root for license information.
44
*-----------------------------------------------------------------------------------------------*/
55

6+
import { Cluster } from '@kubernetes/client-node/dist/config_types';
67
import { Url } from 'url';
78
import { Ctx, Data } from './componentTypeDescription';
89
import { ComponentMetadata } from './config';
@@ -12,6 +13,16 @@ export enum ComponentKind {
1213
DEVFILE = 'devfile'
1314
}
1415

16+
export interface RegistryList {
17+
registries: Registry[];
18+
}
19+
20+
export interface Registry {
21+
readonly Name: string;
22+
readonly URL: string;
23+
readonly Secure: boolean;
24+
}
25+
1526
export interface ImageStreamTag {
1627
name: string,
1728
typeName?: string;
@@ -36,6 +47,14 @@ export function isImageStreamTag(tag: any): tag is ImageStreamTag {
3647
return tag.name && tag.annotations;
3748
}
3849

50+
export function isCluster(cluster: any): cluster is Cluster {
51+
return cluster.name && cluster.server && cluster.skipTLSVerify !== undefined;
52+
}
53+
54+
export function isRegistry(registry: any): registry is Registry {
55+
return registry.Name && registry.URL && registry.Secure !== undefined;
56+
}
57+
3958
export interface S2iComponentType {
4059
kind: 'ComponentType';
4160
apiVersion: string;
@@ -55,6 +74,7 @@ export interface RegistryRef {
5574

5675
export interface DevfileComponentType {
5776
Name: string;
77+
DisplayName: string;
5878
Description: string;
5979
Link: string;
6080
Registry: RegistryRef;

0 commit comments

Comments
 (0)