Skip to content

Commit e9a96ff

Browse files
authored
Update extension to work with Porter v1 (#58)
* Update extension to work with Porter v1 When Porter released v1, it changed the json structure of some of its commands. For example, standardizing on a naming convention (camelCase), or splitting up some commands in two like seeing an installation and its history. I have updated the extension to work with the current v1 json structure of Porter's command output. This doesn't impact the schema (autocomplete/intellisense) when viewing a porter.yaml, that is controlled by the `porter schema` command in Porter itself actually. What this does do is fix the Porter Installation panel that is displayed on the left side of the VS Code window. You can now list installations, see previous runs, their logs and outputs again. Signed-off-by: Carolyn Van Slyck <[email protected]> * Explain how the namespace is defaulted when installing a bundle with the extension Signed-off-by: Carolyn Van Slyck <[email protected]> Signed-off-by: Carolyn Van Slyck <[email protected]>
1 parent 072615e commit e9a96ff

File tree

7 files changed

+99
-65
lines changed

7 files changed

+99
-65
lines changed

src/debugger/configuration-provider.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ async function resolveDebugConfiguration(folder: vscode.WorkspaceFolder | undefi
6666
return;
6767
}
6868

69-
const installInputs: InstallInputs = { parameters: parameters.value, credentialSet: credentialSet.value };
69+
// The namespace is set to "" to make the compiler happy. When porter install is run, the current namespace defined in the porter config file in PORTER_HOME is used.
70+
const installInputs: InstallInputs = { namespace: "", parameters: parameters.value, credentialSet: credentialSet.value };
7071

7172
config.installInputs = installInputs;
7273
}

