Skip to content

Commit

Permalink
Create/Delete remote RHOSAK cluster
Browse files Browse the repository at this point in the history
Fixes redhat-developer#1

Signed-off-by: Fred Bricon <fbricon@gmail.com>
  • Loading branch information
fbricon committed Jul 7, 2021
1 parent 6ea933b commit 8af6f40
Show file tree
Hide file tree
Showing 8 changed files with 661 additions and 102 deletions.
26 changes: 17 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 24 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"description": "Contributes `Red Hat OpenShift Streams For Apache Kafka` clusters to `Tools for Apache Kafka`",
"version": "0.0.2",
"engines": {
"vscode": "^1.54.0"
"vscode": "^1.52.0"
},
"license": "MIT",
"icon": "icons/icon128.png",
Expand Down Expand Up @@ -36,6 +36,16 @@
"command": "rhoas.open.RHOSAKDashboard",
"category": "Red Hat",
"title": "Open Red Hat OpenShift Streams for Apache Kafka Dashboard"
},
{
"command": "rhoas.create.RHOSAKCluster",
"category": "Red Hat",
"title": "Create a Red Hat OpenShift Streams for Apache Kafka cluster"
},
{
"command": "rhoas.delete.RHOSAKCluster",
"category": "Red Hat",
"title": "Delete remote cluster"
}
],
"menus": {
Expand All @@ -44,6 +54,11 @@
"command": "rhoas.open.RHOSAKDashboard",
"when": "view == kafkaExplorer && viewItem =~ /^cluster-rhosak$|^selectedCluster-rhosak$/ && !listMultiSelection",
"group": "0_rhosak"
},
{
"command": "rhoas.delete.RHOSAKCluster",
"when": "view == kafkaExplorer && viewItem =~ /^cluster-rhosak$|^selectedCluster-rhosak$/ && !listMultiSelection",
"group": "0_rhosak"
}
]
}
Expand All @@ -54,7 +69,9 @@
],
"main": "./dist/extension.js",
"activationEvents": [
"onCommand:rhoas.open.RHOSAKDashboard"
"onCommand:rhoas.open.RHOSAKDashboard",
"onCommand:rhoas.create.RHOSAKCluster",
"onCommand:rhoas.delete.RHOSAKCluster"
],
"scripts": {
"vscode:prepublish": "npm run package",
Expand All @@ -68,23 +85,23 @@
"test": "node ./out/test/runTest.js"
},
"devDependencies": {
"@types/vscode": "^1.54.0",
"@types/vscode": "^1.52.0",
"@types/glob": "^7.1.3",
"@types/mocha": "^8.0.4",
"@types/mocha": "^8.2.2",
"@types/node": "^12.11.7",
"eslint": "^7.19.0",
"@typescript-eslint/eslint-plugin": "^4.14.1",
"@typescript-eslint/parser": "^4.14.1",
"glob": "^7.1.6",
"mocha": "^8.2.1",
"mocha": "^8.2.2",
"typescript": "^4.2.3",
"vscode-test": "^1.5.0",
"ts-loader": "^8.0.14",
"webpack": "^5.19.0",
"webpack-cli": "^4.4.0"
},
"dependencies": {
"@redhat-developer/vscode-redhat-telemetry": "0.1.1",
"axios": "^0.21.1"
"@redhat-developer/vscode-redhat-telemetry": "0.2.0",
"@rhoas/kafka-management-sdk": "0.9.0"
}
}
87 changes: 87 additions & 0 deletions src/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { TelemetryService } from '@redhat-developer/vscode-redhat-telemetry/lib';
import { authentication, commands, ExtensionContext, Uri, window } from 'vscode';
import { rhosakService } from './rhosakService';
import { createCluster } from './wizard';

export const OPEN_RHOSAK_DASHBOARD_CMD = 'rhoas.open.RHOSAKDashboard';
export const DELETE_RHOSAK_CLUSTER_CMD = 'rhoas.delete.RHOSAKCluster';
export const CREATE_RHOSAK_CLUSTER_CMD = 'rhoas.create.RHOSAKCluster';
const LANDING_PAGE = 'https://cloud.redhat.com/beta/application-services/streams';

