Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Components" sidebar section take a while to load #3850 #4473

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
4 changes: 2 additions & 2 deletions src/devfile-registry/devfileRegistryWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ 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 { OdoPreference } from '../odo/odoPreference';
import { DevfileData, DevfileInfo } from './devfileInfo';

export const DEVFILE_VERSION_LATEST: string = 'latest';
Expand Down Expand Up @@ -86,7 +86,7 @@ export class DevfileRegistry {
let registries: Registry[] = [];
const key = ExecutionContext.key('getRegistries');
if (this.executionContext && !this.executionContext.has(key)) {
registries = await Odo.Instance.getRegistries();
registries = await OdoPreference.Instance.getRegistries();
this.executionContext.set(key, registries);
} else {
registries = this.executionContext.get(key);
Expand Down
9 changes: 6 additions & 3 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/

import { Context as KcuContext } from '@kubernetes/client-node/dist/config_types';
import {
authentication,
commands, env, ExtensionContext, languages, QuickPickItemKind,
Expand All @@ -25,16 +26,16 @@ import { startTelemetry } from './telemetry';
import { ToolsConfig } from './tools';
import { TokenStore } from './util/credentialManager';
import { getNamespaceKind, KubeConfigUtils, setKubeConfig } from './util/kubeUtils';
import { Context as KcuContext } from '@kubernetes/client-node/dist/config_types';
import { setupWorkspaceDevfileContext } from './util/workspace';
import { registerCommands } from './vscommand';
import { OpenShiftTerminalManager } from './webview/openshift-terminal/openShiftTerminal';
import { WelcomePage } from './welcomePage';
import { registerYamlHandlers } from './yaml/yamlDocumentFeatures';

import { Oc } from './oc/ocWrapper';
import { K8S_RESOURCE_SCHEME, K8S_RESOURCE_SCHEME_READONLY, KubernetesResourceVirtualFileSystemProvider } from './k8s/vfs/kuberesources.virtualfs';
import { KubernetesResourceLinkProvider } from './k8s/vfs/kuberesources.linkprovider';
import { K8S_RESOURCE_SCHEME, K8S_RESOURCE_SCHEME_READONLY, KubernetesResourceVirtualFileSystemProvider } from './k8s/vfs/kuberesources.virtualfs';
import { Oc } from './oc/ocWrapper';
import { OdoPreference } from './odo/odoPreference';

// eslint-disable-next-line @typescript-eslint/no-empty-function
// this method is called when your extension is deactivated
Expand Down Expand Up @@ -274,6 +275,8 @@ export async function activate(extensionContext: ExtensionContext): Promise<unkn
void startTelemetry(extensionContext);
await verifyBinariesInRemoteContainer();

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

// Wait for finishing Kube Config setup
await setKubeConfigPromise;

Expand Down
174 changes: 174 additions & 0 deletions src/odo/odoPreference.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*-----------------------------------------------------------------------------------------------
* 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 fs from 'fs/promises';
import * as YAML from 'js-yaml';
import * as path from 'path';
import { TelemetryProps } from '../telemetry';
import { Platform } from '../util/platform';
import { Registry } from './componentType';


type OdoRegistryObject = {
Name: string;
URL: string;
secure: boolean;
};

type OdoSettingsObject = {
RegistryList: OdoRegistryObject[];
ConsentTelemetry;
};

type OdoPreferenceObject = {
kind: string,
apiversion: string,
OdoSettings: OdoSettingsObject;
};

export class OdoPreferenceError extends Error {
constructor(message: string, public readonly telemetryMessage = message, public readonly parent?, public readonly telemetryProps: TelemetryProps = {}) {
super(message);
Object.setPrototypeOf(this, new.target.prototype);
}
}

export class OdoPreference {
private static instance: OdoPreference;

public static DEFAULT_DEVFILE_REGISTRY_NAME: string = 'DefaultDevfileRegistry';
public static DEFAULT_DEVFILE_REGISTRY_URL: string = 'https://registry.devfile.io';
public static DEFAULT_DEVFILE_REGISTRY_SECURE: boolean = false;

private static DefaultOdoPreference: OdoPreferenceObject = {
kind: 'Preference',
apiversion: 'odo.dev/v1alpha1',
OdoSettings: {
RegistryList: [
{
Name: OdoPreference.DEFAULT_DEVFILE_REGISTRY_NAME,
URL: OdoPreference.DEFAULT_DEVFILE_REGISTRY_URL,
secure: OdoPreference.DEFAULT_DEVFILE_REGISTRY_SECURE
} as OdoRegistryObject
],
ConsentTelemetry: false
} as OdoSettingsObject
};


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

private getOdoPreferenceFile(): string {
// This value can be provided to set a seperate directory for users 'homedir' resolution
// note for mocking purpose ONLY
const customHomeDir = Platform.ENV.CUSTOM_HOMEDIR;
const configFileName = 'preference.yaml';

if (customHomeDir && customHomeDir.length > 0) {
return path.join(customHomeDir, '.odo', configFileName);
}

return path.join(Platform.getUserHomePath(), '.odo', configFileName);
}

public async getRegistries(): Promise<Registry[]> {
const odoPreference = await this.readOdoPreference();
return odoPreference.OdoSettings.RegistryList.map((r) => {
return {
name: r.Name,
url: r.URL,
secure: r.secure } as Registry;
});
}

public async addRegistry(name: string, url: string, token: string): Promise<Registry> {
const odoPreference = await this.readOdoPreference();
odoPreference.OdoSettings.RegistryList.push({
Name: name,
URL: url,
secure: !!token
} as never);
await this.writeOdoPreference(odoPreference);
return {
name,
secure: !!token,
url,
};
}

public async removeRegistry(name: string): Promise<void> {
const odoPreference = await this.readOdoPreference();
odoPreference.OdoSettings.RegistryList.splice(
odoPreference.OdoSettings.RegistryList.findIndex((registry) => registry.Name === name), 1
);
await this.writeOdoPreference(odoPreference);
}

private async readOdoPreference(): Promise<OdoPreferenceObject> {
let mergedPreference = OdoPreference.DefaultOdoPreference;
const odoPreferenceFilePath = this.getOdoPreferenceFile();
let isToBeReWritten: boolean = false;
try {
const odoPreferenceFile = await fs.readFile(odoPreferenceFilePath, 'utf8');
const odoPreferences = YAML.load(odoPreferenceFile) as OdoPreferenceObject;

// If `odoPreferences.OdoSettings.RegistryList` is `null` or doesn't contain the `DefaultDevfileRegistry` item
// we have to recover at least the 'DefaultDevfileRegistry` item on it because this whole list will be replaced
if (!odoPreferences.OdoSettings.RegistryList) {
odoPreferences.OdoSettings.RegistryList = OdoPreference.DefaultOdoPreference.OdoSettings.RegistryList;
isToBeReWritten = true;
} else {
if (!odoPreferences.OdoSettings.RegistryList.find((r) => r.Name === OdoPreference.DEFAULT_DEVFILE_REGISTRY_NAME)) {
odoPreferences.OdoSettings.RegistryList.push(OdoPreference.DefaultOdoPreference.OdoSettings.RegistryList[0]);
isToBeReWritten = true;
}
}

mergedPreference = { ...mergedPreference, ...odoPreferences };
} catch {
isToBeReWritten = true;
}

if(isToBeReWritten) {
await this.writeOdoPreference(mergedPreference);
}

return mergedPreference;
}

private async dirExists(path: string): Promise<boolean> {
try {
if ((await fs.stat(path)).isDirectory()) {
return true;
}
} catch {
// Ignore
}
return false;
}

private async writeOdoPreference(preference: any): Promise<any> {
const odoPreferenceFilePath = this.getOdoPreferenceFile();
try {
const preferenceYaml = YAML.dump(preference);
const odoPreferenceDir = path.parse(odoPreferenceFilePath).dir;
if (!await this.dirExists(odoPreferenceDir)) {
await fs.mkdir(odoPreferenceDir, { recursive: true, mode: 0o750} );
}
await fs.writeFile(this.getOdoPreferenceFile(), preferenceYaml, 'utf8');
} catch (err) {
throw new OdoPreferenceError(
`An error occured while creating the ODO Preference file at ${odoPreferenceFilePath}!`,
`Error when creating the ODO Preference file: ${odoPreferenceFilePath}`,
err
);
}
}
}
32 changes: 0 additions & 32 deletions src/odo/odoWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { ToolsConfig } from '../tools';
import { ChildProcessUtil, CliExitData } from '../util/childProcessUtil';
import { VsCommandError } from '../vscommand';
import { Command } from './command';
import { Registry } from './componentType';
import { ComponentDescription } from './componentTypeDescription';
import { BindableService } from './odoTypes';

Expand Down Expand Up @@ -176,37 +175,6 @@ export class Odo {
);
}

private async loadRegistryFromPreferences() {
const cliData = await this.execute(new CommandText('odo', 'preference view -o json'));
const prefs = JSON.parse(cliData.stdout) as { registries: Registry[] };
return prefs.registries;
}

public getRegistries(): Promise<Registry[]> {
return this.loadRegistryFromPreferences();
}

public async addRegistry(name: string, url: string, token: string): Promise<Registry> {
const command = new CommandText('odo', `preference add registry ${name} ${url}`);
if (token) {
command.addOption(new CommandOption('--token', token));
}
await this.execute(command);
return {
name,
secure: !!token,
url,
};
}

public async removeRegistry(name: string): Promise<void> {
await this.execute(
new CommandText('odo', `preference remove registry ${name}`, [
new CommandOption('--force'),
]),
);
}

/**
* Deletes all the odo configuration files associated with the component (`.odo`, `devfile.yaml`) located at the given path.
*
Expand Down
11 changes: 6 additions & 5 deletions src/registriesView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
Registry
} from './odo/componentType';
import { StarterProject } from './odo/componentTypeDescription';
import { OdoPreference } from './odo/odoPreference';
import { Odo } from './odo/odoWrapper';
import { inputValue, quickBtn } from './util/inputValue';
import { Progress } from './util/progress';
Expand Down Expand Up @@ -77,8 +78,8 @@ export class ComponentTypesView implements TreeDataProvider<ComponentType> {
let children: ComponentType[] = [];
if (!parent) {
const result = await this.getRegistries();
const newChildren = result.filter((reg) => reg.name === 'DefaultDevfileRegistry')
.concat(result.filter((reg) => reg.name !== 'DefaultDevfileRegistry').sort());
const newChildren = result.filter((reg) => reg.name === OdoPreference.DEFAULT_DEVFILE_REGISTRY_NAME)
.concat(result.filter((reg) => reg.name !== OdoPreference.DEFAULT_DEVFILE_REGISTRY_NAME).sort());
children = newChildren;
}
return children;
Expand Down Expand Up @@ -326,9 +327,9 @@ export class ComponentTypesView implements TreeDataProvider<ComponentType> {
try {
const devfileInfos = await DevfileRegistry.Instance.getDevfileInfoList(regURL);
if (devfileInfos.length > 0) {
const newRegistry = await Odo.Instance.addRegistry(regName, regURL, token);
const newRegistry = await OdoPreference.Instance.addRegistry(regName, regURL, token);
if (registryContext) {
await Odo.Instance.removeRegistry(registryContext.name);
await OdoPreference.Instance.removeRegistry(registryContext.name);
ComponentTypesView.instance.replaceRegistry(registryContext, newRegistry);
} else {
ComponentTypesView.instance.addRegistry(newRegistry);
Expand All @@ -353,7 +354,7 @@ export class ComponentTypesView implements TreeDataProvider<ComponentType> {
const yesNo = await window.showInformationMessage(
`Remove registry '${registry.name}'?`, 'Yes', 'No');
if (yesNo === 'Yes') {
await Odo.Instance.removeRegistry(registry.name);
await OdoPreference.Instance.removeRegistry(registry.name);
ComponentTypesView.instance.removeRegistry(registry);
ComponentTypesView.instance.subject.next();
}
Expand Down
4 changes: 3 additions & 1 deletion test/integration/alizerWrapper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { promisify } from 'util';
import { Uri, workspace } from 'vscode';
import { Alizer } from '../../src/alizer/alizerWrapper';
import { Oc } from '../../src/oc/ocWrapper';
import { OdoPreference } from '../../src/odo/odoPreference';
import { Odo } from '../../src/odo/odoWrapper';
import { LoginUtil } from '../../src/util/loginUtil';

Expand All @@ -22,6 +23,7 @@ suite('./alizer/alizerWrapper.ts', function () {
const password = process.env.CLUSTER_PASSWORD || 'developer';

suiteSetup(async function () {
await OdoPreference.Instance.getRegistries(); // This creates the ODO preferences, if needed
if (isOpenShift) {
try {
await LoginUtil.Instance.logout();
Expand Down Expand Up @@ -121,7 +123,7 @@ suite('./alizer/alizerWrapper.ts', function () {
await Odo.Instance.createComponentFromTemplateProject(
tmpFolder, 'my-component', 8080,
COMPONENT_TYPE, COMPONENT_VERSION,
'DefaultDevfileRegistry', 'dotnet50-example');
OdoPreference.DEFAULT_DEVFILE_REGISTRY_NAME, 'dotnet50-example');
try {
await fs.access(path.join(tmpFolder, 'devfile.yaml'));
} catch {
Expand Down
6 changes: 4 additions & 2 deletions test/integration/command.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { CliChannel } from '../../src/cli';
import { Oc } from '../../src/oc/ocWrapper';
import { Command } from '../../src/odo/command';
import { ComponentDescription } from '../../src/odo/componentTypeDescription';
import { OdoPreference } from '../../src/odo/odoPreference';
import { Odo } from '../../src/odo/odoWrapper';
import { LoginUtil } from '../../src/util/loginUtil';

Expand All @@ -34,6 +35,7 @@ suite('odo commands integration', function () {
const password = process.env.CLUSTER_PASSWORD || 'developer';

suiteSetup(async function() {
await OdoPreference.Instance.getRegistries(); // This creates the ODO preferences, if needed
if (isOpenShift) {
try {
await LoginUtil.Instance.logout();
Expand Down Expand Up @@ -87,7 +89,7 @@ suite('odo commands integration', function () {
Command.createLocalComponent(
componentType,
'2.0.0',
'DefaultDevfileRegistry',
OdoPreference.DEFAULT_DEVFILE_REGISTRY_NAME,
componentName,
8080,
componentStarterProject,
Expand Down Expand Up @@ -409,7 +411,7 @@ suite('odo commands integration', function () {
Command.createLocalComponent(
componentType,
'2.1.1',
'DefaultDevfileRegistry',
OdoPreference.DEFAULT_DEVFILE_REGISTRY_NAME,
componentName,
undefined,
componentStarterProject,
Expand Down
Loading
Loading