Skip to content

Commit a1c98bf

Browse files
Saas 7417 - idiomatic runner installation (#493)
* added idiomatic installation * added build-node-selector flag to runner init command
1 parent e44594a commit a1c98bf

File tree

8 files changed

+885
-176
lines changed

8 files changed

+885
-176
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ const installAgentCmd = new Command({
8383
name, token,
8484
} = argv;
8585
const {
86+
'runtime-name': reName,
8687
'kube-node-selector': kubeNodeSelector,
8788
'dry-run': dryRun,
8889
'in-cluster': inCluster,
@@ -180,6 +181,7 @@ const installAgentCmd = new Command({
180181
}
181182
if (installRuntime) {
182183
return installRuntimeCmd.handler({
184+
'runtime-name': reName,
183185
'runtime-kube-context-name': kubeContextName,
184186
'runtime-kube-namespace': kubeNamespace,
185187
'agent-name': name,
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/* eslint-disable no-await-in-loop */
2+
const _ = require('lodash');
3+
const installationProgress = require('./installation-process');
4+
const { to } = require('./../../../../logic/cli-config/errors/awaitTo');
5+
const colors = require('colors');
6+
const {
7+
resolve, join,
8+
} = require('path');
9+
const {
10+
homedir,
11+
} = require('os');
12+
const {
13+
existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync,
14+
} = require('fs');
15+
16+
const CODEFRESH_PATH = resolve(homedir(), '.Codefresh');
17+
const RUNNER_INSTALLATIONS_PATH = join(CODEFRESH_PATH, 'installations');
18+
const INSTALLATION_STATE_FILE_PATH = join(RUNNER_INSTALLATIONS_PATH, 'installation_state.json');
19+
const STEP_STATUSES = {
20+
SUCCESS: 'success',
21+
FAILURE: 'failure',
22+
PENDING: 'pending',
23+
SKIPPED: 'skipped',
24+
};
25+
26+
function _ensureDirectory(location) {
27+
if (!existsSync(location)) {
28+
mkdirSync(location);
29+
}
30+
}
31+
32+
class InstallationPlan {
33+
constructor({
34+
pendingSteps,
35+
finishedSteps,
36+
progressReporter,
37+
errHandler,
38+
context,
39+
}) {
40+
this.state = {
41+
pendingSteps: pendingSteps || [],
42+
finishedSteps: finishedSteps || [],
43+
context: context || {},
44+
};
45+
this.completedSteps = {};
46+
this.progressReporter = progressReporter;
47+
this.errHandler = errHandler;
48+
_ensureDirectory(RUNNER_INSTALLATIONS_PATH);
49+
}
50+
51+
addStep({
52+
name,
53+
func,
54+
args,
55+
arg,
56+
errMessage,
57+
successMessage,
58+
installationEvent,
59+
exitOnError = true,
60+
condition = true,
61+
}) {
62+
if (!this.completedSteps[name]) {
63+
this.state.pendingSteps.push({
64+
name,
65+
func,
66+
args,
67+
arg,
68+
errMessage,
69+
successMessage,
70+
installationEvent,
71+
exitOnError,
72+
condition,
73+
status: STEP_STATUSES.PENDING,
74+
});
75+
}
76+
}
77+
78+
reset() {
79+
this.state.finishedSteps = this.state.finishedSteps.filter((step) => {
80+
if (step.status === STEP_STATUSES.SUCCESS || step.status === STEP_STATUSES.SKIPPED) {
81+
this.completedSteps[step.name] = step;
82+
return true;
83+
}
84+
return false;
85+
});
86+
87+
this.state.pendingSteps = [];
88+
}
89+
90+
async execute() {
91+
while (this.state.pendingSteps.length) {
92+
const step = this.state.pendingSteps.shift();
93+
this.state.finishedSteps.push(step);
94+
const _args = step.args || [step.arg];
95+
96+
if (!step.condition || (_.isFunction(step.condition) && !await step.condition())) {
97+
console.log(`skipping step: ${colors.cyan(step.name)}`);
98+
step.status = STEP_STATUSES.SKIPPED;
99+
this._writeState();
100+
// eslint-disable-next-line no-continue
101+
continue;
102+
}
103+
104+
if (step.name) {
105+
console.log(`executing step: ${colors.cyan(step.name)}`);
106+
}
107+
108+
const [stepErr] = await to(step.func(..._args));
109+
if (stepErr) {
110+
step.status = STEP_STATUSES.FAILURE;
111+
this._writeState();
112+
await this.errHandler(
113+
stepErr,
114+
step.errMessage || `Failed to ${step.name}`,
115+
this.progressReporter, step.installationEvent,
116+
step.exitOnError,
117+
);
118+
} else {
119+
step.status = STEP_STATUSES.SUCCESS;
120+
this._writeState();
121+
if (step.successMessage) {
122+
console.log(step.successMessage);
123+
}
124+
if (step.installationEvent && this.progressReporter) {
125+
await to(this.progressReporter.report(step.installationEvent, installationProgress.status.SUCCESS));
126+
}
127+
}
128+
}
129+
130+
InstallationPlan._cleanup();
131+
}
132+
133+
setProgressReporter(progressReporter) {
134+
this.progressReporter = progressReporter;
135+
}
136+
137+
setErrorHandler(errHandler) {
138+
this.errHandler = errHandler;
139+
}
140+
141+
addContext(key, value) {
142+
this.state.context[key] = value;
143+
}
144+
145+
getContext(key) {
146+
return this.state.context[key];
147+
}
148+
149+
_writeState() {
150+
writeFileSync(INSTALLATION_STATE_FILE_PATH, JSON.stringify(this.state));
151+
}
152+
153+
static _cleanup() {
154+
try {
155+
unlinkSync(INSTALLATION_STATE_FILE_PATH);
156+
} catch (err) {
157+
console.warn('** could not delete installation state file **');
158+
}
159+
}
160+
161+
static restorePreviousState() {
162+
try {
163+
const data = readFileSync(join(RUNNER_INSTALLATIONS_PATH, 'installation_state.json'));
164+
const oldState = JSON.parse(data.toString('utf-8'));
165+
return new InstallationPlan({ ...oldState });
166+
} catch (err) {
167+
return undefined;
168+
}
169+
}
170+
171+
printState() {
172+
this.state.finishedSteps.forEach((step) => {
173+
if (step.status === STEP_STATUSES.SUCCESS) {
174+
console.log(`${colors.green('✓')} ${step.name}`);
175+
} else if (step.status === STEP_STATUSES.SKIPPED) {
176+
console.log(`${colors.cyan('⤳')} ${step.name}`);
177+
} else if (step.status === STEP_STATUSES.FAILURE) {
178+
console.log(`${colors.red('✘')} ${step.name}`);
179+
}
180+
});
181+
182+
this.state.pendingSteps.forEach((step) => {
183+
console.log(`${colors.white('◼')} ${step.name}`);
184+
});
185+
}
186+
}
187+
188+
module.exports = InstallationPlan;

0 commit comments

Comments
 (0)