export function registerCommands (context: ExtensionContext, telemetryService:TelemetryService ) {
context.subscriptions.push(
commands.registerCommand(OPEN_RHOSAK_DASHBOARD_CMD, (clusterItem?: any) => {
let clusterId:string|undefined;
if (clusterItem?.cluster?.id) {
clusterId = clusterItem.cluster.id;
} else if (clusterItem?.id) {
clusterId = clusterItem.id;
}
const reason = clusterId?"Cluster page":"Manual invocation";
openRHOSAKDashboard(telemetryService, reason, clusterId);
})
);
context.subscriptions.push(
commands.registerCommand(DELETE_RHOSAK_CLUSTER_CMD, async (clusterItem?: any) => {
let clusterId:string|undefined;
if (clusterItem?.cluster?.id) {
clusterId = clusterItem.cluster.id;
} else if (clusterItem?.id) {
clusterId = clusterItem.id;
}
if (!clusterId) {
return;
}
let name: string|undefined;
if (clusterItem?.cluster?.name) {
name = clusterItem.cluster.name;
} else if (clusterItem?.name) {
name = clusterItem.name;
}
const deleteConfirmation = await window.showWarningMessage(`Are you sure you want to delete remote cluster '${name}'?`, 'Cancel', 'Delete');
if (deleteConfirmation !== 'Delete') {
return;
}

const session = await authentication.getSession('redhat-account-auth', ['openid'], { createIfNone: true });
if (session) {
try {
await rhosakService.deleteKafka(clusterId!, session.accessToken);
} catch (error) {
window.showErrorMessage(`Failed to delete remote cluster '${name}': ${error.message}`);
}
const deleteRequest = {
clusterIds: [clusterId]
};
return commands.executeCommand("vscode-kafka.api.deleteclusters", deleteRequest);
}
})
);
context.subscriptions.push(
commands.registerCommand(CREATE_RHOSAK_CLUSTER_CMD, async () => {
try {
const clusters = await createCluster(telemetryService);
if (clusters && clusters.length > 0){
return commands.executeCommand("vscode-kafka.api.addclusters", clusters);
}
} catch(e) {
window.showErrorMessage(e.message);
}
})
);
}

export async function openRHOSAKDashboard(telemetryService:TelemetryService, reason: string, clusterId?:string) {
let event = {
name: "rhoas.open.rhosak.dashboard",
properties: {
"reason": reason
}
};
let page = LANDING_PAGE;
if (clusterId) {
page = `${page}/kafkas/${clusterId}`;
}
telemetryService.send(event);
return commands.executeCommand('vscode.open', Uri.parse(page));
}
108 changes: 22 additions & 86 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,25 @@
import axios from 'axios';
import {authentication, commands, ExtensionContext, ProgressLocation, Uri, window } from 'vscode';
import { Cluster, KafkaExtensionParticipant, ClusterSettings, ConnectionOptions, KafkaConfig, ClusterProviderParticipant } from './vscodekafka-api';
/* eslint-disable @typescript-eslint/naming-convention */
import { getRedHatService, TelemetryService } from "@redhat-developer/vscode-redhat-telemetry";
const KAFKA_API = 'https://api.openshift.com/api/managed-services-api/v1/kafkas';
const LANDING_PAGE = 'https://cloud.redhat.com/beta/application-services/streams';
const OPEN_RHOSAK_DASHBOARD_COMMAND = 'rhoas.open.RHOSAKDashboard';
import { authentication, commands, ExtensionContext, ProgressLocation, window } from 'vscode';
import { CREATE_RHOSAK_CLUSTER_CMD, openRHOSAKDashboard, registerCommands } from './commands';
import { rhosakService } from './rhosakService';
import { convertAll } from './utils';
import { Cluster, ClusterProviderParticipant, ClusterSettings, ConnectionOptions, KafkaConfig, KafkaExtensionParticipant } from './vscodekafka-api';

const RHOSAK_LABEL = "Red Hat OpenShift Streams for Apache Kafka";
const OPEN_DASHBOARD = 'Open Dashboard';
const CREATE_CLUSTER = 'Create a new remote cluster';

export async function activate(context: ExtensionContext): Promise<KafkaExtensionParticipant> {
let telemetryService: TelemetryService = await (await getRedHatService(context)).getTelemetryService();
telemetryService.sendStartupEvent();
context.subscriptions.push(
commands.registerCommand(OPEN_RHOSAK_DASHBOARD_COMMAND, (clusterItem?: any) => {
let clusterId:string|undefined;
if (clusterItem?.cluster?.id) {
clusterId = clusterItem.cluster.id;
} else if (clusterItem?.id) {
clusterId = clusterItem.id;
}
const reason = clusterId?"Cluster page":"Manual invocation";
openRHOSAKDashboard(telemetryService, reason, clusterId);
})
);
registerCommands(context, telemetryService);
return getRHOSAKClusterProvider(telemetryService);
}

const RHOSAK_CLUSTER_PROVIDER_ID = "rhosak";
const RHOSAK_LABEL = "Red Hat OpenShift Streams for Apache Kafka";
const OPEN_DASHBOARD = 'Open Dashboard';

