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
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,15 +104,15 @@
"internalConsoleOptions": "neverOpen"
},
{
"type": "pwa-chrome",
"type": "chrome",
"request": "launch",
"name": "Launch 'Cluster Editor' in Chrome",
"file": "${workspaceFolder}/out/clusterViewer/index.html",
"webRoot": "${workspaceFolder}",
"trace": true
},
{
"type": "pwa-chrome",
"type": "chrome",
"request": "launch",
"name": "Launch 'Devfile Registry viewer' in Chrome",
"file": "${workspaceFolder}/out/devFileRegistryViewer/index.html",
Expand Down
8 changes: 1 addition & 7 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,9 @@ import * as cp from 'child_process';
import { CommandText } from './base/command';
import { ToolsConfig } from './tools';
import { ChildProcessUtil, CliExitData } from './util/childProcessUtil';
import { hash } from './util/utils';
import { ExecutionContext } from './util/utils';
import { VsCommandError } from './vscommand';

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

export class CliChannel {

private static telemetrySettings = new VSCodeSettings();
Expand Down
2 changes: 1 addition & 1 deletion src/devfile-registry/devfileRegistryWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import { get as httpGet } from 'http';
import { get as httpsGet } from 'https';
import * as YAML from 'js-yaml';
import { ExecutionContext } from '../cli';
import { Registry } from '../odo/componentType';
import { OdoPreference } from '../odo/odoPreference';
import { ExecutionContext } from '../util/utils';
import { DevfileData, DevfileInfo } from './devfileInfo';

export const DEVFILE_VERSION_LATEST: string = 'latest';
Expand Down
29 changes: 29 additions & 0 deletions src/devfile/commandResolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*-----------------------------------------------------------------------------------------------
* Copyright (c) Red Hat, Inc. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/
import { Command, Data } from '../odo/componentTypeDescription';

export class CommandResolver {
public static getCommand(devfile: Data, commandId: string): Command {
const command = devfile.commands.find(
(c) => c.id.toLowerCase() === commandId.toLowerCase(),
);

if (!command) {
throw new Error(`Command '${commandId}' not found`);
}

return command;
}

public static getAllCommandsMap(devfile: Data): Map<string, Command> {
const map = new Map<string, Command>();

for (const command of devfile.commands) {
map.set(command.id.toLowerCase(), command);
}

return map;
}
}
24 changes: 24 additions & 0 deletions src/devfile/compositeCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*-----------------------------------------------------------------------------------------------
* Copyright (c) Red Hat, Inc. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/

import { ComponentWorkspaceFolder } from '../odo/workspace';
import { Command } from '../odo/componentTypeDescription';
import { DevfileCommandRunner } from './devfileCommandRunner';

export class CompositeCommand {

public static async execute(
componentFolder: ComponentWorkspaceFolder,
command: Command,
): Promise<void> {

for (const childId of command.composite.commands) {
await DevfileCommandRunner.execute(
componentFolder,
childId,
);
}
}
}
70 changes: 70 additions & 0 deletions src/devfile/devfileCommandRunner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*-----------------------------------------------------------------------------------------------
* Copyright (c) Red Hat, Inc. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/

import { ComponentWorkspaceFolder } from '../odo/workspace';
import { Command } from '../odo/componentTypeDescription';
import { CommandResolver } from './commandResolver';
import { ExecCommandExecutor } from './execCommand';

export class DevfileCommandRunner {
public static async execute(
componentFolder: ComponentWorkspaceFolder,
commandId: string,
): Promise<void> {
const devfile = componentFolder.component.devfileData.devfile;

const command = CommandResolver.getCommand(devfile, commandId);

await this.executeCommand(componentFolder, command);
}

private static async executeCommand(
componentFolder: ComponentWorkspaceFolder,
command: Command,
): Promise<void> {
if (command.exec) {
await ExecCommandExecutor.execute(componentFolder, command.id, command.exec);

return;
}

if (command.composite) {
const devfile = componentFolder.component.devfileData.devfile;

const commandMap = CommandResolver.getAllCommandsMap(devfile);

const children = command.composite.commands.map((id) => {
const child = commandMap.get(id.toLowerCase());

if (!child) {
throw new Error(`Command '${id}' not found`);
}

return child;
});

const isParallel =
(
command.composite as {
parallel?: boolean;
}
).parallel === true;

if (isParallel) {
await Promise.all(
children.map((child) => this.executeCommand(componentFolder, child)),
);
} else {
for (const child of children) {
await this.executeCommand(componentFolder, child);
}
}

return;
}

throw new Error(`Unsupported command '${command.id}'`);
}
}
47 changes: 47 additions & 0 deletions src/devfile/execCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*-----------------------------------------------------------------------------------------------
* Copyright (c) Red Hat, Inc. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/

import { CommandOption, CommandText } from '../base/command';
import { Oc } from '../oc/ocWrapper';
import { Exec } from '../odo/componentTypeDescription';
import { ComponentWorkspaceFolder } from '../odo/workspace';
import { OpenShiftTerminalManager } from '../webview/openshift-terminal/openShiftTerminal';
import { DevfileResolver } from './devfileResolver';
import { VariableResolver } from './variableResolver';

export class ExecCommandExecutor {
public static async execute(
componentFolder: ComponentWorkspaceFolder,
commandId: string,
exec: Exec,
): Promise<void> {
const rawDevfile = componentFolder.component.devfileData.devfile;

const resolver = new DevfileResolver();
const devfile = await resolver.resolve(rawDevfile);

const resolvedExec = VariableResolver.resolveExec(devfile, exec);

const componentName = devfile.metadata.name;

const podName = await Oc.Instance.getComponentPod(componentName);

const command = new CommandText('oc', 'exec', [
new CommandOption(podName),
new CommandOption('-c'),
new CommandOption(resolvedExec.component),
new CommandOption('--'),
new CommandOption('sh'),
new CommandOption('-c'),
new CommandOption(`cd ${resolvedExec.workingDir} && ${resolvedExec.commandLine}`),
]);

void OpenShiftTerminalManager.getInstance().createTerminal(
command,
`Component ${componentName}: Run '${commandId}' Command`,
componentFolder.contextPath,
);
}
}
27 changes: 27 additions & 0 deletions src/devfile/parallelCompositeCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*-----------------------------------------------------------------------------------------------
* Copyright (c) Red Hat, Inc. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/

import { Command } from '../odo/componentTypeDescription';
import { ComponentWorkspaceFolder } from '../odo/workspace';
import { DevfileCommandRunner } from './devfileCommandRunner';

export class ParallelCompositeCommand {

public static async execute(
componentFolder: ComponentWorkspaceFolder,
command: Command,
): Promise<void> {

await Promise.all(
command.composite.commands.map(
childId =>
DevfileCommandRunner.execute(
componentFolder,
childId,
),
),
);
}
}
68 changes: 68 additions & 0 deletions src/devfile/variableResolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*-----------------------------------------------------------------------------------------------
* Copyright (c) Red Hat, Inc. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/

import { Container, Data, Exec } from '../odo/componentTypeDescription';

export class VariableResolver {
private static readonly VARIABLE_REGEX = /\$\{([^}]+)\}/g;

public static resolveExec(devfile: Data, exec: Exec): Exec {
return {
...exec,
workingDir: this.resolveValue(devfile, exec.workingDir ?? '/projects', exec.component),
commandLine: this.resolveValue(devfile, exec.commandLine, exec.component),
};
}

public static resolveValue(devfile: Data, value: string, componentName?: string): string {
if (!value) {
return value;
}

return value.replace(this.VARIABLE_REGEX, (_, variable) =>
this.resolveVariable(devfile, variable, componentName),
);
}

private static resolveVariable(
devfile: Data,
variable: string,
componentName?: string,
): string {
if (variable === 'PROJECT_SOURCE') {
return '/projects';
}

if (componentName) {
const component = devfile.components.find((c) => c.name === componentName);

const container = component?.container;

const envValue = this.findEnvValue(container, variable);

if (envValue) {
return envValue;
}
}

return process.env[variable] ?? `\${${variable}}`;
}

private static findEnvValue(
container: Container | undefined,
variable: string,
): string | undefined {
const env = (
container as unknown as {
env?: {
name: string;
value: string;
}[];
}
)?.env;

return env?.find((e) => e.name === variable)?.value;
}
}
3 changes: 1 addition & 2 deletions src/explorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import {
workspace
} from 'vscode';
import { CommandOption, CommandText } from './base/command';
import { ExecutionContext } from './cli';
import * as Helm from './helm/helm';
import { HelmRepo } from './helm/helmChartType';
import { getOutputFormat, helmfsUri, kubefsUri } from './k8s/vfs/kuberesources.utils';
Expand All @@ -37,7 +36,7 @@ import { getKubeConfigFiles, getNamespaceKind, isOpenShiftCluster, KubeConfigInf
import { LoginUtil } from './util/loginUtil';
import { Platform } from './util/platform';
import { Progress } from './util/progress';
import { imagePath } from './util/utils';
import { ExecutionContext, imagePath } from './util/utils';
import { FileContentChangeNotifier, WatchUtil } from './util/watch';
import { vsCommand } from './vscommand';
import { CustomResourceDefinitionStub, K8sResourceKind } from './webview/common/createServiceTypes';
Expand Down
4 changes: 2 additions & 2 deletions src/k8s/vfs/kuberesources.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import * as _ from 'lodash';
import { Diagnostic, DiagnosticSeverity, FileStat, FileType, Range, TextDocument, Uri, workspace } from 'vscode';
import { Document, isMap, isPair, isScalar, isSeq, Pair, parse, ParsedNode, parseDocument, stringify } from 'yaml';
import { CommandOption, CommandText } from '../../base/command';
import { CliChannel, ExecutionContext } from '../../cli';
import { CliChannel } from '../../cli';
import { Oc } from '../../oc/ocWrapper';
import { YAML_STRINGIFY_OPTIONS } from '../../util/utils';
import { ExecutionContext, YAML_STRINGIFY_OPTIONS } from '../../util/utils';

export const K8S_RESOURCE_SCHEME = 'osmsx'; // Changed from 'k8smsx' to 'osmsx' to not make a conflict with k8s extension
export const K8S_RESOURCE_SCHEME_READONLY = 'osmsxro'; // Changed from 'k8smsxro' to 'osmsxro' to not make a conflict with k8s extension
Expand Down
35 changes: 32 additions & 3 deletions src/oc/ocWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@
import { Cluster, Context, User } from '@kubernetes/client-node';
import { KubernetesObject } from '@kubernetes/client-node/dist/types';
import * as fs from 'fs/promises';
import path from 'path';
import * as tmp from 'tmp';
import validator from 'validator';
import { CommandOption, CommandText } from '../base/command';
import { CliChannel, ExecutionContext } from '../cli';
import { CliChannel } from '../cli';
import { CliExitData } from '../util/childProcessUtil';
import { isOpenShiftCluster, KubeConfigInfo, loadKubeConfig, serializeKubeConfig } from '../util/kubeUtils';
import { ExecutionContext } from '../util/utils';
import { findDevfiles, getComponentName } from './devfileUtils';
import { Project } from './project';
import { ClusterType, KubernetesConsole } from './types';
import { findDevfiles, getComponentName } from './devfileUtils';
import path from 'path';

/**
* A wrapper around the `oc` CLI tool.
Expand Down Expand Up @@ -1131,4 +1132,32 @@ export class Oc {

await this.deleteOdoFiles(componentPath, componentName);
}

public async getComponentPod(componentName: string): Promise<string> {

const selectors = [
`app.kubernetes.io/instance=${componentName}`,
`app.kubernetes.io/component=${componentName}`,
`component=${componentName}`,
`app=${componentName}`
];

for (const selector of selectors) {

const pods =
await this.getKubernetesObjects(
'pods',
undefined,
selector
);

if (pods.length > 0) {
return pods[0].metadata.name as string;
}
}

throw new Error(
`No running pod found for component '${componentName}'`
);
}
}
Loading
Loading