Skip to content

Commit

Permalink
Devfile Registry editor is to allow devfile version selection and use…
Browse files Browse the repository at this point in the history
… when creating a component redhat-developer#4189

Fixes: redhat-developer#4189
Issue: redhat-developer#3850

Signed-off-by: Victor Rubezhny <[email protected]>
  • Loading branch information
vrubezhny committed Sep 14, 2024
1 parent 58a1894 commit 7583093
Show file tree
Hide file tree
Showing 31 changed files with 1,654 additions and 976 deletions.
4 changes: 4 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@

import { VSCodeSettings } from '@redhat-developer/vscode-redhat-telemetry/lib/common/vscode/settings';
import * as cp from 'child_process';
import * as hasha from 'hasha';
import * as vscode from 'vscode';
import { CommandText } from './base/command';
import { ToolsConfig } from './tools';
import { ChildProcessUtil, CliExitData } from './util/childProcessUtil';
import { VsCommandError } from './vscommand';

export class ExecutionContext extends Map<string, any> {
public static key(value: string): string {
return hasha(value);
}
}

export class CliChannel {
Expand Down
65 changes: 65 additions & 0 deletions src/devfile-registry/devfileInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*-----------------------------------------------------------------------------------------------
* Copyright (c) Red Hat, Inc. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/
//
// The Types representing the output of
// 'odo registry [--devfile-registry="<Devfile Registry Name>" -o=json'
//

import { Data } from '../odo/componentTypeDescription';

export type DevfileRegistryInfo = {
name: string,
url: string,
secure: boolean
};

export type DevfileLinksInfo = {
any
};

export type DevfileCommandGroupsInfo = {
build: boolean,
debug: boolean,
deploy: boolean,
run: boolean,
test: boolean
};

export type DevfileVersionInfo = {
version: string,
schemaVersion: string,
default: boolean,
description: string,
tags: string[],
icon: string,
links: DevfileLinksInfo,
commandGroups: DevfileCommandGroupsInfo,
resources: string[],
starterProjects: string[],
};

export type DevfileInfo = {
name: string,
displayName: string,
description: string,
type: string,
tags: string[],
architectures: string[],
icon: string,
projectType: string,
language: string,
provider: string,
supportUrl: string,
versions: DevfileVersionInfo[],
registry?: DevfileRegistryInfo
};

export type DevfileInfoExt = DevfileInfo & {
proposedVersion?: string
};

export type DevfileData = Data & {
yaml: string;
};
183 changes: 183 additions & 0 deletions src/devfile-registry/devfileRegistryWrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/*-----------------------------------------------------------------------------------------------
* Copyright (c) Red Hat, Inc. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/
import * as https from 'https';
import * as YAML from 'js-yaml';
import { ExecutionContext } from '../cli';
import { Registry } from '../odo/componentType';
import { Odo } from '../odo/odoWrapper';
import { DevfileData, DevfileInfo } from './devfileInfo';

export const DEVFILE_VERSION_LATEST: string = 'latest';

/**
* Wraps some the Devfile Registry REST API calls.
*/
export class DevfileRegistry {
private static instance: DevfileRegistry;

private executionContext: ExecutionContext = new ExecutionContext();

public static get Instance(): DevfileRegistry {
if (!DevfileRegistry.instance) {
DevfileRegistry.instance = new DevfileRegistry();
}
return DevfileRegistry.instance;
}

private constructor() {
// no state
}

/**
* Get list of Devfile Infos from Registry.
*
* GET http://{registry host}/v2index/all
*
* @param url Devfile Registry URL
* @param abortTimeout (Optional) If provided, allow cancelling the operation by timeout
* @param abortController (Optional) If provided, allows cancelling the operation by signal
*/
public async getDevfileInfoList(url: string, abortTimeout?: number, abortController?: AbortController): Promise<DevfileInfo[]> {
const requestUrl = `${url}/v2index/all`;
const key = ExecutionContext.key(requestUrl);
if (this.executionContext && this.executionContext.has(key)) {
return this.executionContext.get(key);
}
const rawList = await DevfileRegistry._get(`${url}/v2index/all`, abortTimeout, abortController);
const jsonList = JSON.parse(rawList);
this.executionContext.set(key, jsonList);
return jsonList;
}

/**
* Get Devfile of specified version from Registry.
*
* GET http://{registry host}/devfiles/{stack}/{version}
*
* @param url Devfile Registry URL
* @param stack Devfile stack
* @param version (Optional) If specified, the version of Devfile to be received, otherwize 'latest' version is requested
* @param abortTimeout (Optional) If provided, allow cancelling the operation by timeout
* @param abortController (Optional) If provided, allows cancelling the operation by signal
*/
public async _getDevfile(url: string, stack: string, version?: string, abortTimeout?: number, abortController?: AbortController): Promise<string> {
const requestUrl = `${url}/devfiles/${stack}/${version ? version : DEVFILE_VERSION_LATEST}`;
const key = ExecutionContext.key(requestUrl);
if (this.executionContext && this.executionContext.has(key)) {
return this.executionContext.get(key);
}
const devfile = DevfileRegistry._get(`${url}/devfiles/${stack}/${version ? version : DEVFILE_VERSION_LATEST}`,
abortTimeout, abortController);
this.executionContext.set(key, devfile);
return devfile;
}

/**
* Returns a list of the devfile registries from ODO preferences.
*
* @returns a list of the devfile registries
*/
public async getRegistries(registryUrl?: string): Promise<Registry[]> {
// Return only registries registered for user (from ODO preferences)
// and filter by registryUrl (if provided)

let registries: Registry[] = [];
const key = ExecutionContext.key('getRegistries');
if (this.executionContext && !this.executionContext.has(key)) {
registries = await Odo.Instance.getRegistries();
this.executionContext.set(key, registries);
} else {
registries = this.executionContext.get(key);
}

return !registries ? [] :
registries.filter((reg) => {
if (registryUrl) {
return (reg.url === registryUrl)
}
return true;
});
}

/**
* Returns a list of the devfile registries with their devfiles attached.
*
* @returns a list of the devfile registries with their devfiles attached
*/
public async getRegistryDevfileInfos(registryUrl?: string): Promise<DevfileInfo[]> {
// Return only registries registered for user (from ODO preferences)
// and filter by registryUrl (if provided)
const registries: Registry[] = await this.getRegistries(registryUrl);
if (!registries || registries.length === 0) {
throw new Error('No Devfile registries available. Default registry is missing');
}

const devfiles: DevfileInfo[] = [];
await Promise.all(registries
.map(async (registry): Promise<void> => {
const devfileInfoList = (await this.getDevfileInfoList(registry.url))
.filter((devfileInfo) => 'stack' === devfileInfo.type.toLowerCase());
devfileInfoList.forEach((devfileInfo) => {
devfileInfo.registry = registry;
});
devfiles.push(...devfileInfoList);
}));

return devfiles.sort((a, b) => (a.name < b.name ? -1 : 1));
}

/**
* Returns a devfile data with raw devvile text attached
*
* @returns a devfile data with raw devvile text attached
*/
public async getRegistryDevfile(registryUrl: string, name: string, version?: string): Promise<DevfileData> {
const rawDevfile = await this._getDevfile(registryUrl, name, version ? version : 'latest');
const devfile = YAML.load(rawDevfile) as DevfileData;
devfile.yaml = rawDevfile;
return devfile;
}

private static async _get(url: string, abortTimeout?: number, abortController?: AbortController): Promise<string> {
return new Promise<string>((resolve, reject) => {
const signal = abortController?.signal;
const timeout = abortTimeout ? abortTimeout : 5000;
const options = { rejectUnauthorized: false, signal, timeout };
let result: string = '';
https.get(url, options, (response) => {
if (response.statusCode < 500) {
response.on('data', (d) => {
result = result.concat(d);
});
response.resume();
response.on('end', () => {
if (!response.complete) {
reject(new Error(`The connection was terminated while the message was still being sent: ${response.statusMessage}`));
} else {
resolve(result);
}
});
} else {
reject(new Error(`Connect error: ${response.statusMessage}`));
}
}).on('error', (e) => {
reject(new Error(`Connect error: ${e}`));
}).on('success', (s) => {
resolve(result);
});
});
}

/**
* Clear the Execution context as well as all cached data
*/
public clearCache() {
if (this.executionContext) {
this.executionContext.clear();
}
this.executionContext = new ExecutionContext();
}

}
16 changes: 8 additions & 8 deletions src/odo/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,24 @@ export class Command {

@verbose
static createLocalComponent(
type = '', // will use empty string in case of undefined type passed in
devfileType = '', // will use empty string in case of undefined devfileType passed in
devfileVersion: string = undefined,
registryName: string,
name: string,
portNumber: number,
starter: string = undefined,
useExistingDevfile = false,
customDevfilePath = '',
devfileVersion: string = undefined,
customDevfilePath = ''
): CommandText {
const cTxt = new CommandText('odo', 'init', [
new CommandOption('--name', name)
]
);
if (type !== '') {
cTxt.addOption(new CommandOption('--devfile', type));
if (devfileType !== '') {
cTxt.addOption(new CommandOption('--devfile', devfileType));
}
if (devfileVersion) {
cTxt.addOption(new CommandOption('--devfile-version', devfileVersion, false));
}
if (registryName) {
cTxt.addOption(new CommandOption('--devfile-registry', registryName));
Expand All @@ -90,9 +93,6 @@ export class Command {
if (customDevfilePath.length > 0) {
cTxt.addOption(new CommandOption('--devfile-path', customDevfilePath, false));
}
if (devfileVersion) {
cTxt.addOption(new CommandOption('--devfile-version', devfileVersion, false));
}
if (portNumber) {
cTxt.addOption(new CommandOption(' --run-port', portNumber.toString(), false));
}
Expand Down
2 changes: 2 additions & 0 deletions src/odo/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/

// TODO: Not used. A candidate to removal?

export interface ComponentsJson {
// list is not present when there are no components
// i.e. if there are no components the JSON is `{}`
Expand Down
Loading

0 comments on commit 7583093

Please sign in to comment.