Files

137 lines
6.0 KiB
JavaScript

'use strict';
/**
* redeploy.js — запуск операции redeploy для инстанса tf-docs через Nubes API.
*
* Авторизация: Bearer-токен из tf_provider/secrets/test.token.
* Токен НЕ печатается.
*
* Запуск: node llm_project/tests/redeploy.js [--uid <instanceUid>] [--profile test]
*/
const fs = require('fs');
const argv = process.argv.slice(2);
function arg(name, def) {
const i = argv.indexOf('--' + name);
return i !== -1 && argv[i + 1] ? argv[i + 1] : def;
}
const UID = arg('uid', '7a48a863-9b40-4cef-a352-033ee9dde8de');
const TOKEN_FILE = arg('token', '/home/naeel/TF/tf_provider/secrets/test.token');
const BASE = arg('base', 'https://lk-api-gateway-test.ngcloud.ru/api/v1/svc');
const ACTION = arg('action', 'redeploy');
const HEADERS = () => ({
Authorization: 'Bearer ' + fs.readFileSync(TOKEN_FILE, 'utf8').trim(),
'Content-Type': 'application/json',
'User-Agent': 'Mozilla/5.0',
});
async function api(method, path, body) {
const opts = { method, headers: HEADERS() };
if (body !== undefined) opts.body = JSON.stringify(body);
const resp = await fetch(BASE + path, opts);
const text = await resp.text();
let json = null;
try { json = JSON.parse(text); } catch { /* не JSON */ }
return { status: resp.status, text, json, location: resp.headers.get('location') };
}
/** Извлекает UID из заголовка Location (последний сегмент пути). */
function uidFromLocation(loc) {
if (!loc) return '';
const clean = loc.split('?')[0].replace(/\/+$/, '');
const parts = clean.split('/');
return parts[parts.length - 1] || '';
}
function sleep(ms) { return new Promise((r) => setTimeout(r, ms)); }
async function main() {
console.log('Инстанс:', UID, '| операция:', ACTION);
// 1) Состояние инстанса: доступные операции + текущие params.
const st = await api('GET', `/instances/${UID}`);
if (st.status !== 200) {
console.error('Ошибка GET /instances:', st.status, st.text.slice(0, 300));
process.exit(1);
}
const inst = (st.json && st.json.instance) || {};
console.log('Статус:', inst.explainedStatus, '| inProgress:', inst.operationIsInProgress, '| pending:', inst.operationIsPending);
const instanceParams = (inst.state && inst.state.params) || {};
const ops = inst.availableOperations || [];
const op = ops.find((o) => String(o.operation).toLowerCase() === ACTION.toLowerCase());
if (!op) {
console.error('Операция', ACTION, 'недоступна. Доступные:', ops.map((o) => o.operation).join(', '));
process.exit(1);
}
console.log('svcOperationId:', op.svcOperationId);
// 2) Создать операцию.
const created = await api('POST', '/instanceOperations', {
instanceUid: UID,
svcOperationId: op.svcOperationId,
operation: ACTION,
});
let opUid = '';
if (created.json && (created.json.uid || created.json.instanceOperationUid || created.json.opUid)) {
opUid = created.json.uid || created.json.instanceOperationUid || created.json.opUid;
}
if (!opUid) opUid = uidFromLocation(created.location);
if (!opUid) opUid = created.text.replace(/^"|"$/g, '').trim();
console.log('Создана операция:', created.status, '| opUid:', opUid);
if (!opUid || opUid.length < 8) {
console.error('Не удалось получить opUid. Ответ:', created.text.slice(0, 300));
process.exit(1);
}
// 2б) Установить требуемые CFS-параметры (например startupConfiguration) из live-состояния.
const cfs = await api('GET', `/instanceOperations/${opUid}?fields=cfsParams`);
const cfsParams = (cfs.json && cfs.json.instanceOperation && cfs.json.instanceOperation.cfsParams) || [];
for (const p of cfsParams) {
const val = instanceParams[p.svcOperationCfsParam];
if (val === undefined) {
console.log(' параметр', p.svcOperationCfsParam, '(id=' + p.svcOperationCfsParamId + ') — в состоянии нет, пропуск');
continue;
}
const paramValue = typeof val === 'string' ? val : JSON.stringify(val);
const pr = await api('POST', '/instanceOperationCfsParams', {
instanceOperationUid: opUid,
svcOperationCfsParamId: p.svcOperationCfsParamId,
paramValue: paramValue,
});
console.log(' параметр', p.svcOperationCfsParam, '=', paramValue, '->', pr.status);
}
// 3) Запустить операцию.
const run = await api('POST', `/instanceOperations/${opUid}/run`, {});
console.log('Запуск:', run.status, run.text.slice(0, 120));
// 4) Ждать завершения.
const deadline = Date.now() + 6 * 60 * 1000;
let last = '';
while (Date.now() < deadline) {
await sleep(5000);
const o = await api('GET', `/instanceOperations/${opUid}?fields=dtFinish,isSuccessful,errorLog,isInProgress,isPending,duration,stages`);
if (o.status !== 200) { console.log(' poll:', o.status); continue; }
const d = (o.json && (o.json.instanceOperation || o.json)) || {};
const line = ` inProgress=${d.isInProgress} pending=${d.isPending} finish=${d.dtFinish || '—'} ok=${d.isSuccessful}`;
if (line !== last) { console.log(line); last = line; }
if (d.dtFinish && !d.isInProgress && !d.isPending) {
if (d.isSuccessful) {
console.log('РЕДЕПЛОЙ ЗАВЕРШЁН УСПЕШНО');
} else {
console.error('РЕДЕПЛОЙ ЗАВЕРШЁН С ОШИБКОЙ:', d.errorLog || '(нет errorLog)');
process.exit(2);
}
return;
}
}
console.error('Таймаут ожидания операции');
process.exit(3);
}
main().catch((e) => { console.error('FATAL', e.message); process.exit(1); });