function getRHOSAKClusterProvider(telemetryService: TelemetryService): KafkaExtensionParticipant {
return {
getClusterProviderParticipant(clusterProviderId: string): ClusterProviderParticipant {
getClusterProviderParticipant(_clusterProviderId: string): ClusterProviderParticipant {
return {
configureClusters: async (clusterSettings: ClusterSettings): Promise<Cluster[] | undefined> => configureClusters(clusterSettings, telemetryService),
createKafkaConfig: (connectionOptions: ConnectionOptions): KafkaConfig => createKafkaConfig(connectionOptions)
Expand All @@ -47,6 +34,7 @@ async function configureClusters(clusterSettings: ClusterSettings, telemetryServ
window.showWarningMessage('You need to log into Red Hat first!');
return [];
}
//console.log(`token:${session.accessToken}`);
const existingClusterUrls = clusterSettings.getAll().map(cluster => cluster.bootstrap);
const existingNames = clusterSettings.getAll().map(cluster => cluster.name);
let clusters = [] as Cluster[];
Expand All @@ -57,7 +45,8 @@ async function configureClusters(clusterSettings: ClusterSettings, telemetryServ
progress.report({
message: `Fetching Kafka cluster definitions from Red Hat...`,
});
return getRHOSAKClusters(session.accessToken, existingNames);
const rhosaks = (await rhosakService.listKafkas(session.accessToken)).filter(c => c.status === 'ready');
return convertAll(rhosaks, existingNames);
});
} catch (error) {
let event = {
Expand All @@ -68,7 +57,7 @@ async function configureClusters(clusterSettings: ClusterSettings, telemetryServ
};
telemetryService.send(event);
if (error.response && error.response.status === 403) {
//Apparently this is not supposed to happened once we go in prod
//Apparently this is not supposed to happen once we go in prod
const signUp = 'Sign Up';
const action = await window.showErrorMessage(`You have no ${RHOSAK_LABEL} account`, signUp);
if (action === signUp) {
Expand Down Expand Up @@ -97,29 +86,16 @@ async function configureClusters(clusterSettings: ClusterSettings, telemetryServ
window.showInformationMessage(`All ${RHOSAK_LABEL} clusters have already been added`);
} else {
// Should open the landing page
const action = await window.showWarningMessage(`No ${RHOSAK_LABEL} cluster available!`, OPEN_DASHBOARD);
if (action === OPEN_DASHBOARD) {
const action = await window.showWarningMessage(`No ${RHOSAK_LABEL} cluster available!`, CREATE_CLUSTER, OPEN_DASHBOARD);
if (action === CREATE_CLUSTER) {
commands.executeCommand(CREATE_RHOSAK_CLUSTER_CMD);
} else if (action === OPEN_DASHBOARD) {
openRHOSAKDashboard(telemetryService, "No clusters");
}
}
return [];
}

async function openRHOSAKDashboard(telemetryService:TelemetryService, reason: string, clusterId?:string) {
let event = {
name: "rhoas.open.rhosak.dashboard",
properties: {
"reason": reason
}
};
let page = LANDING_PAGE;
if (clusterId) {
page = `${page}/kafkas/${clusterId}`;
}
telemetryService.send(event);
return commands.executeCommand('vscode.open', Uri.parse(page));
}
function createKafkaConfig(connectionOptions: ConnectionOptions): KafkaConfig {
return {
clientId: "vscode-kafka",
Expand All @@ -138,48 +114,8 @@ function createKafkaConfig(connectionOptions: ConnectionOptions): KafkaConfig {
};
}
async function getRHOSAKClusters(token: string, existingNames: string[]): Promise<Cluster[]> {
let requestConfig = {
params: {
orderBy: 'name asc'
},
headers: {
// eslint-disable-next-line @typescript-eslint/naming-convention
'Authorization': `Bearer ${token}`,
// eslint-disable-next-line @typescript-eslint/naming-convention
'Accept': 'application/json'
}
};
const clusters: Cluster[] = [];
const response = await axios.get(KAFKA_API, requestConfig);
const kafkas = response.data;
if (kafkas && kafkas.items && kafkas.items.length > 0) {
kafkas.items.forEach((cluster: { id: string; status: string; name: string; bootstrapServerHost: any; }) => {
if (cluster?.status === 'ready') {
clusters.push({
id: cluster.id,
name: uniquify(cluster.name, existingNames),
bootstrap: cluster.bootstrapServerHost,
clusterProviderId: RHOSAK_CLUSTER_PROVIDER_ID,
});
}
});
}
return clusters;
}
// this method is called when your extension is deactivated
export function deactivate() { }

function uniquify(name: string, existingNames: string[]): string {
if (!existingNames || existingNames.length === 0) {
return name;
}
let uniqueName = name;
let i = 1;
while(existingNames.includes(uniqueName)) {
i++;
uniqueName = `${name} ${i}`;
}
return uniqueName;
}


Loading

0 comments on commit 8af6f40

Please sign in to comment.