src/debugger/runtime.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,11 @@ export class PorterInstallRuntime extends EventEmitter {
128128
private credentials: LazyVariableInfo[] | undefined = undefined;
129129

130130
public async getCredentials(): Promise<LazyVariableInfo[]> {
131-
if (this.credentials !== undefined) {
131+
if (this.credentials !== undefined) {
132132
return this.credentials;
133133
}
134134
if (this.actionInputs && this.actionInputs.credentialSet) {
135-
const credentials = await porter.getCredentials(shell, this.actionInputs.credentialSet);
135+
const credentials = await porter.getCredentials(shell, this.actionInputs.namespace, this.actionInputs.credentialSet);
136136
if (credentials.succeeded) {
137137
this.credentials = credentials.result.credentials.map((c) => ({ name: c.name, value: () => this.evaluateCredential(c.source) }));
138138
return this.credentials;
@@ -200,7 +200,7 @@ export class PorterInstallRuntime extends EventEmitter {
200200
}
201201

202202
private run(stepEvent?: string) {
203-
for (let ln = this.currentLine+1; ln < this.sourceLines.length; ln++) {
203+
for (let ln = this.currentLine + 1; ln < this.sourceLines.length; ln++) {
204204
if (this.fireEventsForLine(ln, stepEvent)) {
205205
this.currentLine = ln;
206206
return true;
@@ -238,7 +238,7 @@ export class PorterInstallRuntime extends EventEmitter {
238238
return false;
239239
}
240240

241-
private sendEvent(event: string, ... args: any[]) {
241+
private sendEvent(event: string, ...args: any[]) {
242242
setImmediate((_) => {
243243
this.emit(event, ...args);
244244
});

src/debugger/session-protocol.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const EVENT_OUTPUT = 'output';
88
export const EVENT_END = 'end';
99

1010
export interface InstallInputs {
11+
readonly namespace: string;
1112
readonly parameters?: { [key: string]: string };
1213
readonly credentialSet?: string;
1314
}

src/explorer/installation/installation-explorer.ts

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { ViewLogs } from '../../commands/viewlogs';
55
import { ViewOutputs } from '../../commands/viewoutputs';
66

77
import * as porter from '../../porter/porter';
8-
import { Installation, InstallationHistoryEntry } from '../../porter/porter.objectmodel';
8+
import { Installation, Run } from '../../porter/porter.objectmodel';
99
import { Shell } from '../../utils/shell';
1010
import { viewLogs } from '../../views/logs';
1111
import { viewOutputs } from '../../views/outputs';
@@ -48,68 +48,67 @@ export class InstallationExplorer implements vscode.TreeDataProvider<Installatio
4848

4949
class InstallationNode implements Node<InstallationExplorerTreeNode>, ViewOutputs {
5050
readonly kind = 'installation' as const;
51-
constructor(private readonly shell: Shell, private readonly installation: Installation) {}
51+
constructor(private readonly shell: Shell, private readonly installation: Installation) { }
5252
async getChildren(): Promise<InstallationExplorerTreeNode[]> {
53-
const installationDetail = await porter.getInstallationDetail(this.shell, this.installation.Name);
53+
const installationDetail = await porter.getInstallationDetail(this.shell, this.installation.namespace, this.installation.name);
5454
if (!installationDetail.succeeded) {
5555
return [new ExplorerErrorNode(installationDetail.error[0])];
5656
}
57-
return installationDetail.result.History.map((e) => new InstallationHistoryEntryNode(this.shell, e));
57+
return installationDetail.result.history?.map((run) => new RunEntryNode(this.shell, run));
5858
}
5959
getTreeItem(): vscode.TreeItem {
60-
const treeItem = new vscode.TreeItem(this.installation.Name, vscode.TreeItemCollapsibleState.Collapsed);
60+
const fullName = this.installation.namespace ? `${this.installation.namespace}/${this.installation.name}` : this.installation.name;
61+
const treeItem = new vscode.TreeItem(fullName, vscode.TreeItemCollapsibleState.Collapsed);
6162
treeItem.contextValue = 'porter.installation porter.has-outputs';
62-
treeItem.tooltip = `Last action: ${this.installation.Action} (${this.installation.Status})\nat: ${displayTime(this.installation.Modified)}`;
63+
treeItem.tooltip = `${this.installation._calculated.displayInstallationState} (${this.installation._calculated.displayInstallationStatus})\nat: ${displayTime(this.installation.status.modified)}`;
6364
return treeItem;
6465
}
6566

6667
async viewOutputs(): Promise<CommandResult> {
67-
const installationDetail = await porter.getInstallationDetail(this.shell, this.installation.Name);
68-
if (!installationDetail.succeeded) {
69-
await vscode.window.showErrorMessage(`Can't view outputs: ${installationDetail.error[0]}`);
68+
const outputs = await porter.listInstallationOutputs(this.shell, this.installation.namespace, this.installation.name);
69+
if (!outputs.succeeded) {
70+
await vscode.window.showErrorMessage(`Can't view outputs: ${outputs.error[0]}`);
7071
return CommandResult.Failed;
7172
}
72-
const title = `Porter Outputs - ${this.installation.Name}`;
73-
await viewOutputs(title, installationDetail.result.Outputs);
73+
const title = `Porter Outputs - ${this.installation.name}`;
74+
await viewOutputs(title, outputs.result);
7475
return CommandResult.Succeeded;
7576
}
7677
}
7778

78-
class InstallationHistoryEntryNode implements Node<InstallationExplorerTreeNode>, ViewLogs, CopyId {
79-
readonly kind = 'installation-history-entry' as const;
80-
constructor(private readonly shell: Shell, private readonly data: InstallationHistoryEntry) {}
79+
class RunEntryNode implements Node<InstallationExplorerTreeNode>, ViewLogs, CopyId {
80+
readonly kind = 'run-entry' as const;
81+
constructor(private readonly shell: Shell, private readonly data: Run) { }
8182
getChildren(): InstallationExplorerTreeNode[] | Promise<InstallationExplorerTreeNode[]> {
8283
return [];
8384
}
8485
getTreeItem(): vscode.TreeItem {
85-
const treeItem = new vscode.TreeItem(`${this.data.Action} at ${displayTime(this.data.Timestamp)}`);
86-
treeItem.contextValue = 'porter.installation-history-entry porter.has-copiable-id';
87-
if (this.data.HasLogs) {
88-
treeItem.contextValue += ' porter.has-logs';
89-
}
86+
const treeItem = new vscode.TreeItem(`${this.data.action} at ${displayTime(this.data.started)}`);
87+
treeItem.contextValue = 'porter.run-entry porter.has-copiable-id';
88+
treeItem.contextValue += ' porter.has-logs';
9089
return treeItem;
9190
}
9291

9392
async viewLogs(): Promise<CommandResult> {
94-
const logs = await porter.getClaimLogs(this.shell, this.data.ClaimID);
93+
const logs = await porter.getRunLogs(this.shell, this.data.id);
9594
if (!logs.succeeded) {
9695
await vscode.window.showErrorMessage(`Can't view logs: ${logs.error[0]}`);
9796
return CommandResult.Failed;
9897
}
99-
const title = `Porter Logs - ${this.data.ClaimID}`;
98+
const title = `Porter Logs - ${this.data.id}`;
10099
await viewLogs(title, logs.result);
101100
return CommandResult.Succeeded;
102101
}
103102

104103
async copyId(): Promise<CommandResult> {
105-
vscode.env.clipboard.writeText(this.data.ClaimID);
104+
vscode.env.clipboard.writeText(this.data.id);
106105
return CommandResult.Succeeded;
107106
}
108107
}
109108

110109
export type InstallationExplorerTreeNode =
111110
InstallationNode |
112-
InstallationHistoryEntryNode |
111+
RunEntryNode |
113112
ExplorerErrorNode;
114113

115114
function displayTime(timeString: string): string {

src/porter/porter.objectmodel.ts

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
export interface CredentialInfo {
2-
readonly Name: string;
3-
readonly Modified: string;
2+
readonly namespace: string;
3+
readonly name: string;
4+
readonly modified: string;
45
}
56

67
export interface CredentialSetContent {
8+
readonly namespace: string;
79
readonly name: string;
810
readonly credentials: Credential[];
911
}
@@ -48,34 +50,44 @@ export function isCommand(s: CredentialSource): s is ShellCommandCredentialSourc
4850
}
4951

5052
export interface Installation {
51-
readonly Name: string;
52-
readonly Created: string;
53-
readonly Modified: string;
54-
readonly Action: string;
55-
readonly Status: string;
56-
readonly History: ReadonlyArray<InstallationHistoryEntry>; // only has latest in practice
53+
readonly namespace: string;
54+
readonly name: string;
55+
readonly status: InstallationStatus;
56+
readonly _calculated: InstallationDisplayValues;
57+
}
58+
59+
export interface InstallationStatus {
60+
readonly resultStatus: string;
61+
readonly created: string;
62+
readonly modified: string;
63+
readonly action: string;
64+
}
65+
66+
export interface InstallationDisplayValues {
67+
readonly displayInstallationState: string;
68+
readonly displayInstallationStatus: string;
5769
}
5870

5971
export interface InstallationDetail {
60-
readonly Name: string;
61-
readonly Created: string;
62-
readonly Modified: string;
63-
readonly Action: string;
64-
readonly Status: string;
65-
readonly Outputs: ReadonlyArray<InstallationOutput>;
66-
readonly History: ReadonlyArray<InstallationHistoryEntry>;
72+
readonly namespace: string;
73+
readonly name: string;
74+
readonly created: string;
75+
readonly modified: string;
76+
readonly action: string;
77+
readonly status: string;
78+
history: Run[];
6779
}
6880

69-
export interface InstallationHistoryEntry {
70-
readonly ClaimID: string;
71-
readonly Action: string;
72-
readonly Timestamp: string;
73-
readonly Status: string;
74-
readonly HasLogs: boolean;
81+
export interface Run {
82+
readonly id: string;
83+
readonly action: string;
84+
readonly started: string;
85+
readonly stopped: string;
86+
readonly status: string;
7587
}
7688

7789
export interface InstallationOutput {
78-
readonly Name: string;
79-
readonly Value: string;
80-
readonly Type: string;
90+
readonly name: string;
91+
readonly value: string;
92+
readonly type: string;
8193
}

src/porter/porter.ts

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Errorable } from '../utils/errorable';
66
import * as shell from '../utils/shell';
77
import { fs } from '../utils/fs';
88
import * as pairs from '../utils/pairs';
9-
import { CredentialInfo, CredentialSetContent, Installation, InstallationDetail } from './porter.objectmodel';
9+
import { CredentialInfo, CredentialSetContent, Installation, InstallationDetail, InstallationOutput, Run } from './porter.objectmodel';
1010

1111
import { PORTER_OUTPUT_CHANNEL as logChannel } from '../utils/logging';
1212

@@ -36,37 +36,58 @@ export function home(sh: shell.Shell): string {
3636
export async function listCredentialSets(sh: shell.Shell): Promise<Errorable<string[]>> {
3737
function parse(stdout: string): string[] {
3838
return (JSON.parse(stdout) as CredentialInfo[])
39-
.map((c) => c.Name);
39+
.map((c) => c.name);
4040
}
41-
return await invokeObj(sh, 'credentials list', '-o json', { }, parse);
41+
return await invokeObj(sh, 'credentials list', '--all-namespaces -o json', {}, parse);
4242
}
4343

4444
export async function listInstallations(sh: shell.Shell): Promise<Errorable<Installation[]>> {
4545
function parse(stdout: string): Installation[] {
4646
return (JSON.parse(stdout) as Installation[]);
4747
}
48-
return await invokeObj(sh, 'list', '-o json', { }, parse);
48+
return await invokeObj(sh, 'list', '--all-namespaces -o json', {}, parse);
4949
}
5050

51-
export async function getInstallationDetail(sh: shell.Shell, installationId: string): Promise<Errorable<InstallationDetail>> {
52-
function parse(stdout: string): InstallationDetail {
51+
export async function listInstallationOutputs(sh: shell.Shell, namespace: string, name: string): Promise<Errorable<InstallationOutput[]>> {
52+
function parse(stdout: string): InstallationOutput[] {
53+
return (JSON.parse(stdout) as InstallationOutput[]);
54+
}
55+
return await invokeObj(sh, 'installation outputs list', `--installation ${name} --namespace=${namespace} -o json`, {}, parse);
56+
}
57+
58+
export async function getInstallationDetail(sh: shell.Shell, namespace: string, name: string): Promise<Errorable<InstallationDetail>> {
59+
function parseInstallation(stdout: string): InstallationDetail {
5360
return (JSON.parse(stdout) as InstallationDetail);
5461
}
55-
return await invokeObj(sh, 'show', `${installationId} -o json`, { }, parse);
62+
const installation = await invokeObj(sh, 'show', `${name} --namespace=${namespace} -o json`, {}, parseInstallation);
63+
if (!installation.succeeded) {
64+
return { succeeded: false, error: installation.error };
65+
}
66+
67+
function parseRuns(stdout: string): Run[] {
68+
return (JSON.parse(stdout) as Run[]);
69+
}
70+
const runs = await invokeObj(sh, 'installation runs list', `${name} --namespace=${namespace} -o json`, {}, parseRuns);
71+
if (!runs.succeeded) {
72+
return { succeeded: false, error: runs.error };
73+
}
74+
75+
installation.result.history = runs.result;
76+
return installation;
5677
}
5778

58-
export async function getClaimLogs(sh: shell.Shell, claimId: string): Promise<Errorable<string[]>> {
79+
export async function getRunLogs(sh: shell.Shell, runId: string): Promise<Errorable<string[]>> {
5980
function parse(stdout: string): string[] {
6081
return stdout.split('\n');
6182
}
62-
return await invokeObj(sh, 'logs', `--run ${claimId}`, { }, parse);
83+
return await invokeObj(sh, 'logs', `--run ${runId}`, {}, parse);
6384
}
6485

65-
export async function getCredentials(sh: shell.Shell, credentialSetName: string): Promise<Errorable<CredentialSetContent>> {
86+
export async function getCredentials(sh: shell.Shell, namespace: string, name: string): Promise<Errorable<CredentialSetContent>> {
6687
function parse(stdout: string): CredentialSetContent {
6788
return JSON.parse(stdout);
6889
}
69-
return await invokeObj(sh, 'credentials show', `${credentialSetName} -o json`, { }, parse);
90+
return await invokeObj(sh, 'credentials show', `${name} --namespace=${namespace} -o json`, {}, parse);
7091
}
7192

7293
export async function create(sh: shell.Shell, folder: string): Promise<Errorable<string>> {

src/views/outputs.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ function renderMarkdown(title: string, outputs: ReadonlyArray<InstallationOutput
2626
}
2727

2828
function renderOutput(output: InstallationOutput): string {
29-
const valueLines = output.Value.split('\n');
29+
const valueLines = output.value.split('\n');
3030
return [
31-
`| ${output.Name} | ${output.Type} | ${valueLines[0]} |`,
31+
`| ${output.name} | ${output.type} | ${valueLines[0]} |`,
3232
...valueLines.slice(1).map((l) => `| | | ${l} |`)
3333
].join('\n');
3434
}

0 commit comments

Comments
 (0)