Repositorio del curso CCOM4030 el semestre B91 del proyecto Artesanías con el Instituto de Cultura

docker.js 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. 'use strict';
  2. // @ts-check
  3. // ==================================================================================
  4. // docker.js
  5. // ----------------------------------------------------------------------------------
  6. // Description: System Information - library
  7. // for Node.js
  8. // Copyright: (c) 2014 - 2020
  9. // Author: Sebastian Hildebrandt
  10. // ----------------------------------------------------------------------------------
  11. // License: MIT
  12. // ==================================================================================
  13. // 13. Docker
  14. // ----------------------------------------------------------------------------------
  15. const util = require('./util');
  16. const DockerSocket = require('./dockerSocket');
  17. let _platform = process.platform;
  18. const _windows = (_platform === 'win32');
  19. let _docker_container_stats = {};
  20. let _docker_socket;
  21. let _docker_last_read = 0;
  22. // --------------------------
  23. // get containers (parameter all: get also inactive/exited containers)
  24. function dockerInfo(callback) {
  25. return new Promise((resolve) => {
  26. process.nextTick(() => {
  27. if (!_docker_socket) {
  28. _docker_socket = new DockerSocket();
  29. }
  30. const result = {};
  31. _docker_socket.getInfo(data => {
  32. result.id = data.ID;
  33. result.containers = data.Containers;
  34. result.containersRunning = data.ContainersRunning;
  35. result.containersPaused = data.ContainersPaused;
  36. result.containersStopped = data.ContainersStopped;
  37. result.images = data.Images;
  38. result.driver = data.Driver;
  39. result.memoryLimit = data.MemoryLimit;
  40. result.swapLimit = data.SwapLimit;
  41. result.kernelMemory = data.KernelMemory;
  42. result.cpuCfsPeriod = data.CpuCfsPeriod;
  43. result.cpuCfsQuota = data.CpuCfsQuota;
  44. result.cpuShares = data.CPUShares;
  45. result.cpuSet = data.CPUSet;
  46. result.ipv4Forwarding = data.IPv4Forwarding;
  47. result.bridgeNfIptables = data.BridgeNfIptables;
  48. result.bridgeNfIp6tables = data.BridgeNfIp6tables;
  49. result.debug = data.Debug;
  50. result.nfd = data.NFd;
  51. result.oomKillDisable = data.OomKillDisable;
  52. result.ngoroutines = data.NGoroutines;
  53. result.systemTime = data.SystemTime;
  54. result.loggingDriver = data.LoggingDriver;
  55. result.cgroupDriver = data.CgroupDriver;
  56. result.nEventsListener = data.NEventsListener;
  57. result.kernelVersion = data.KernelVersion;
  58. result.operatingSystem = data.OperatingSystem;
  59. result.osType = data.OSType;
  60. result.architecture = data.Architecture;
  61. result.ncpu = data.NCPU;
  62. result.memTotal = data.MemTotal;
  63. result.dockerRootDir = data.DockerRootDir;
  64. result.httpProxy = data.HttpProxy;
  65. result.httpsProxy = data.HttpsProxy;
  66. result.noProxy = data.NoProxy;
  67. result.name = data.Name;
  68. result.labels = data.Labels;
  69. result.experimentalBuild = data.ExperimentalBuild;
  70. result.serverVersion = data.ServerVersion;
  71. result.clusterStore = data.ClusterStore;
  72. result.clusterAdvertise = data.ClusterAdvertise;
  73. result.defaultRuntime = data.DefaultRuntime;
  74. result.liveRestoreEnabled = data.LiveRestoreEnabled;
  75. result.isolation = data.Isolation;
  76. result.initBinary = data.InitBinary;
  77. result.productLicense = data.ProductLicense;
  78. if (callback) { callback(result); }
  79. resolve(result);
  80. });
  81. });
  82. });
  83. }
  84. exports.dockerInfo = dockerInfo;
  85. function dockerContainers(all, callback) {
  86. function inContainers(containers, id) {
  87. let filtered = containers.filter(obj => {
  88. /**
  89. * @namespace
  90. * @property {string} Id
  91. */
  92. return (obj.Id && (obj.Id === id));
  93. });
  94. return (filtered.length > 0);
  95. }
  96. // fallback - if only callback is given
  97. if (util.isFunction(all) && !callback) {
  98. callback = all;
  99. all = false;
  100. }
  101. all = all || false;
  102. let result = [];
  103. return new Promise((resolve) => {
  104. process.nextTick(() => {
  105. if (!_docker_socket) {
  106. _docker_socket = new DockerSocket();
  107. }
  108. const workload = [];
  109. _docker_socket.listContainers(all, data => {
  110. let docker_containers = {};
  111. try {
  112. docker_containers = data;
  113. if (docker_containers && Object.prototype.toString.call(docker_containers) === '[object Array]' && docker_containers.length > 0) {
  114. // GC in _docker_container_stats
  115. for (let key in _docker_container_stats) {
  116. if ({}.hasOwnProperty.call(_docker_container_stats, key)) {
  117. if (!inContainers(docker_containers, key)) delete _docker_container_stats[key];
  118. }
  119. }
  120. docker_containers.forEach(function (element) {
  121. if (element.Names && Object.prototype.toString.call(element.Names) === '[object Array]' && element.Names.length > 0) {
  122. element.Name = element.Names[0].replace(/^\/|\/$/g, '');
  123. }
  124. workload.push(dockerContainerInspect(element.Id.trim(), element));
  125. // result.push({
  126. // id: element.Id,
  127. // name: element.Name,
  128. // image: element.Image,
  129. // imageID: element.ImageID,
  130. // command: element.Command,
  131. // created: element.Created,
  132. // state: element.State,
  133. // ports: element.Ports,
  134. // mounts: element.Mounts,
  135. // // hostconfig: element.HostConfig,
  136. // // network: element.NetworkSettings
  137. // });
  138. });
  139. if (workload.length) {
  140. Promise.all(
  141. workload
  142. ).then(data => {
  143. if (callback) { callback(data); }
  144. resolve(data);
  145. });
  146. } else {
  147. if (callback) { callback(result); }
  148. resolve(result);
  149. }
  150. } else {
  151. if (callback) { callback(result); }
  152. resolve(result);
  153. }
  154. } catch (err) {
  155. // GC in _docker_container_stats
  156. for (let key in _docker_container_stats) {
  157. if ({}.hasOwnProperty.call(_docker_container_stats, key)) {
  158. if (!inContainers(docker_containers, key)) delete _docker_container_stats[key];
  159. }
  160. }
  161. if (callback) { callback(result); }
  162. resolve(result);
  163. }
  164. });
  165. });
  166. });
  167. }
  168. // --------------------------
  169. // container inspect (for one container)
  170. function dockerContainerInspect(containerID, payload) {
  171. containerID = containerID || '';
  172. return new Promise((resolve) => {
  173. process.nextTick(() => {
  174. if (containerID) {
  175. if (!_docker_socket) {
  176. _docker_socket = new DockerSocket();
  177. }
  178. _docker_socket.getInspect(containerID.trim(), data => {
  179. try {
  180. resolve({
  181. id: payload.Id,
  182. name: payload.Name,
  183. image: payload.Image,
  184. imageID: payload.ImageID,
  185. command: payload.Command,
  186. created: payload.Created,
  187. started: data.State && data.State.StartedAt ? Math.round(new Date(data.State.StartedAt).getTime() / 1000) : 0,
  188. finished: data.State && data.State.FinishedAt && !data.State.FinishedAt.startsWith('0001-01-01') ? Math.round(new Date(data.State.FinishedAt).getTime() / 1000) : 0,
  189. createdAt: data.Created ? data.Created : '',
  190. startedAt: data.State && data.State.StartedAt ? data.State.StartedAt : '',
  191. finishedAt: data.State && data.State.FinishedAt && !data.State.FinishedAt.startsWith('0001-01-01') ? data.State.FinishedAt : '',
  192. state: payload.State,
  193. restartCount: data.RestartCount || 0,
  194. platform: data.Platform || '',
  195. driver: data.Driver || '',
  196. ports: payload.Ports,
  197. mounts: payload.Mounts,
  198. // hostconfig: payload.HostConfig,
  199. // network: payload.NetworkSettings
  200. });
  201. } catch (err) {
  202. resolve();
  203. }
  204. });
  205. } else {
  206. resolve();
  207. }
  208. });
  209. });
  210. }
  211. exports.dockerContainers = dockerContainers;
  212. // --------------------------
  213. // helper functions for calculation of docker stats
  214. function docker_calcCPUPercent(cpu_stats, precpu_stats) {
  215. /**
  216. * @namespace
  217. * @property {object} cpu_usage
  218. * @property {number} cpu_usage.total_usage
  219. * @property {number} system_cpu_usage
  220. * @property {object} cpu_usage
  221. * @property {Array} cpu_usage.percpu_usage
  222. */
  223. if (!_windows) {
  224. let cpuPercent = 0.0;
  225. // calculate the change for the cpu usage of the container in between readings
  226. let cpuDelta = cpu_stats.cpu_usage.total_usage - precpu_stats.cpu_usage.total_usage;
  227. // calculate the change for the entire system between readings
  228. let systemDelta = cpu_stats.system_cpu_usage - precpu_stats.system_cpu_usage;
  229. if (systemDelta > 0.0 && cpuDelta > 0.0) {
  230. // calculate the change for the cpu usage of the container in between readings
  231. cpuPercent = (cpuDelta / systemDelta) * cpu_stats.cpu_usage.percpu_usage.length * 100.0;
  232. }
  233. return cpuPercent;
  234. } else {
  235. let nanoSecNow = util.nanoSeconds();
  236. let cpuPercent = 0.0;
  237. if (_docker_last_read > 0) {
  238. let possIntervals = (nanoSecNow - _docker_last_read); // / 100 * os.cpus().length;
  239. let intervalsUsed = cpu_stats.cpu_usage.total_usage - precpu_stats.cpu_usage.total_usage;
  240. if (possIntervals > 0) {
  241. cpuPercent = 100.0 * intervalsUsed / possIntervals;
  242. }
  243. }
  244. _docker_last_read = nanoSecNow;
  245. return cpuPercent;
  246. }
  247. }
  248. function docker_calcNetworkIO(networks) {
  249. let rx;
  250. let tx;
  251. for (let key in networks) {
  252. // skip loop if the property is from prototype
  253. if (!{}.hasOwnProperty.call(networks, key)) continue;
  254. /**
  255. * @namespace
  256. * @property {number} rx_bytes
  257. * @property {number} tx_bytes
  258. */
  259. let obj = networks[key];
  260. rx = +obj.rx_bytes;
  261. tx = +obj.tx_bytes;
  262. }
  263. return {
  264. rx: rx,
  265. tx: tx
  266. };
  267. }
  268. function docker_calcBlockIO(blkio_stats) {
  269. let result = {
  270. r: 0,
  271. w: 0
  272. };
  273. /**
  274. * @namespace
  275. * @property {Array} io_service_bytes_recursive
  276. */
  277. if (blkio_stats && blkio_stats.io_service_bytes_recursive && Object.prototype.toString.call(blkio_stats.io_service_bytes_recursive) === '[object Array]' && blkio_stats.io_service_bytes_recursive.length > 0) {
  278. blkio_stats.io_service_bytes_recursive.forEach(function (element) {
  279. /**
  280. * @namespace
  281. * @property {string} op
  282. * @property {number} value
  283. */
  284. if (element.op && element.op.toLowerCase() === 'read' && element.value) {
  285. result.r += element.value;
  286. }
  287. if (element.op && element.op.toLowerCase() === 'write' && element.value) {
  288. result.w += element.value;
  289. }
  290. });
  291. }
  292. return result;
  293. }
  294. function dockerContainerStats(containerIDs, callback) {
  295. let containerArray = [];
  296. // fallback - if only callback is given
  297. if (util.isFunction(containerIDs) && !callback) {
  298. callback = containerIDs;
  299. containerArray = ['*'];
  300. } else {
  301. containerIDs = containerIDs || '*';
  302. containerIDs = containerIDs.trim().toLowerCase().replace(/,+/g, '|');
  303. containerArray = containerIDs.split('|');
  304. }
  305. return new Promise((resolve) => {
  306. process.nextTick(() => {
  307. const result = [];
  308. const workload = [];
  309. if (containerArray.length && containerArray[0].trim() === '*') {
  310. containerArray = [];
  311. dockerContainers().then(allContainers => {
  312. for (let container of allContainers) {
  313. containerArray.push(container.id);
  314. }
  315. dockerContainerStats(containerArray.join(',')).then(result => {
  316. if (callback) { callback(result); }
  317. resolve(result);
  318. });
  319. });
  320. } else {
  321. for (let containerID of containerArray) {
  322. workload.push(dockerContainerStatsSingle(containerID.trim()));
  323. }
  324. if (workload.length) {
  325. Promise.all(
  326. workload
  327. ).then(data => {
  328. if (callback) { callback(data); }
  329. resolve(data);
  330. });
  331. } else {
  332. if (callback) { callback(result); }
  333. resolve(result);
  334. }
  335. }
  336. });
  337. });
  338. }
  339. // --------------------------
  340. // container stats (for one container)
  341. function dockerContainerStatsSingle(containerID) {
  342. containerID = containerID || '';
  343. let result = {
  344. id: containerID,
  345. mem_usage: 0,
  346. mem_limit: 0,
  347. mem_percent: 0,
  348. cpu_percent: 0,
  349. pids: 0,
  350. netIO: {
  351. rx: 0,
  352. wx: 0
  353. },
  354. blockIO: {
  355. r: 0,
  356. w: 0
  357. }
  358. };
  359. return new Promise((resolve) => {
  360. process.nextTick(() => {
  361. if (containerID) {
  362. if (!_docker_socket) {
  363. _docker_socket = new DockerSocket();
  364. }
  365. _docker_socket.getInspect(containerID, dataInspect => {
  366. try {
  367. _docker_socket.getStats(containerID, data => {
  368. try {
  369. let stats = data;
  370. /**
  371. * @namespace
  372. * @property {Object} memory_stats
  373. * @property {number} memory_stats.usage
  374. * @property {number} memory_stats.limit
  375. * @property {Object} cpu_stats
  376. * @property {Object} pids_stats
  377. * @property {number} pids_stats.current
  378. * @property {Object} networks
  379. * @property {Object} blkio_stats
  380. */
  381. if (!stats.message) {
  382. result.mem_usage = (stats.memory_stats && stats.memory_stats.usage ? stats.memory_stats.usage : 0);
  383. result.mem_limit = (stats.memory_stats && stats.memory_stats.limit ? stats.memory_stats.limit : 0);
  384. result.mem_percent = (stats.memory_stats && stats.memory_stats.usage && stats.memory_stats.limit ? stats.memory_stats.usage / stats.memory_stats.limit * 100.0 : 0);
  385. result.cpu_percent = (stats.cpu_stats && stats.precpu_stats ? docker_calcCPUPercent(stats.cpu_stats, stats.precpu_stats) : 0);
  386. result.pids = (stats.pids_stats && stats.pids_stats.current ? stats.pids_stats.current : 0);
  387. result.restartCount = (dataInspect.RestartCount ? dataInspect.RestartCount : 0);
  388. if (stats.networks) result.netIO = docker_calcNetworkIO(stats.networks);
  389. if (stats.blkio_stats) result.blockIO = docker_calcBlockIO(stats.blkio_stats);
  390. result.cpu_stats = (stats.cpu_stats ? stats.cpu_stats : {});
  391. result.precpu_stats = (stats.precpu_stats ? stats.precpu_stats : {});
  392. result.memory_stats = (stats.memory_stats ? stats.memory_stats : {});
  393. result.networks = (stats.networks ? stats.networks : {});
  394. }
  395. } catch (err) {
  396. util.noop();
  397. }
  398. // }
  399. resolve(result);
  400. });
  401. } catch (err) {
  402. util.noop();
  403. }
  404. });
  405. } else {
  406. resolve(result);
  407. }
  408. });
  409. });
  410. }
  411. exports.dockerContainerStats = dockerContainerStats;
  412. // --------------------------
  413. // container processes (for one container)
  414. function dockerContainerProcesses(containerID, callback) {
  415. containerID = containerID || '';
  416. let result = [];
  417. return new Promise((resolve) => {
  418. process.nextTick(() => {
  419. if (containerID) {
  420. if (!_docker_socket) {
  421. _docker_socket = new DockerSocket();
  422. }
  423. _docker_socket.getProcesses(containerID, data => {
  424. /**
  425. * @namespace
  426. * @property {Array} Titles
  427. * @property {Array} Processes
  428. **/
  429. try {
  430. if (data && data.Titles && data.Processes) {
  431. let titles = data.Titles.map(function (value) {
  432. return value.toUpperCase();
  433. });
  434. let pos_pid = titles.indexOf('PID');
  435. let pos_ppid = titles.indexOf('PPID');
  436. let pos_pgid = titles.indexOf('PGID');
  437. let pos_vsz = titles.indexOf('VSZ');
  438. let pos_time = titles.indexOf('TIME');
  439. let pos_elapsed = titles.indexOf('ELAPSED');
  440. let pos_ni = titles.indexOf('NI');
  441. let pos_ruser = titles.indexOf('RUSER');
  442. let pos_user = titles.indexOf('USER');
  443. let pos_rgroup = titles.indexOf('RGROUP');
  444. let pos_group = titles.indexOf('GROUP');
  445. let pos_stat = titles.indexOf('STAT');
  446. let pos_rss = titles.indexOf('RSS');
  447. let pos_command = titles.indexOf('COMMAND');
  448. data.Processes.forEach(process => {
  449. result.push({
  450. pid_host: (pos_pid >= 0 ? process[pos_pid] : ''),
  451. ppid: (pos_ppid >= 0 ? process[pos_ppid] : ''),
  452. pgid: (pos_pgid >= 0 ? process[pos_pgid] : ''),
  453. user: (pos_user >= 0 ? process[pos_user] : ''),
  454. ruser: (pos_ruser >= 0 ? process[pos_ruser] : ''),
  455. group: (pos_group >= 0 ? process[pos_group] : ''),
  456. rgroup: (pos_rgroup >= 0 ? process[pos_rgroup] : ''),
  457. stat: (pos_stat >= 0 ? process[pos_stat] : ''),
  458. time: (pos_time >= 0 ? process[pos_time] : ''),
  459. elapsed: (pos_elapsed >= 0 ? process[pos_elapsed] : ''),
  460. nice: (pos_ni >= 0 ? process[pos_ni] : ''),
  461. rss: (pos_rss >= 0 ? process[pos_rss] : ''),
  462. vsz: (pos_vsz >= 0 ? process[pos_vsz] : ''),
  463. command: (pos_command >= 0 ? process[pos_command] : '')
  464. });
  465. });
  466. }
  467. } catch (err) {
  468. util.noop();
  469. }
  470. if (callback) { callback(result); }
  471. resolve(result);
  472. });
  473. } else {
  474. if (callback) { callback(result); }
  475. resolve(result);
  476. }
  477. });
  478. });
  479. }
  480. exports.dockerContainerProcesses = dockerContainerProcesses;
  481. function dockerAll(callback) {
  482. return new Promise((resolve) => {
  483. process.nextTick(() => {
  484. dockerContainers(true).then(result => {
  485. if (result && Object.prototype.toString.call(result) === '[object Array]' && result.length > 0) {
  486. let l = result.length;
  487. result.forEach(function (element) {
  488. dockerContainerStats(element.id).then(res => {
  489. // include stats in array
  490. element.mem_usage = res[0].mem_usage;
  491. element.mem_limit = res[0].mem_limit;
  492. element.mem_percent = res[0].mem_percent;
  493. element.cpu_percent = res[0].cpu_percent;
  494. element.pids = res[0].pids;
  495. element.netIO = res[0].netIO;
  496. element.blockIO = res[0].blockIO;
  497. element.cpu_stats = res[0].cpu_stats;
  498. element.precpu_stats = res[0].precpu_stats;
  499. element.memory_stats = res[0].memory_stats;
  500. element.networks = res[0].networks;
  501. dockerContainerProcesses(element.id).then(processes => {
  502. element.processes = processes;
  503. l -= 1;
  504. if (l === 0) {
  505. if (callback) { callback(result); }
  506. resolve(result);
  507. }
  508. });
  509. // all done??
  510. });
  511. });
  512. } else {
  513. if (callback) { callback(result); }
  514. resolve(result);
  515. }
  516. });
  517. });
  518. });
  519. }
  520. exports.dockerAll = dockerAll;