Skip to content

Commit e06799f

Browse files
author
Luke Goodfellow
authored
Error Handling (#3)
* Added Error Handling * Refactor code to be more DRY * Updated Version
1 parent 29efdcc commit e06799f

File tree

2 files changed

+191
-152
lines changed

2 files changed

+191
-152
lines changed

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.0.0
1+
2.1.0

src/main.js

Lines changed: 190 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { Codefresh } from './codefresh.js';
44
import { autoDetectClient } from 'https://deno.land/x/[email protected]/mod.ts';
5-
import { AppsV1Api } from "https://deno.land/x/[email protected]/builtin/apps@v1/mod.ts";
5+
import { AppsV1Api } from 'https://deno.land/x/[email protected]/builtin/apps@v1/mod.ts';
66
import { BatchV1Api } from 'https://deno.land/x/[email protected]/builtin/batch@v1/mod.ts';
77
import { CoreV1Api } from 'https://deno.land/x/[email protected]/builtin/core@v1/mod.ts';
88
import { StorageV1Api } from 'https://deno.land/x/[email protected]/builtin/storage.k8s.io@v1/mod.ts';
@@ -25,190 +25,229 @@ function selectRuntimeType() {
2525
reTypes.forEach((reType, index) => {
2626
console.log(`${index + 1}. ${reType}`);
2727
});
28-
const typeSelected = prompt('\nWhich Type Of Runtime Are We Using? (Number):');
28+
29+
let typeSelected = Number(prompt('\nWhich Type Of Runtime Are We Using? (Number):'));
30+
while (isNaN(typeSelected) || typeSelected < 1 || typeSelected > reTypes.length) {
31+
console.log('Invalid selection. Please enter a number corresponding to one of the listed runtime types.');
32+
typeSelected = Number(prompt('\nWhich Type Of Runtime Are We Using? (Number):'));
33+
}
34+
2935
return reTypes[typeSelected - 1];
3036
}
3137

3238
async function saveItems(resources, dir) {
33-
await Deno.mkdir(`${dirPath}/${dir}/`, { recursive: true });
34-
return Promise.all(resources.map((item) => {
35-
return Deno.writeTextFile(`${dirPath}/${dir}/${item.metadata.name}.yaml`, toYaml(item, { skipInvalid: true }));
36-
}));
39+
try {
40+
await Deno.mkdir(`${dirPath}/${dir}/`, { recursive: true });
41+
42+
const writePromises = resources.map(async (item) => {
43+
const filePath = `${dirPath}/${dir}/${item.metadata.name}.yaml`;
44+
const fileContent = toYaml(item, { skipInvalid: true });
45+
await Deno.writeTextFile(filePath, fileContent);
46+
});
47+
48+
await Promise.all(writePromises);
49+
} catch (error) {
50+
console.error(`Error saving items to ${dir}:`, error);
51+
}
3752
}
3853

39-
async function gatherClassic() {
40-
const cf = new Codefresh();
41-
await cf.init();
42-
const reNames = await cf.getAllRuntimes();
43-
console.log('');
44-
reNames.forEach((re, index) => {
45-
console.log(`${index + 1}. ${re}`);
46-
});
47-
const selection = prompt('\nWhich Classic Runtime Are We Working With? (Number): ');
48-
const reSpec = cf.runtimes[selection - 1];
49-
const namespace = reSpec.runtimeScheduler.cluster.namespace;
50-
51-
console.log(`\nGathering Data For ${reSpec.metadata.name}.`);
52-
53-
const helmList = new Deno.Command('helm', { args: ['list', '-n', namespace, '-o', 'json'] });
54-
const output = await helmList.output();
55-
const helmReleases = JSON.parse(new TextDecoder().decode(output.stdout));
56-
57-
const dataFetchers = {
58-
'Cron': () => batchApi.namespace(namespace).getCronJobList(),
59-
'Jobs': () => batchApi.namespace(namespace).getJobList(),
60-
'Nodes': () => coreApi.getNodeList(),
61-
'Volumes': () => coreApi.getPersistentVolumeList({ labelSelector: 'io.codefresh.accountName' }),
62-
'Volumeclaims': () => coreApi.namespace(namespace).getPersistentVolumeClaimList({ labelSelector: 'io.codefresh.accountName' }),
63-
'Configmaps': () => coreApi.namespace(namespace).getConfigMapList({ labelSelector: 'app.kubernetes.io/name=cf-runtime' }),
64-
'Services': () => coreApi.namespace(namespace).getServiceList(),
65-
'Pods': () => coreApi.namespace(namespace).getPodList(),
66-
'Events': () => coreApi.namespace(namespace).getEventList(),
67-
'Storageclass': () => storageApi.getStorageClassList(),
68-
};
69-
70-
for (const [dir, fetcher] of Object.entries(dataFetchers)) {
54+
async function saveHelmReleases(type, namespace) {
55+
try {
56+
const helmList = new Deno.Command('helm', { args: ['list', '-n', namespace, '-o', 'json'] });
57+
const output = await helmList.output();
58+
const helmReleases = JSON.parse(new TextDecoder().decode(output));
59+
await Deno.writeTextFile(`${dirPath}/${type}-helmReleases.yaml`, toYaml(helmReleases, { skipInvalid: true }));
60+
} catch (error) {
61+
console.error(`Error saving Helm releases for ${type}:`, error);
62+
}
63+
}
64+
65+
66+
function dataFetchers(type, namespace) {
67+
switch (type) {
68+
case 'classic':
69+
return {
70+
'Cron': () => batchApi.namespace(namespace).getCronJobList(),
71+
'Jobs': () => batchApi.namespace(namespace).getJobList(),
72+
'Nodes': () => coreApi.getNodeList(),
73+
'Volumes': () => coreApi.getPersistentVolumeList({ labelSelector: 'io.codefresh.accountName' }),
74+
'Volumeclaims': () => coreApi.namespace(namespace).getPersistentVolumeClaimList({ labelSelector: 'io.codefresh.accountName' }),
75+
'Configmaps': () => coreApi.namespace(namespace).getConfigMapList({ labelSelector: 'app.kubernetes.io/name=cf-runtime' }),
76+
'Services': () => coreApi.namespace(namespace).getServiceList(),
77+
'Pods': () => coreApi.namespace(namespace).getPodList(),
78+
'Events': () => coreApi.namespace(namespace).getEventList(),
79+
'Storageclass': () => storageApi.getStorageClassList(),
80+
};
81+
case 'gitops':
82+
return {
83+
'Apps': () => argoProj.namespace(namespace).getApplicationList(),
84+
'Nodes': () => coreApi.getNodeList(),
85+
'Configmaps': () => coreApi.namespace(namespace).getConfigMapList(),
86+
'Services': () => coreApi.namespace(namespace).getServiceList(),
87+
'Pods': () => coreApi.namespace(namespace).getPodList(),
88+
'Events': () => coreApi.namespace(namespace).getEventList(),
89+
};
90+
case 'onprem':
91+
return {
92+
'Deployments': () => appsApi.namespace(namespace).getDeploymentList(),
93+
'Daemonsets': () => appsApi.namespace(namespace).getDaemonSetList(),
94+
'Nodes': () => coreApi.getNodeList(),
95+
'Volumes': () => coreApi.getPersistentVolumeList({ labelSelector: 'io.codefresh.accountName' }),
96+
'Volumeclaims': () => coreApi.namespace(namespace).getPersistentVolumeClaimList({ labelSelector: 'io.codefresh.accountName' }),
97+
'Services': () => coreApi.namespace(namespace).getServiceList(),
98+
'Pods': () => coreApi.namespace(namespace).getPodList(),
99+
'Events': () => coreApi.namespace(namespace).getEventList(),
100+
'Storageclass': () => storageApi.getStorageClassList(),
101+
};
102+
default:
103+
console.error('Invalid runtime type selected');
104+
return;
105+
}
106+
}
107+
108+
async function fetchAndSaveData(type, namespace) {
109+
for (const [dir, fetcher] of Object.entries(dataFetchers(type, namespace))) {
71110
const resources = await fetcher();
111+
await saveItems(resources.items, dir);
112+
72113
if (dir === 'Pods') {
73-
await saveItems(resources.items, dir);
74114
await Promise.all(resources.items.map(async (item) => {
75-
const log = await coreApi.namespace(namespace).getPodLog(item.metadata.name, { container: item.spec.containers[0].name });
76-
return Deno.writeTextFile(`${dirPath}/${dir}/${item.metadata.name}.log`, log);
115+
let log;
116+
try {
117+
log = await coreApi.namespace(namespace).getPodLog(item.metadata.name, { container: item.spec.containers[0].name });
118+
} catch (error) {
119+
console.error(`Failed to get logs for ${item.metadata.name}:`, error);
120+
log = error;
121+
}
122+
await Deno.writeTextFile(`${dirPath}/${dir}/${item.metadata.name}.log`, log);
77123
}));
78-
} else {
79-
await saveItems(resources.items, dir);
80124
}
81125
}
82-
83-
Deno.writeTextFile(`${dirPath}/runtimeSpec.yaml`, toYaml(reSpec, { skipInvalid: true }));
84-
Deno.writeTextFile(`${dirPath}/classicReleases.yaml`, toYaml(helmReleases, { skipInvalid: true }));
126+
await saveHelmReleases(type, namespace);
85127
}
86128

87-
async function gatherGitOps() {
88-
const namespaceList = await coreApi.getNamespaceList();
89-
console.log('');
90-
namespaceList.items.forEach((ns, index) => {
91-
console.log(`${index + 1}. ${ns.metadata.name}`);
92-
});
93-
const selection = prompt('\nWhich Namespace Is The GitOps Runtime Installed In? (Number): ');
94-
const namespace = namespaceList.items[selection - 1].metadata.name;
129+
async function gatherClassic() {
130+
try {
131+
const cf = new Codefresh();
132+
await cf.init();
133+
const reNames = await cf.getAllRuntimes();
134+
console.log('');
135+
reNames.forEach((re, index) => {
136+
console.log(`${index + 1}. ${re}`);
137+
});
138+
139+
let selection = Number(prompt('\nWhich Classic Runtime Are We Working With? (Number): '));
140+
while (isNaN(selection) || selection < 1 || selection > reNames.length) {
141+
console.log('Invalid selection. Please enter a number corresponding to one of the listed runtimes.');
142+
selection = Number(prompt('\nWhich Classic Runtime Are We Working With? (Number): '));
143+
}
95144

96-
const apps = await argoProj.namespace(namespace).getApplicationList();
97-
const isCodfresh = apps.items.some((app) => ['codefresh.io/entity'] in app.metadata.labels);
145+
const reSpec = cf.runtimes[selection - 1];
146+
const namespace = reSpec.runtimeScheduler.cluster.namespace;
98147

99-
if (!isCodfresh) {
100-
const continueData = confirm(`\nCould not find a GitOps Runtime in ${namespace}. Do you still want to continue?`);
101-
if (!continueData) {
102-
return;
103-
}
148+
console.log(`\nGathering Data For ${reSpec.metadata.name}.`);
149+
150+
await fetchAndSaveData('classic', namespace);
151+
152+
await Deno.writeTextFile(`${dirPath}/runtimeSpec.yaml`, toYaml(reSpec, { skipInvalid: true }));
153+
} catch (error) {
154+
console.error(`Error gathering classic runtime data:`, error);
104155
}
156+
}
105157

106-
console.log(`\nGathering Data In ${namespace} For The GitOps Runtime.`);
158+
async function gatherGitOps() {
159+
try {
160+
const namespaceList = await coreApi.getNamespaceList();
161+
console.log('');
162+
namespaceList.items.forEach((ns, index) => {
163+
console.log(`${index + 1}. ${ns.metadata.name}`);
164+
});
165+
166+
let selection = Number(prompt('\nWhich Namespace Is The GitOps Runtime Installed In? (Number): '));
167+
while (isNaN(selection) || selection < 1 || selection > namespaceList.items.length) {
168+
console.log('Invalid selection. Please enter a number corresponding to one of the listed namespaces.');
169+
selection = Number(prompt('\nWhich Namespace Is The GitOps Runtime Installed In? (Number): '));
170+
}
107171

108-
const helmList = new Deno.Command('helm', { args: ['list', '-n', namespace, '-o', 'json'] });
109-
const output = await helmList.output();
110-
const helmReleases = JSON.parse(new TextDecoder().decode(output.stdout));
172+
const namespace = namespaceList.items[selection - 1].metadata.name;
111173

112-
const dataFetchers = {
113-
'Apps': () => argoProj.namespace(namespace).getApplicationList(),
114-
'Nodes': () => coreApi.getNodeList(),
115-
'Configmaps': () => coreApi.namespace(namespace).getConfigMapList(),
116-
'Services': () => coreApi.namespace(namespace).getServiceList(),
117-
'Pods': () => coreApi.namespace(namespace).getPodList(),
118-
'Events': () => coreApi.namespace(namespace).getEventList(),
119-
};
174+
const apps = await argoProj.namespace(namespace).getApplicationList();
175+
const isCodfresh = apps.items.some((app) => ['codefresh.io/entity'] in app.metadata.labels);
120176

121-
for (const [dir, fetcher] of Object.entries(dataFetchers)) {
122-
const resources = await fetcher();
123-
if (dir === 'Pods') {
124-
await saveItems(resources.items, dir);
125-
await Promise.all(resources.items.map(async (item) => {
126-
const log = await coreApi.namespace(namespace).getPodLog(item.metadata.name, { container: item.spec.containers[0].name });
127-
return Deno.writeTextFile(`${dirPath}/${dir}/${item.metadata.name}.log`, log);
128-
}));
129-
} else {
130-
await saveItems(resources.items, dir);
177+
if (!isCodfresh) {
178+
const continueData = confirm(`\nCould not find a GitOps Runtime in ${namespace}. Do you still want to continue?`);
179+
if (!continueData) {
180+
Deno.exit();
181+
}
131182
}
132-
}
133183

134-
Deno.writeTextFile(`${dirPath}/gitopsReleases.yaml`, toYaml(helmReleases, { skipInvalid: true }));
184+
console.log(`\nGathering Data In ${namespace} For The GitOps Runtime.`);
185+
186+
await fetchAndSaveData('gitops', namespace);
187+
} catch (error) {
188+
console.error(`Error gathering GitOps runtime data:`, error);
189+
}
135190
}
136191

137192
async function gatherOnPrem() {
138-
const cf = new Codefresh();
139-
await cf.init();
140-
const accounts = await cf.getOnPremAccounts();
141-
const runtimes = await cf.getOnPremRuntimes();
142-
143-
const namespaceList = await coreApi.getNamespaceList();
144-
console.log('');
145-
namespaceList.items.forEach((ns, index) => {
146-
console.log(`${index + 1}. ${ns.metadata.name}`);
147-
});
148-
const selection = prompt('\nWhich Namespace Is Codefresh OnPrem Installed In? (Number): ');
149-
const namespace = namespaceList.items[selection - 1].metadata.name;
150-
151-
console.log(`\nGathering Data For On Prem.`);
152-
153-
const helmList = new Deno.Command('helm', { args: ['list', '-n', namespace, '-o', 'json'] });
154-
const output = await helmList.output();
155-
const helmReleases = JSON.parse(new TextDecoder().decode(output.stdout));
156-
157-
const dataFetchers = {
158-
'Deployments': () => appsApi.namespace(namespace).getDeploymentList(),
159-
'Daemonsets': () => appsApi.namespace(namespace).getDaemonSetList(),
160-
'Nodes': () => coreApi.getNodeList(),
161-
'Volumes': () => coreApi.getPersistentVolumeList({ labelSelector: 'io.codefresh.accountName' }),
162-
'Volumeclaims': () => coreApi.namespace(namespace).getPersistentVolumeClaimList({ labelSelector: 'io.codefresh.accountName' }),
163-
'Services': () => coreApi.namespace(namespace).getServiceList(),
164-
'Pods': () => coreApi.namespace(namespace).getPodList(),
165-
'Events': () => coreApi.namespace(namespace).getEventList(),
166-
'Storageclass': () => storageApi.getStorageClassList(),
167-
};
168-
169-
for (const [dir, fetcher] of Object.entries(dataFetchers)) {
170-
const resources = await fetcher();
171-
if (dir === 'Pods') {
172-
await saveItems(resources.items, dir);
173-
await Promise.all(resources.items.map(async (item) => {
174-
const log = await coreApi.namespace(namespace).getPodLog(item.metadata.name, { container: item.spec.containers[0].name });
175-
return Deno.writeTextFile(`${dirPath}/${dir}/${item.metadata.name}.log`, log);
176-
}));
177-
} else {
178-
await saveItems(resources.items, dir);
193+
try {
194+
const cf = new Codefresh();
195+
await cf.init();
196+
const accounts = await cf.getOnPremAccounts();
197+
const runtimes = await cf.getOnPremRuntimes();
198+
199+
const namespaceList = await coreApi.getNamespaceList();
200+
console.log('');
201+
namespaceList.items.forEach((ns, index) => {
202+
console.log(`${index + 1}. ${ns.metadata.name}`);
203+
});
204+
205+
let selection = Number(prompt('\nWhich Namespace Is Codefresh OnPrem Installed In? (Number): '));
206+
while (isNaN(selection) || selection < 1 || selection > namespaceList.items.length) {
207+
console.log('Invalid selection. Please enter a number corresponding to one of the listed namespaces.');
208+
selection = Number(prompt('\nWhich Namespace Is Codefresh OnPrem Installed In? (Number): '));
179209
}
180-
}
181210

182-
Deno.writeTextFile(`${dirPath}/onPremReleases.yaml`, toYaml(helmReleases, { skipInvalid: true }));
183-
Deno.writeTextFile(`${dirPath}/onPremAccounts.yaml`, toYaml(accounts, { skipInvalid: true }));
184-
Deno.writeTextFile(`${dirPath}/onPremRuntimes.yaml`, toYaml(runtimes, { skipInvalid: true }));
211+
const namespace = namespaceList.items[selection - 1].metadata.name;
212+
213+
console.log(`\nGathering Data For On Prem.`);
214+
215+
await fetchAndSaveData('onprem', namespace);
216+
217+
await Deno.writeTextFile(`${dirPath}/onPremAccounts.yaml`, toYaml(accounts, { skipInvalid: true }));
218+
await Deno.writeTextFile(`${dirPath}/onPremRuntimes.yaml`, toYaml(runtimes, { skipInvalid: true }));
219+
} catch (error) {
220+
console.error(`Error gathering On Prem data:`, error);
221+
}
185222
}
186223

187224
async function main() {
188-
const runtimeType = selectRuntimeType();
225+
try {
226+
const runtimeType = selectRuntimeType();
227+
228+
switch (runtimeType) {
229+
case 'classic':
230+
await gatherClassic();
231+
break;
232+
case 'gitops':
233+
await gatherGitOps();
234+
break;
235+
case 'onprem':
236+
await gatherOnPrem();
237+
break;
238+
}
189239

190-
switch (runtimeType) {
191-
case 'classic':
192-
await gatherClassic();
193-
break;
194-
case 'gitops':
195-
await gatherGitOps();
196-
break;
197-
case 'onprem':
198-
await gatherOnPrem();
199-
break;
200-
default:
201-
console.log('Invalid runtime type selected');
202-
return;
203-
}
240+
console.log(`\nSaving data to ./codefresh-support-package-${timestamp}.zip`);
241+
await compress(dirPath, `./codefresh-support-package-${timestamp}.zip`, { overwrite: true });
204242

205-
console.log(`\nSaving data to ./codefresh-support-package-${timestamp}.zip`);
206-
await compress(dirPath, `./codefresh-support-package-${timestamp}.zip`, { overwrite: true });
243+
console.log('\nCleaning up temp directory');
244+
await Deno.remove(dirPath, { recursive: true });
207245

208-
console.log('\nCleaning up temp directory');
209-
await Deno.remove(dirPath, { recursive: true });
210-
console.log(`\nPlease attach ./codefresh-support-package-${timestamp}.zip to your support ticket.`);
211-
console.log('Before attaching, verify the contents and remove any sensitive information.');
246+
console.log(`\nPlease attach ./codefresh-support-package-${timestamp}.zip to your support ticket.`);
247+
console.log('Before attaching, verify the contents and remove any sensitive information.');
248+
} catch (error) {
249+
console.error(`Error in main function:`, error);
250+
}
212251
}
213252

214253
await main();

0 commit comments

Comments
 (0)