Skip to content

Commit bd3ba01

Browse files
oren-codefreshOleg Sucharevich
andauthored
support install agent (#394)
* support install agent * wip * install runtime * wip * add attach command * add root install command * auto attach on installer * install agent and attach * install agent with token * fixes * up * make runtimeName mandatory * remove agentId on install and attach * bump sdk * remove camel case * add status to agent * wip * fix agent patch * install agent and runtime in one command * support agent-restart * install runtime is opt in * default for kube-context-name * use kubeconfig path to resolve context * typo * call attach also if already agent's attached * print token in a new line * protect error flows * wip * wip * uninstall agent * uninstall runtime * set defaults Co-authored-by: Oleg Sucharevich <olegs@codefresh.io>
1 parent a1ea4d0 commit bd3ba01

File tree

14 files changed

+1350
-25
lines changed

14 files changed

+1350
-25
lines changed

lib/interface/cli/commands/agent/apply.cmd.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ const command = new Command({
5353
throw new CFError(`No such agent: "${name || id}"`);
5454
}
5555

56-
await sdk.agents.replace({ agentId: agent.id }, { name: newName, runtimes });
56+
await sdk.agents.update({ agentId: agent.id }, { name: newName, runtimes });
5757
console.log(`Agent: "${name || id}" patched.`);
5858
},
5959
});

