forked from Haufe-Lexware/wicked.haufe.io
-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathindex.js
316 lines (289 loc) · 9.88 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
'use strict';
/* global __dirname */
const fs = require('fs');
const path = require('path');
const axios = require('axios');
const wicked = require('wicked-sdk');
const https = require('https');
const kubernetesAgent = new https.Agent({ rejectUnauthorized: false });
function getDate() { return new Date().toISOString(); }
function debug(s) { if (process.env.DEBUG) console.log(`[${getDate()}] DEBUG - ${s}`); } // eslint-disable-line
function info(s) { console.log(`[${getDate()}] INFO - ${s}`); }
function warn(s) { console.log(`[${getDate()}] WARN - ${s}`); }
function error(s) { console.error(`[${getDate()}] ERROR - ${s}`); }
const TOKEN_FILE = '/var/run/secrets/kubernetes.io/serviceaccount/token';
let APP_ID = 'app-id';
let API_ID = 'api-id';
let PLAN_ID = 'unlimited';
let CLIENT_TYPE = wicked.WickedClientType.Public_SPA;
let SECRET_NAME = 'some-secret';
let NAMESPACE = 'default';
let initSuccess = true;
let TOKEN;
if (!process.env.IGNORE_K8S) {
if (!process.env.KUBERNETES_SERVICE_HOST) {
error('KUBERNETES_SERVICE_HOST is not set.');
initSuccess = false;
}
if (!process.env.KUBERNETES_SERVICE_PORT) {
error('KUBERNETES_SERVICE_PORT is not set.');
initSuccess = false;
}
if (!fs.existsSync(TOKEN_FILE)) {
error('File ' + TOKEN_FILE + ' does not exist.');
initSuccess = false;
} else {
TOKEN = fs.readFileSync(TOKEN_FILE, 'utf8');
}
}
if (!process.env.REDIRECT_URI) {
error('REDIRECT_URI is not set.');
initSuccess = false;
}
if (!initSuccess) {
error('Not successful, exiting.');
process.exit(1);
}
if (process.env.NAMESPACE) {
NAMESPACE = process.env.NAMESPACE;
}
if (process.env.APP_ID) {
APP_ID = process.env.APP_ID;
}
if (process.env.API_ID) {
API_ID = process.env.API_ID;
}
if (process.env.PLAN_ID) {
PLAN_ID = process.env.PLAN_ID;
}
if (process.env.CLIENT_TYPE) {
CLIENT_TYPE = process.env.CLIENT_TYPE;
}
if (process.env.SECRET_NAME) {
SECRET_NAME = process.env.SECRET_NAME;
}
const REDIRECT_URIS = process.env.REDIRECT_URI.split('|');
info('Using k8s Namespace: ' + NAMESPACE);
info('Using App ID: ' + APP_ID);
info('Using API ID: ' + API_ID);
info('Using Plan ID: ' + PLAN_ID);
info('Using Client Type: ' + CLIENT_TYPE);
info('Using Secret Name: ' + SECRET_NAME);
info('Using Redirect URIs: ' + REDIRECT_URIS);
const KUBERNETES_API = 'https://' + process.env.KUBERNETES_SERVICE_HOST +
':' + process.env.KUBERNETES_SERVICE_PORT + '/api/v1/';
const USER_AGENT = 'auto-deploy';
const wickedOptions = {
userAgentName: USER_AGENT,
userAgentVersion: getVersion(),
doNotPollConfigHash: true
};
(async () => {
try {
debug('Attempting init wicked');
await initWicked(wickedOptions);
debug('Finished init wicked');
debug('Attempting init machine user');
await wicked.initMachineUser(USER_AGENT);
debug('Finished init machine user');
await createAppIfNotPresent(APP_ID, REDIRECT_URIS, CLIENT_TYPE);
const subscription = await createSubscriptionIfNotPresent(APP_ID, API_ID);
if (!process.env.IGNORE_K8S) {
await upsertKubernetesSecret(subscription);
} else {
warn('Detected env var IGNORE_K8S - not upserting Kubernetes secret.');
}
info('Successfully created or checked application/subscription.');
process.exit(0);
} catch (err) {
error('Initialization failed.');
if (err.statusCode) {
error('Status code: ' + err.statusCode);
}
if (err.body) {
error('Error body:');
error(JSON.stringify(err.body));
}
process.exit(1);
}
})();
async function initWicked(wickedOptions) {
info('Initializing wicked.');
await wicked.initialize(wickedOptions);
}
async function createAppIfNotPresent(appId, redirectUris, clientType) {
info('Create application if not present');
let appInfo = null;
try {
debug(`Attempting get of application ${appId}`);
appInfo = await wicked.getApplication(appId);
debug(`appInfo: ${JSON.stringify(appInfo)}`);
} catch (err) {
if (err.statusCode !== 404) {
debug(`Caught err: ${err}`);
debug(err.stack);
throw err;
}
// App not present, fine; we have status 404
debug(`Application ${appId} was not found.`);
}
if (appInfo) {
info('Application is already present.');
// Check whether name and redirect URIs match
const presentUris = appInfo.redirectUris.join('|');
const newUris = redirectUris.join('|');
if (presentUris !== newUris
|| appInfo.clientType !== clientType) {
info('** Application has changed, patching...');
await wicked.patchApplication(appId, {
id: appId,
clientType,
redirectUris
});
debug('Patching application finished');
} else {
info('Application does not need patch.');
}
} else {
info('Creating application...');
await wicked.createApplication({
id: appId,
name: appId + ' (auto generated)',
clientType: clientType,
redirectUris: redirectUris
});
debug('Creating application finished.');
}
}
async function createSubscriptionIfNotPresent(appId, apiId) {
info('Creating subscription if not present...');
debug('Attempting get subscriptions');
const subsList = await wicked.getSubscriptions(appId);
debug(`subsList: ${JSON.stringify(subsList)}`);
const subs = subsList.find(s => s.api === apiId);
if (subs) {
info('Subscription is present.');
if (subs.plan !== PLAN_ID) {
info('** Plan ID has changed, deleting...');
await wicked.deleteSubscription(appId, apiId);
debug('Subscription delete finished');
} else {
info('Subscription is correct, not changing.');
return subs;
}
}
info('Creating subscription...');
await wicked.createSubscription(appId, {
application: appId,
api: apiId,
plan: PLAN_ID
});
debug('Create subscription finished');
return;
}
function urlCombine(p1, p2) {
const pp1 = p1.endsWith('/') ? p1.substring(0, p1.length - 1) : p1;
const pp2 = p2.startsWith('/') ? p2.substring(1) : p2;
return pp1 + '/' + pp2;
}
async function kubernetesAction(endpoint, method, body) {
const uri = urlCombine(KUBERNETES_API, endpoint);
info("Kubernetes: " + method + " " + uri);
const req = {
url: uri,
method: method,
headers: {
'Authorization': 'Bearer ' + TOKEN,
'Accept': 'application/json'
},
httpsAgent: kubernetesAgent
};
if (body) {
req.data = body;
}
try {
debug('Attempting call to Kubernetes.');
const res = await axios(req);
debug('Call finished, data:');
debug(JSON.stringify(res.data));
return res.data;
} catch (err) {
if (method === 'GET' && err.response && err.response.status === 404) {
// Special treatment for 404
debug('Caught 404, returning null');
return null;
}
debug(`Caught axios error: ${err.message}`);
debug(err.stack);
throw err;
}
}
async function kubernetesGet(endpoint) {
return await kubernetesAction(endpoint, 'GET', null);
}
async function kubernetesPost(endpoint, body) {
return await kubernetesAction(endpoint, 'POST', body);
}
async function kubernetesDelete(endpoint) {
return await kubernetesAction(endpoint, 'DELETE', null);
}
async function upsertKubernetesSecret(subscription) {
debug('upsertKubernetesSecret');
const secretUrl = 'namespaces/' + NAMESPACE + '/secrets';
const secretGetUrl = urlCombine(secretUrl, SECRET_NAME);
debug('Attempting deleteKubernetesSecretIfPresent');
await deleteKubernetesSecretIfPresent(secretGetUrl);
debug('Finished deleteKubernetesSecretIfPresent');
debug('Attempting createKubernetesSecret');
await createKubernetesSecret(subscription, secretUrl);
debug('Finished createKubernetesSecret');
return;
}
async function deleteKubernetesSecretIfPresent(getUrl) {
debug('deleteKubernetesSecretIfPresent');
debug(`Attempt kubernetesGet(${getUrl}`);
const secret = await kubernetesGet(getUrl);
debug('kubernetesGet returned.');
if (secret) {
debug(`Attempt kubernetesDelete(${getUrl})`);
await kubernetesDelete(getUrl);
debug('kubernetesDelete returned.');
}
}
async function createKubernetesSecret(subscription, secretUrl) {
debug('createKubernetesSecret()');
let stringData = {};
if (subscription.clientId && subscription.clientSecret) {
stringData.client_id = subscription.clientId;
stringData.client_secret = subscription.clientSecret;
} else if (subscription.apikey) {
stringData.api_key = subscription.apikey;
} else {
// wtf?
const errorMessage = 'Subscription does not contain neither client_id and client_secret nor apikey';
error(errorMessage + ':');
error(JSON.stringify(subscription));
throw new Error(errorMessage);
}
debug(`Attempting POST ${secretUrl}`);
await kubernetesPost(secretUrl, {
metadata: { name: SECRET_NAME },
stringData: stringData
});
debug(`POST to ${secretUrl} finished`);
}
function getVersion() {
const packageFile = path.join(__dirname, 'package.json');
if (fs.existsSync(packageFile)) {
try {
const packageInfo = JSON.parse(fs.readFileSync(packageFile, 'utf8'));
if (packageInfo.version) {
return packageInfo.version;
}
} catch (ex) {
error(ex);
}
}
warn("Could not retrieve package version, returning 0.0.0.");
return "0.0.0";
}