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

docker.js 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  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. }
  151. } catch (err) {
  152. // GC in _docker_container_stats
  153. for (let key in _docker_container_stats) {
  154. if ({}.hasOwnProperty.call(_docker_container_stats, key)) {
  155. if (!inContainers(docker_containers, key)) delete _docker_container_stats[key];
  156. }
  157. }
  158. if (callback) { callback(result); }
  159. resolve(result);
  160. }
  161. });
  162. });
  163. });
  164. }
  165. // --------------------------
  166. // container inspect (for one container)
  167. function dockerContainerInspect(containerID, payload) {
  168. containerID = containerID || '';
  169. return new Promise((resolve) => {
  170. process.nextTick(() => {
  171. if (containerID) {
  172. if (!_docker_socket) {
  173. _docker_socket = new DockerSocket();
  174. }
  175. _docker_socket.getInspect(containerID.trim(), data => {
  176. try {
  177. resolve({
  178. id: payload.Id,
  179. name: payload.Name,
  180. image: payload.Image,
  181. imageID: payload.ImageID,
  182. command: payload.Command,
  183. created: payload.Created,
  184. started: data.State && data.State.StartedAt ? Math.round(new Date(data.State.StartedAt).getTime() / 1000) : 0,
  185. finished: data.State && data.State.FinishedAt && !data.State.FinishedAt.startsWith('0001-01-01') ? Math.round(new Date(data.State.FinishedAt).getTime() / 1000) : 0,
  186. createdAt: data.Created ? data.Created : '',
  187. startedAt: data.State && data.State.StartedAt ? data.State.StartedAt : '',
  188. finishedAt: data.State && data.State.FinishedAt && !data.State.FinishedAt.startsWith('0001-01-01') ? data.State.FinishedAt : '',
  189. state: payload.State,
  190. restartCount: data.RestartCount || 0,
  191. platform: data.Platform || '',
  192. driver: data.Driver || '',
  193. ports: payload.Ports,
  194. mounts: payload.Mounts,
  195. // hostconfig: payload.HostConfig,
  196. // network: payload.NetworkSettings
  197. });
  198. } catch (err) {
  199. resolve();
  200. }
  201. });
  202. } else {
  203. resolve();
  204. }
  205. });
  206. });
  207. }
  208. exports.dockerContainers = dockerContainers;
  209. // --------------------------
  210. // helper functions for calculation of docker stats
  211. function docker_calcCPUPercent(cpu_stats, precpu_stats) {
  212. /**
  213. * @namespace
  214. * @property {object} cpu_usage
  215. * @property {number} cpu_usage.total_usage
  216. * @property {number} system_cpu_usage
  217. * @property {object} cpu_usage
  218. * @property {Array} cpu_usage.percpu_usage
  219. */
  220. if (!_windows) {
  221. let cpuPercent = 0.0;
  222. // calculate the change for the cpu usage of the container in between readings
  223. let cpuDelta = cpu_stats.cpu_usage.total_usage - precpu_stats.cpu_usage.total_usage;
  224. // calculate the change for the entire system between readings
  225. let systemDelta = cpu_stats.system_cpu_usage - precpu_stats.system_cpu_usage;
  226. if (systemDelta > 0.0 && cpuDelta > 0.0) {
  227. // calculate the change for the cpu usage of the container in between readings
  228. cpuPercent = (cpuDelta / systemDelta) * cpu_stats.cpu_usage.percpu_usage.length * 100.0;
  229. }
  230. return cpuPercent;
  231. } else {
  232. let nanoSecNow = util.nanoSeconds();
  233. let cpuPercent = 0.0;
  234. if (_docker_last_read > 0) {
  235. let possIntervals = (nanoSecNow - _docker_last_read); // / 100 * os.cpus().length;
  236. let intervalsUsed = cpu_stats.cpu_usage.total_usage - precpu_stats.cpu_usage.total_usage;
  237. if (possIntervals > 0) {
  238. cpuPercent = 100.0 * intervalsUsed / possIntervals;
  239. }
  240. }
  241. _docker_last_read = nanoSecNow;
  242. return cpuPercent;
  243. }
  244. }
  245. function docker_calcNetworkIO(networks) {
  246. let rx;
  247. let tx;
  248. for (let key in networks) {
  249. // skip loop if the property is from prototype
  250. if (!{}.hasOwnProperty.call(networks, key)) continue;
  251. /**
  252. * @namespace
  253. * @property {number} rx_bytes
  254. * @property {number} tx_bytes
  255. */
  256. let obj = networks[key];
  257. rx = +obj.rx_bytes;
  258. tx = +obj.tx_bytes;
  259. }
  260. return {
  261. rx: rx,
  262. tx: tx
  263. };
  264. }
  265. function docker_calcBlockIO(blkio_stats) {
  266. let result = {
  267. r: 0,
  268. w: 0
  269. };
  270. /**
  271. * @namespace
  272. * @property {Array} io_service_bytes_recursive
  273. */
  274. 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) {
  275. blkio_stats.io_service_bytes_recursive.forEach(function (element) {
  276. /**
  277. * @namespace
  278. * @property {string} op
  279. * @property {number} value
  280. */
  281. if (element.op && element.op.toLowerCase() === 'read' && element.value) {
  282. result.r += element.value;
  283. }
  284. if (element.op && element.op.toLowerCase() === 'write' && element.value) {
  285. result.w += element.value;
  286. }
  287. });
  288. }
  289. return result;
  290. }
  291. function dockerContainerStats(containerIDs, callback) {
  292. let containerArray = [];
  293. // fallback - if only callback is given
  294. if (util.isFunction(containerIDs) && !callback) {
  295. callback = containerIDs;
  296. containerArray = ['*'];
  297. } else {
  298. containerIDs = containerIDs || '*';
  299. containerIDs = containerIDs.trim().toLowerCase().replace(/,+/g, '|');
  300. containerArray = containerIDs.split('|');
  301. }
  302. return new Promise((resolve) => {
  303. process.nextTick(() => {
  304. const result = [];
  305. const workload = [];
  306. if (containerArray.length && containerArray[0].trim() === '*') {
  307. containerArray = [];
  308. dockerContainers().then(allContainers => {
  309. for (let container of allContainers) {
  310. containerArray.push(container.id);
  311. }
  312. dockerContainerStats(containerArray.join(',')).then(result => {
  313. if (callback) { callback(result); }
  314. resolve(result);
  315. });
  316. });
  317. } else {
  318. for (let containerID of containerArray) {
  319. workload.push(dockerContainerStatsSingle(containerID.trim()));
  320. }
  321. if (workload.length) {
  322. Promise.all(
  323. workload
  324. ).then(data => {
  325. if (callback) { callback(data); }
  326. resolve(data);
  327. });
  328. } else {
  329. if (callback) { callback(result); }
  330. resolve(result);
  331. }
  332. }
  333. });
  334. });
  335. }
  336. // --------------------------
  337. // container stats (for one container)
  338. function dockerContainerStatsSingle(containerID) {
  339. containerID = containerID || '';
  340. let result = {
  341. id: containerID,
  342. mem_usage: 0,
  343. mem_limit: 0,
  344. mem_percent: 0,
  345. cpu_percent: 0,
  346. pids: 0,
  347. netIO: {
  348. rx: 0,
  349. wx: 0
  350. },
  351. blockIO: {
  352. r: 0,
  353. w: 0
  354. }
  355. };
  356. return new Promise((resolve) => {
  357. process.nextTick(() => {
  358. if (containerID) {
  359. if (!_docker_socket) {
  360. _docker_socket = new DockerSocket();
  361. }
  362. _docker_socket.getInspect(containerID, dataInspect => {
  363. try {
  364. _docker_socket.getStats(containerID, data => {
  365. try {
  366. let stats = data;
  367. /**
  368. * @namespace
  369. * @property {Object} memory_stats
  370. * @property {number} memory_stats.usage
  371. * @property {number} memory_stats.limit
  372. * @property {Object} cpu_stats
  373. * @property {Object} pids_stats
  374. * @property {number} pids_stats.current
  375. * @property {Object} networks
  376. * @property {Object} blkio_stats
  377. */
  378. if (!stats.message) {
  379. result.mem_usage = (stats.memory_stats && stats.memory_stats.usage ? stats.memory_stats.usage : 0);
  380. result.mem_limit = (stats.memory_stats && stats.memory_stats.limit ? stats.memory_stats.limit : 0);
  381. 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);
  382. result.cpu_percent = (stats.cpu_stats && stats.precpu_stats ? docker_calcCPUPercent(stats.cpu_stats, stats.precpu_stats) : 0);
  383. result.pids = (stats.pids_stats && stats.pids_stats.current ? stats.pids_stats.current : 0);
  384. result.restartCount = (dataInspect.RestartCount ? dataInspect.RestartCount : 0);
  385. if (stats.networks) result.netIO = docker_calcNetworkIO(stats.networks);
  386. if (stats.blkio_stats) result.blockIO = docker_calcBlockIO(stats.blkio_stats);
  387. result.cpu_stats = (stats.cpu_stats ? stats.cpu_stats : {});
  388. result.precpu_stats = (stats.precpu_stats ? stats.precpu_stats : {});
  389. result.memory_stats = (stats.memory_stats ? stats.memory_stats : {});
  390. result.networks = (stats.networks ? stats.networks : {});
  391. }
  392. } catch (err) {
  393. util.noop();
  394. }
  395. // }
  396. resolve(result);
  397. });
  398. } catch (err) {
  399. util.noop();
  400. }
  401. });
  402. } else {
  403. resolve(result);
  404. }
  405. });
  406. });
  407. }
  408. exports.dockerContainerStats = dockerContainerStats;
  409. // --------------------------
  410. // container processes (for one container)
  411. function dockerContainerProcesses(containerID, callback) {
  412. containerID = containerID || '';
  413. let result = [];
  414. return new Promise((resolve) => {
  415. process.nextTick(() => {
  416. if (containerID) {
  417. if (!_docker_socket) {
  418. _docker_socket = new DockerSocket();
  419. }
  420. _docker_socket.getProcesses(containerID, data => {
  421. /**
  422. * @namespace
  423. * @property {Array} Titles
  424. * @property {Array} Processes
  425. **/
  426. try {
  427. if (data && data.Titles && data.Processes) {
  428. let titles = data.Titles.map(function (value) {
  429. return value.toUpperCase();
  430. });
  431. let pos_pid = titles.indexOf('PID');
  432. let pos_ppid = titles.indexOf('PPID');
  433. let pos_pgid = titles.indexOf('PGID');
  434. let pos_vsz = titles.indexOf('VSZ');
  435. let pos_time = titles.indexOf('TIME');
  436. let pos_elapsed = titles.indexOf('ELAPSED');
  437. let pos_ni = titles.indexOf('NI');
  438. let pos_ruser = titles.indexOf('RUSER');
  439. let pos_user = titles.indexOf('USER');
  440. let pos_rgroup = titles.indexOf('RGROUP');
  441. let pos_group = titles.indexOf('GROUP');
  442. let pos_stat = titles.indexOf('STAT');
  443. let pos_rss = titles.indexOf('RSS');
  444. let pos_command = titles.indexOf('COMMAND');
  445. data.Processes.forEach(process => {
  446. result.push({
  447. pid_host: (pos_pid >= 0 ? process[pos_pid] : ''),
  448. ppid: (pos_ppid >= 0 ? process[pos_ppid] : ''),
  449. pgid: (pos_pgid >= 0 ? process[pos_pgid] : ''),
  450. user: (pos_user >= 0 ? process[pos_user] : ''),
  451. ruser: (pos_ruser >= 0 ? process[pos_ruser] : ''),
  452. group: (pos_group >= 0 ? process[pos_group] : ''),
  453. rgroup: (pos_rgroup >= 0 ? process[pos_rgroup] : ''),
  454. stat: (pos_stat >= 0 ? process[pos_stat] : ''),
  455. time: (pos_time >= 0 ? process[pos_time] : ''),
  456. elapsed: (pos_elapsed >= 0 ? process[pos_elapsed] : ''),
  457. nice: (pos_ni >= 0 ? process[pos_ni] : ''),
  458. rss: (pos_rss >= 0 ? process[pos_rss] : ''),
  459. vsz: (pos_vsz >= 0 ? process[pos_vsz] : ''),
  460. command: (pos_command >= 0 ? process[pos_command] : '')
  461. });
  462. });
  463. }
  464. } catch (err) {
  465. util.noop();
  466. }
  467. if (callback) { callback(result); }
  468. resolve(result);
  469. });
  470. } else {
  471. if (callback) { callback(result); }
  472. resolve(result);
  473. }
  474. });
  475. });
  476. }
  477. exports.dockerContainerProcesses = dockerContainerProcesses;
  478. function dockerAll(callback) {
  479. return new Promise((resolve) => {
  480. process.nextTick(() => {
  481. dockerContainers(true).then(result => {
  482. if (result && Object.prototype.toString.call(result) === '[object Array]' && result.length > 0) {
  483. let l = result.length;
  484. result.forEach(function (element) {
  485. dockerContainerStats(element.id).then(res => {
  486. // include stats in array
  487. element.mem_usage = res[0].mem_usage;
  488. element.mem_limit = res[0].mem_limit;
  489. element.mem_percent = res[0].mem_percent;
  490. element.cpu_percent = res[0].cpu_percent;
  491. element.pids = res[0].pids;
  492. element.netIO = res[0].netIO;
  493. element.blockIO = res[0].blockIO;
  494. element.cpu_stats = res[0].cpu_stats;
  495. element.precpu_stats = res[0].precpu_stats;
  496. element.memory_stats = res[0].memory_stats;
  497. element.networks = res[0].networks;
  498. dockerContainerProcesses(element.id).then(processes => {
  499. element.processes = processes;
  500. l -= 1;
  501. if (l === 0) {
  502. if (callback) { callback(result); }
  503. resolve(result);
  504. }
  505. });
  506. // all done??
  507. });
  508. });
  509. } else {
  510. if (callback) { callback(result); }
  511. resolve(result);
  512. }
  513. });
  514. });
  515. });
  516. }
  517. exports.dockerAll = dockerAll;