Skip to content

Commit c57afd0

Browse files
Cr 565 - app-proxy install and uninstall cmd (#570)
Co-authored-by: Oren Gurfinkel <oren.gurfinkel@codefresh.io>
1 parent 1078150 commit c57afd0

File tree

10 files changed

+446
-8
lines changed

10 files changed

+446
-8
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ coverage
1010
public
1111
temp
1212
package-lock.json
13+
venonalog.json

lib/binary/downloader.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,10 @@ class Downloader {
125125
const version = component.version.prefix ? `${component.version.prefix}${remoteVersion}` : remoteVersion;
126126
const url = _buildDownloadURL({ name, version, binary });
127127
const resp = await request(url);
128-
128+
129+
const zipLocation = `${compressedBinaryLocation}.${osSuffix}`;
130+
resp.pipe(createWriteStream(zipLocation));
131+
129132
if (this.progress) {
130133
let size = 0;
131134
resp.on('response', (res) => {
@@ -142,8 +145,6 @@ class Downloader {
142145
});
143146
}
144147

145-
const zipLocation = `${compressedBinaryLocation}.${osSuffix}`;
146-
resp.pipe(createWriteStream(zipLocation));
147148

148149
return new Promise((resolveFn, rejectFn) => {
149150
resp.on('end', async () => {
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
const _ = require('lodash');
2+
const inquirer = require('inquirer');
3+
const YAML = require('yaml');
4+
const { readFileSync, lstatSync } = require('fs');
5+
const { resolve } = require('path');
6+
const { INSTALLATION_DEFAULTS } = require('../hybrid/helper');
7+
const { getKubeContext } = require('../../helpers/kubernetes');
8+
9+
const selectRuntime = async (runtimes) => {
10+
const ans = await inquirer.prompt({
11+
type: 'list',
12+
name: 're',
13+
message: 'Name of Codefresh runtime-environment to use',
14+
choices: runtimes,
15+
});
16+
return ans.re;
17+
};
18+
19+
const setIfNotDefined = (obj, prop, value) => {
20+
_.set(obj, prop, _.get(obj, prop, value));
21+
};
22+
23+
const mergeWithValues = (argv) => {
24+
if (!argv.values || !lstatSync(resolve(process.cwd(), argv.values)).isFile()) {
25+
return argv;
26+
}
27+
const valuesFileStr = readFileSync(resolve(process.cwd(), argv.values), 'utf8');
28+
const valuesObj = YAML.parse(valuesFileStr);
29+
30+
setIfNotDefined(argv, 'kube-namespace', valuesObj.Namespace || INSTALLATION_DEFAULTS.NAMESPACE);
31+
setIfNotDefined(argv, 'kube-context-name', valuesObj.Context || getKubeContext());
32+
setIfNotDefined(argv, 'host', _.get(valuesObj, 'AppProxy.Host'));
33+
setIfNotDefined(argv, 'runtime-environment', valuesObj.RuntimeEnvironmentName);
34+
35+
return argv;
36+
};
37+
38+
module.exports = {
39+
selectRuntime,
40+
mergeWithValues,
41+
};
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
const colors = require('colors');
2+
3+
const _ = require('lodash');
4+
const Command = require('../../Command');
5+
const installRoot = require('../root/install.cmd');
6+
const {
7+
INSTALLATION_DEFAULTS,
8+
installAppProxy,
9+
createErrorHandler,
10+
drawCodefreshFiglet,
11+
} = require('../hybrid/helper');
12+
const { getKubeContext } = require('../../helpers/kubernetes');
13+
const sdk = require('../../../../logic/sdk');
14+
const { to } = require('../../../../logic/cli-config/errors/awaitTo');
15+
const { mergeWithValues, selectRuntime } = require('./helper');
16+
17+
const openIssueMessage = 'If you had any issues with this installation process please report them at:'
18+
+ ` ${colors.blue('https://github.com/codefresh-io/cli/issues/new')}`;
19+
const handleError = createErrorHandler(openIssueMessage);
20+
21+
function printInstallationOptionsSummary({
22+
kubeContextName,
23+
kubeNamespace,
24+
host,
25+
runtimeEnvironment,
26+
}) {
27+
const summary = `\n${colors.green('Installation options summary:')}
28+
1. Kubernetes Context: ${colors.cyan(kubeContextName)}
29+
2. Kubernetes Namespace: ${colors.cyan(kubeNamespace)}
30+
3. App-Proxy hostname: ${colors.cyan(host)}
31+
4. Runtime-Environment: ${colors.cyan(runtimeEnvironment)}
32+
`;
33+
console.log(summary);
34+
}
35+
36+
37+
const installAppProxyHandler = new Command({
38+
root: false,
39+
parent: installRoot,
40+
command: 'app-proxy',
41+
description: 'Install the App-Proxy component on your Kubernetes cluster',
42+
webDocs: {
43+
category: 'App-Proxy',
44+
title: 'Install',
45+
weight: 100,
46+
},
47+
48+
builder: yargs => yargs
49+
.env('CF_ARG_') // this means that every process.env.CF_ARG_* will be passed to argv
50+
.option('kube-config-path', {
51+
describe: 'Path to kubeconfig file [$KUBECONFIG]',
52+
default: INSTALLATION_DEFAULTS.KUBECONFIG_PATH,
53+
type: 'string',
54+
})
55+
.option('kube-context-name', {
56+
describe: 'Name of the kubernetes context on which the app-proxy should be installed'
57+
+ ` (default: ${getKubeContext()}) [$CF_ARG_KUBE_CONTEXT_NAME]`,
58+
})
59+
.option('kube-namespace', {
60+
describe: 'Name of the namespace on which app-proxy should be installed (default:'
61+
+ ` ${INSTALLATION_DEFAULTS.NAMESPACE}) [$CF_ARG_KUBE_NAMESPACE]`,
62+
type: 'string',
63+
})
64+
.option('runtime-environment', {
65+
describe: 'The Codefresh runtime-environment that this app-proxy will be associated with',
66+
type: 'string',
67+
})
68+
.option('docker-registry', {
69+
describe: 'The prefix for the container registry that will be used for pulling the app-proxy component image',
70+
default: 'docker.io',
71+
type: 'string',
72+
})
73+
.option('values', {
74+
describe: 'specify values in a YAML file',
75+
})
76+
.option('set-value', {
77+
describe: 'Set values for templates, example: --set-value LocalVolumesDir=/mnt/disks/ssd0/codefresh-volumes',
78+
type: 'array',
79+
})
80+
.option('verbose', {
81+
describe: 'Print logs',
82+
})
83+
.option('host', {
84+
describe: 'the hostname that will be used by the app-proxy ingress',
85+
type: 'string',
86+
})
87+
.option('ingress-class', {
88+
describe: 'the ingress class that will be used by the app-proxy ingress',
89+
type: 'string',
90+
}),
91+
92+
handler: async (_argv) => {
93+
const argv = mergeWithValues(_argv);
94+
const {
95+
'kube-config-path': kubeConfigPath,
96+
'kube-context-name': kubeContextName,
97+
'kube-namespace': kubeNamespace,
98+
'docker-registry': dockerRegistry,
99+
verbose,
100+
values,
101+
'set-value': setValues,
102+
host,
103+
'ingress-class': ingressClass,
104+
noExit,
105+
} = argv;
106+
let {
107+
'runtime-environment': runtimeEnvironment,
108+
} = argv;
109+
110+
const [listReErr, runtimes] = await to(sdk.runtimeEnvs.list({ }));
111+
await handleError(listReErr, 'Failed to get account\'s runtime environments');
112+
const runtimeNames = runtimes.reduce((acc, re) => {
113+
if (_.get(re, 'metadata.agent')) {
114+
acc.push(_.get(re, 'metadata.name'));
115+
}
116+
return acc;
117+
}, []);
118+
119+
if (_.isEmpty(runtimeNames)) {
120+
await handleError(
121+
new Error('no runtime environments found'),
122+
'Cannot install app-proxy without a Codefresh runtime-environment',
123+
);
124+
}
125+
if (!runtimeEnvironment || !runtimeNames.find(re => re === runtimeEnvironment)) {
126+
if (runtimeEnvironment) {
127+
console.log(colors.bold(`Runtime-environment "${colors.cyan(runtimeEnvironment)}" `
128+
+ 'was not found, please choose on of the following:'));
129+
}
130+
runtimeEnvironment = await selectRuntime(runtimeNames);
131+
}
132+
133+
printInstallationOptionsSummary({
134+
kubeContextName,
135+
kubeNamespace,
136+
host,
137+
runtimeEnvironment,
138+
});
139+
140+
console.log('installing app-proxy...');
141+
const appProxyUrl = await installAppProxy({
142+
apiHost: sdk.config.context.url,
143+
appProxyHost: host,
144+
appProxyIngressClass: ingressClass,
145+
kubeConfigPath,
146+
kubeContextName,
147+
kubeNamespace,
148+
dockerRegistry,
149+
valuesFile: values,
150+
setValue: setValues,
151+
verbose,
152+
});
153+
154+
const re = runtimes.find(runtime => runtimeEnvironment === _.get(runtime, 'metadata.name'));
155+
const body = {
156+
appProxy: {
157+
externalIP: appProxyUrl,
158+
},
159+
};
160+
console.log(`updating runtime-environment ${colors.cyan(runtimeEnvironment)} with app-proxy url`);
161+
await sdk.runtimeEnvs.update({ name: runtimeEnvironment }, _.merge(re, body));
162+
console.log(`runtime-environment ${colors.cyan(runtimeEnvironment)} updated`);
163+
164+
console.log(openIssueMessage);
165+
await drawCodefreshFiglet();
166+
if (!noExit) {
167+
process.exit();
168+
}
169+
},
170+
171+
});
172+
173+
module.exports = installAppProxyHandler;
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/* eslint-disable max-len */
2+
const Command = require('../../Command');
3+
const uninstallRoot = require('../root/uninstall.cmd');
4+
const inquirer = require('inquirer');
5+
const { selectRuntime, mergeWithValues } = require('./helper');
6+
const colors = require('colors');
7+
const sdk = require('../../../../logic/sdk');
8+
const _ = require('lodash');
9+
const { to } = require('../../../../logic/cli-config/errors/awaitTo');
10+
const {
11+
createErrorHandler,
12+
drawCodefreshFiglet,
13+
unInstallAppProxy,
14+
INSTALLATION_DEFAULTS,
15+
} = require('../hybrid/helper');
16+
17+
const openIssueMessage = `If you had any issues with the uninstallation process please report them at: ${colors.blue('https://github.com/codefresh-io/cli/issues/new')}`;
18+
const handleError = createErrorHandler(openIssueMessage);
19+
20+
async function promptConfirmationMessage() {
21+
const answer = await inquirer.prompt({
22+
type: 'confirm',
23+
name: 'deletionConfirmed',
24+
default: false,
25+
message: 'Are you sure you want to delete the app-proxy-component? (default is NO)',
26+
});
27+
if (!answer.deletionConfirmed) {
28+
console.log('Deletion process aborted, exiting...');
29+
process.exit(1);
30+
}
31+
}
32+
33+
const uninstallAppProxyHandler = new Command({
34+
root: false,
35+
parent: uninstallRoot,
36+
command: 'app-proxy',
37+
description: 'Deletes the App-Proxy component from your Kubernetes cluster',
38+
webDocs: {
39+
category: 'App-Proxy',
40+
title: 'Uninstall',
41+
weight: 100,
42+
},
43+
builder: yargs => yargs
44+
.env('CF_ARG_') // this means that every process.env.CF_ARG_* will be passed to argv
45+
.option('kube-context-name', {
46+
describe: 'Name of the kubernetes context from which the App-Proxy and Runtime should be removed',
47+
})
48+
.option('kube-namespace', {
49+
describe: 'Name of the kubernetes namespace from which the App-Proxy and Runtime should be removed',
50+
})
51+
.option('kube-config-path', {
52+
describe: 'Path to kubeconfig file (default is $HOME/.kube/config)',
53+
default: INSTALLATION_DEFAULTS.KUBECONFIG_PATH,
54+
55+
})
56+
.option('runtime-environment', {
57+
describe: 'The Codefresh runtime-environment that this App-Proxy will be associated with',
58+
type: 'string',
59+
})
60+
.option('force', {
61+
describe: 'Run the delete operation without asking to confirm (use with caution!)',
62+
alias: 'f',
63+
type: Boolean,
64+
})
65+
.option('values', {
66+
describe: 'specify values in a YAML file',
67+
})
68+
.option('set-value', {
69+
describe: 'Set values for templates, example: --set-value LocalVolumesDir=/mnt/disks/ssd0/codefresh-volumes',
70+
type: 'array',
71+
})
72+
.option('verbose', {
73+
describe: 'Print logs',
74+
}),
75+
handler: async (_argv) => {
76+
const argv = mergeWithValues(_argv);
77+
const {
78+
'kube-context-name': kubeContextName,
79+
'kube-namespace': kubeNamespace,
80+
'kube-config-path': kubeConfigPath,
81+
force,
82+
verbose,
83+
values,
84+
'set-value': setValues,
85+
noExit,
86+
} = argv;
87+
let {
88+
'runtime-environment': runtimeEnvironment,
89+
} = argv;
90+
91+
const [listReErr, runtimes] = await to(sdk.runtimeEnvs.list({ }));
92+
await handleError(listReErr, 'Failed to get runtime environments');
93+
const runtimeNames = runtimes.reduce((acc, re) => {
94+
if (_.get(re, 'metadata.agent')) {
95+
acc.push(_.get(re, 'metadata.name'));
96+
}
97+
return acc;
98+
}, []);
99+
100+
if (_.isEmpty(runtimeNames)) {
101+
await handleError(
102+
new Error('no runtime environments found'),
103+
'Cannot uninstall app-proxy without a Codefresh runtime-environment',
104+
);
105+
}
106+
107+
console.log(colors.green('This uninstaller will guide you through the App-Proxy uninstallation process'));
108+
109+
if (!runtimeEnvironment || !runtimeNames.find(re => re === runtimeEnvironment)) {
110+
if (runtimeEnvironment) {
111+
console.log(colors.bold(`Runtime-environment "${colors.cyan(runtimeEnvironment)}" `
112+
+ 'was not found, please choose on of the following:'));
113+
}
114+
runtimeEnvironment = await selectRuntime(runtimeNames);
115+
}
116+
117+
console.log(`\n${colors.green('Uninstallation options summary:')}
118+
1. Kubernetes Context: ${colors.cyan(kubeContextName)}
119+
2. Kubernetes Namespace: ${colors.cyan(kubeNamespace)}
120+
3. Runtime name: ${colors.cyan(runtimeEnvironment)}
121+
`);
122+
123+
if (!force) {
124+
await promptConfirmationMessage();
125+
}
126+
127+
await unInstallAppProxy({
128+
kubeConfigPath,
129+
kubeContextName,
130+
kubeNamespace,
131+
verbose,
132+
valuesFile: values,
133+
setValue: setValues,
134+
135+
});
136+
const re = runtimes.find(runtime => runtimeEnvironment === _.get(runtime, 'metadata.name'));
137+
delete re.appProxy;
138+
139+
console.log(`updating runtime-environment ${colors.cyan(runtimeEnvironment)} `);
140+
await sdk.runtimeEnvs.update({ name: runtimeEnvironment }, re);
141+
142+
console.log('Successfully uninstalled App-Proxy');
143+
console.log(openIssueMessage);
144+
await drawCodefreshFiglet();
145+
if (!noExit) {
146+
process.exit(); // TODO : This is not needed - needed to be fixed
147+
}
148+
},
149+
});
150+
151+
module.exports = uninstallAppProxyHandler;

0 commit comments

Comments
 (0)