lib/interface/cli/commands/agent/create.cmd.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ const command = new Command({
3131
} = argv;
3232

3333
const agent = await sdk.agents.create({ name, runtimes });
34-
console.log(`Agent: "${agent.name}" created with token ${agent.token}`);
34+
console.log(`Agent: "${agent.name}" created`);
35+
console.log(agent.token);
3536
},
3637
});
3738

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/* eslint-disable max-len */
2+
const Command = require('../../Command');
3+
const installRoot = require('../root/install.cmd');
4+
const { sdk } = require('../../../../logic');
5+
const installRuntimeCmd = require('../runtimeEnvironments/install.cmd');
6+
const { getKubeContext } = require('../../helpers/kubernetes');
7+
8+
const installAgentCmd = new Command({
9+
root: false,
10+
parent: installRoot,
11+
command: 'agent',
12+
description: 'Install and create an agent on kubernetes cluster',
13+
webDocs: {
14+
category: 'Agents',
15+
title: 'Install',
16+
weight: 100,
17+
},
18+
builder: yargs => yargs
19+
.env('CF_ARG_') // this means that every process.env.CF_ARG_* will be passed to argv
20+
.option('name', {
21+
describe: 'Agent\'s name to be created if token is not provided',
22+
})
23+
.option('token', {
24+
describe: 'Agent\'s token',
25+
})
26+
.option('kube-context-name', {
27+
describe: 'Name of the kubernetes context on which venona should be installed [$CF_ARG_KUBE_CONTEXT_NAME]',
28+
})
29+
.option('kube-node-selector', {
30+
describe: 'The kubernetes node selector "key=value" to be used by venona build resources (default is no node selector) (string)',
31+
})
32+
.option('dry-run', {
33+
describe: 'Set to true to simulate installation',
34+
})
35+
.option('in-cluster', {
36+
describe: 'Set flag if venona is been installed from inside a cluster',
37+
})
38+
.option('kube-namespace', {
39+
describe: 'Name of the namespace on which venona should be installed [$CF_ARG_KUBE_NAMESPACE]',
40+
})
41+
.option('kubernetes-runner-type', {
42+
describe: 'Set the runner type to kubernetes (alpha feature)',
43+
})
44+
.option('tolerations', {
45+
describe: 'The kubernetes tolerations as path to a JSON file to be used by venona resources (default is no tolerations) (string)',
46+
})
47+
.option('venona-version', {
48+
describe: 'Version of venona to install (default is the latest)',
49+
})
50+
.option('kube-config-path', {
51+
describe: 'Path to kubeconfig file (default is $HOME/.kube/config)',
52+
})
53+
.option('skip-version-check', {
54+
describe: 'Do not compare current Venona\'s version with latest',
55+
})
56+
.option('install-runtime', {
57+
describe: 'Install and attach runtime on the same namespace as the agent (default is false)',
58+
})
59+
.option('verbose', {
60+
describe: 'Print logs',
61+
}),
62+
handler: async (argv) => {
63+
let {
64+
name, token,
65+
} = argv;
66+
const {
67+
'kube-node-selector': kubeNodeSelector,
68+
'dry-run': dryRun,
69+
'in-cluster': inCluster,
70+
'kube-namespace': kubeNamespace,
71+
'kubernetes-runner-type': kubernetesRunnerType,
72+
tolerations,
73+
'venona-version': venonaVersion,
74+
'kube-config-path': kubeConfigPath,
75+
'skip-version-check': skipVersionCheck,
76+
'install-runtime': installRuntime,
77+
verbose,
78+
} = argv;
79+
let agent;
80+
let { 'kube-context-name': kubeContextName } = argv;
81+
if (!kubeContextName) {
82+
kubeContextName = getKubeContext(kubeConfigPath);
83+
}
84+
85+
if (!token) { // Create an agent if not provided
86+
name = name || `${kubeContextName}_${kubeNamespace}`;
87+
agent = await sdk.agents.create({ name });
88+
// eslint-disable-next-line prefer-destructuring
89+
token = agent.token;
90+
console.log(`An agent with name: ${name} was created\n note this the last only time the token will be printed`);
91+
console.log(token);
92+
} else {
93+
// take the agent id from the token
94+
const apiKey = token.split('.')[0];
95+
const agentData = await sdk.tokens.getById({ id: apiKey });
96+
if (!agentData) {
97+
throw new Error('token is not valid');
98+
}
99+
const { subject } = agentData;
100+
101+
if (subject.type !== 'agent') {
102+
throw new Error('token is not assosicated with agent');
103+
}
104+
const agentId = agentData.subject.ref;
105+
const data = await sdk.agents.get({ agentId });
106+
// eslint-disable-next-line prefer-destructuring
107+
name = data.name;
108+
}
109+
const apiHost = sdk.config.context.url;
110+
const agentInstallStatusCode = await sdk.agents.install({
111+
apiHost,
112+
kubeContextName,
113+
kubeNamespace,
114+
token,
115+
dryRun,
116+
inCluster,
117+
kubeNodeSelector,
118+
kubernetesRunnerType,
119+
tolerations,
120+
venonaVersion,
121+
kubeConfigPath,
122+
skipVersionCheck,
123+
verbose,
124+
agentId: name,
125+
terminateProcess: !installRuntime,
126+
});
127+
if (agentInstallStatusCode !== 0) {
128+
throw new Error(`\nAgent installation failed with code ${agentInstallStatusCode}`);
129+
}
130+
if (installRuntime) {
131+
await installRuntimeCmd.handler({
132+
'runtime-kube-context-name': kubeContextName,
133+
'runtime-kube-namespace': kubeNamespace,
134+
'agent-name': name,
135+
'runtime-kube-config-path': kubeConfigPath,
136+
'attach-runtime': true,
137+
'restart-agent': true,
138+
verbose,
139+
});
140+
}
141+
},
142+
});
143+
144+
module.exports = installAgentCmd;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/* eslint-disable max-len */
2+
const Command = require('../../Command');
3+
const deleteAgent = require('../agent/delete.cmd');
4+
const unInstallRoot = require('../root/uninstall.cmd');
5+
const { sdk } = require('../../../../logic');
6+
const { getKubeContext } = require('../../helpers/kubernetes');
7+
8+
9+
const unInstallAgentCmd = new Command({
10+
root: false,
11+
parent: unInstallRoot,
12+
command: 'agent',
13+
description: 'Uninstall an agent on kubernetes cluster',
14+
webDocs: {
15+
category: 'Agents',
16+
title: 'Uninstall',
17+
weight: 100,
18+
},
19+
builder: yargs => yargs
20+
.env('CF_ARG_') // this means that every process.env.CF_ARG_* will be passed to argv
21+
.option('name', {
22+
describe: 'Agent\'s name to be uninstalled',
23+
})
24+
.option('kube-context-name', {
25+
describe: 'Name of the kubernetes context on which venona should be uninstalled [$CF_ARG_KUBE_CONTEXT_NAME]',
26+
})
27+
.option('kube-namespace', {
28+
describe: 'Name of the namespace on which venona should be uninstalled [$CF_ARG_KUBE_NAMESPACE]',
29+
})
30+
.option('kube-config-path', {
31+
describe: 'Path to kubeconfig file (default is $HOME/.kube/config)',
32+
})
33+
.option('verbose', {
34+
describe: 'Print logs',
35+
}),
36+
handler: async (argv) => {
37+
const {
38+
name,
39+
'kube-namespace': kubeNamespace,
40+
'kube-config-path': kubeConfigPath,
41+
42+
} = argv;
43+
44+
let { 'kube-context-name': kubeContextName } = argv;
45+
46+
if (!kubeContextName) {
47+
kubeContextName = getKubeContext(kubeConfigPath);
48+
}
49+
50+
if (!name) {
51+
throw new Error('Name is a mandatory parameter');
52+
}
53+
54+
55+
await sdk.agents.unInstall({
56+
name,
57+
kubeContextName,
58+
kubeNamespace,
59+
terminateProcess: false,
60+
});
61+
await deleteAgent.handler({ name, id: name });
62+
process.exit(0);
63+
},
64+
});
65+
66+
module.exports = unInstallAgentCmd;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const Command = require('../../Command');
2+
3+
4+
const install = new Command({
5+
root: true,
6+
command: 'install',
7+
description: 'Instal a resource',
8+
usage: 'Install operation will install codefresh components on kubernetes cluster',
9+
webDocs: {
10+
description: 'Install a resource',
11+
category: 'Install Resources',
12+
title: 'Install',
13+
weight: 40,
14+
},
15+
});
16+
17+
module.exports = install;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const Command = require('../../Command');
2+
3+
4+
const unInstall = new Command({
5+
root: true,
6+
command: 'uninstall',
7+
description: 'Uninstal a resource',
8+
usage: 'Uninstall operation will uninstall codefresh components on kubernetes cluster',
9+
webDocs: {
10+
description: 'Uninstall a resource',
11+
category: 'Uninstall Resources',
12+
title: 'Uninstall',
13+
weight: 40,
14+
},
15+
});
16+
17+
module.exports = unInstall;
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/* eslint-disable max-len */
2+
const _ = require('lodash');
3+
const Command = require('../../Command');
4+
const { sdk } = require('../../../../logic');
5+
6+
7+
const attachAgentToRuntime = async (agent, runtime) => {
8+
const runtimes = _.get(agent, 'runtimes', []);
9+
const existingRT = _.find(runtimes, value => value === runtime);
10+
if (!existingRT) {
11+
runtimes.push(runtime);
12+
await sdk.agents.update({ agentId: agent.id, runtimes });
13+
}
14+
};
15+
16+
const attachRuntimeCmd = new Command({
17+
root: true,
18+
command: 'attach runtime',
19+
description: 'Attach a runtime to agent',
20+
usage: 'attach runtime',
21+
webDocs: {
22+
category: 'Runtime-Environments',
23+
title: 'Attach Runtime-Environments',
24+
weight: 100,
25+
},
26+
builder: yargs => yargs
27+
.env('CF_ARG_') // this means that every process.env.CF_ARG_* will be passed to argv
28+
.option('runtime-name', {
29+
describe: 'Runtime\'s name',
30+
})
31+
.option('agent-name', {
32+
describe: 'Agent\'s name',
33+
})
34+
.option('agent-id', {
35+
describe: 'Agent\'s ID',
36+
})
37+
.option('runtime-kube-context-name', {
38+
describe: 'Runtime kubernetes context',
39+
})
40+
.option('runtime-kube-namespace', {
41+
describe: 'Runtime\'s namespace',
42+
})
43+
.option('runtime-kube-config-path', {
44+
describe: 'Path to kubeconfig file for runtime (default is $HOME/.kube/config)',
45+
})
46+
.option('agent-kube-context-name', {
47+
describe: 'Agent kubernetes context',
48+
})
49+
.option('agent-kube-namespace', {
50+
describe: 'Agent\'s namespace',
51+
})
52+
.option('agent-kube-config-path', {
53+
describe: 'Path to kubeconfig file for the agent (default is $HOME/.kube/config)',
54+
})
55+
.option('restart-agent', {
56+
describe: 'restart agent afte install - default false',
57+
})
58+
.option('verbose', {
59+
describe: 'Print logs',
60+
}),
61+
handler: async (argv) => {
62+
const {
63+
'agent-name': agentName,
64+
'runtime-name': runtimeName,
65+
'agent-id': agentId,
66+
'runtime-kube-context-name': kubeContextName,
67+
'runtime-kube-namespace': kubeNamespace,
68+
'runtime-kube-config-path': kubeConfigPath,
69+
'agent-kube-context-name': agentKubeContextName,
70+
'agent-kube-namespace': agentKubeNamespace,
71+
'agent-kube-config-path': agentKubeConfigPath,
72+
'restart-agent': restartAgent,
73+
verbose,
74+
75+
} = argv;
76+
const { terminateProcess } = argv;
77+
let agent;
78+
if (_.isNull(runtimeName) || _.isUndefined(runtimeName) || runtimeName === '') {
79+
throw new Error('runtime name is mandatory');
80+
}
81+
if (agentName) {
82+
agent = await sdk.agents.getByName({ name: agentName });
83+
} else if (agentId) {
84+
agent = await sdk.agents.get({ agentId });
85+
} else {
86+
throw new Error('agent name or agent id is needed');
87+
}
88+
if (agent === '' || !agent) {
89+
throw new Error('agent was not found');
90+
}
91+
92+
await attachAgentToRuntime(agent, runtimeName);
93+
94+
// call venonactl to attach
95+
96+
await sdk.runtime.attach({
97+
kubeContextName,
98+
kubeNamespace,
99+
kubeConfigPath,
100+
agentKubeContextName,
101+
agentKubeNamespace,
102+
agentKubeConfigPath,
103+
runtimeName,
104+
verbose,
105+
restartAgent,
106+
terminateProcess: false,
107+
});
108+
if (!restartAgent) {
109+
console.log('Please restart agent\'s pod in order that changes will take effect');
110+
}
111+
if (terminateProcess || terminateProcess === undefined) {
112+
process.exit();
113+
}
114+
},
115+
});
116+
117+
118+
module.exports = attachRuntimeCmd;

0 commit comments

Comments
 (0)