_xpack_info.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import { createHash } from 'crypto';
  2. import moment from 'moment';
  3. import { get, set, includes, forIn } from 'lodash';
  4. import Poller from './poller';
  5. import { LICENSE_EXPIRY_SOON_DURATION_IN_DAYS } from './constants';
  6. export default function _xpackInfo(server, pollFrequencyInMillis, clusterSource = 'data') {
  7. if(!pollFrequencyInMillis) {
  8. const config = server.config();
  9. pollFrequencyInMillis = config.get('xpack.xpack_main.xpack_api_polling_frequency_millis');
  10. }
  11. let _cachedResponseFromElasticsearch;
  12. const _licenseCheckResultsGenerators = {};
  13. const _licenseCheckResults = {};
  14. let _cachedXPackInfoJSON;
  15. let _cachedXPackInfoJSONSignature;
  16. const poller = new Poller({
  17. functionToPoll: _callElasticsearchXPackAPI,
  18. successFunction: _handleResponseFromElasticsearch,
  19. errorFunction: _handleErrorFromElasticsearch,
  20. pollFrequencyInMillis,
  21. continuePollingOnError: true
  22. });
  23. const xpackInfoObject = {
  24. license: {
  25. getUid: function () {
  26. return get(_cachedResponseFromElasticsearch, 'license.uid');
  27. },
  28. isActive: function () {
  29. return true;
  30. //return get(_cachedResponseFromElasticsearch, 'license.status') === 'active';
  31. },
  32. expiresSoon: function () {
  33. //const expiryDateMillis = get(_cachedResponseFromElasticsearch, 'license.expiry_date_in_millis');
  34. //const expirySoonDate = moment.utc(expiryDateMillis).subtract(moment.duration(LICENSE_EXPIRY_SOON_DURATION_IN_DAYS, 'days'));
  35. //return moment.utc().isAfter(expirySoonDate);
  36. return false;
  37. },
  38. getExpiryDateInMillis: function () {
  39. return get(_cachedResponseFromElasticsearch, 'license.expiry_date_in_millis');
  40. },
  41. isOneOf: function (candidateLicenses) {
  42. if (!Array.isArray(candidateLicenses)) {
  43. candidateLicenses = [ candidateLicenses ];
  44. }
  45. return includes(candidateLicenses, get(_cachedResponseFromElasticsearch, 'license.mode'));
  46. },
  47. getType: function () {
  48. return get(_cachedResponseFromElasticsearch, 'license.type');
  49. }
  50. },
  51. feature: function (feature) {
  52. return {
  53. isAvailable: function () {
  54. //return get(_cachedResponseFromElasticsearch, 'features.' + feature + '.available');
  55. return true;
  56. },
  57. isEnabled: function () {
  58. //return get(_cachedResponseFromElasticsearch, 'features.' + feature + '.enabled');
  59. return true;
  60. },
  61. registerLicenseCheckResultsGenerator: function (generator) {
  62. _licenseCheckResultsGenerators[feature] = generator;
  63. _updateXPackInfoJSON();
  64. },
  65. getLicenseCheckResults: function () {
  66. return _licenseCheckResults[feature];
  67. }
  68. };
  69. },
  70. isAvailable: function () {
  71. //return !!_cachedResponseFromElasticsearch && !!get(_cachedResponseFromElasticsearch, 'license');
  72. return true;
  73. },
  74. getSignature: function () {
  75. return _cachedXPackInfoJSONSignature;
  76. },
  77. refreshNow: function () {
  78. const self = this;
  79. return _callElasticsearchXPackAPI()
  80. .then(_handleResponseFromElasticsearch)
  81. .catch(_handleErrorFromElasticsearch)
  82. .then(() => self);
  83. },
  84. stopPolling: function () {
  85. // This method exists primarily for unit testing
  86. poller.stop();
  87. },
  88. toJSON: function () {
  89. return _cachedXPackInfoJSON;
  90. }
  91. };
  92. const cluster = server.plugins.elasticsearch.getCluster(clusterSource);
  93. function _callElasticsearchXPackAPI() {
  94. server.log([ 'license', 'debug', 'xpack' ], 'Calling Elasticsearch _xpack API');
  95. return cluster.callWithInternalUser('transport.request', {
  96. method: 'GET',
  97. path: '/_xpack'
  98. });
  99. };
  100. function _updateXPackInfoJSON() {
  101. const json = {};
  102. // Set response elements common to all features
  103. set(json, 'license.type', xpackInfoObject.license.getType());
  104. set(json, 'license.isActive', xpackInfoObject.license.isActive());
  105. set(json, 'license.expiryDateInMillis', xpackInfoObject.license.getExpiryDateInMillis());
  106. // Set response elements specific to each feature. To do this,
  107. // call the license check results generator for each feature, passing them
  108. // the xpack info object
  109. forIn(_licenseCheckResultsGenerators, (generator, feature) => {
  110. _licenseCheckResults[feature] = generator(xpackInfoObject); // return value expected to be a dictionary object
  111. });
  112. set(json, 'features', _licenseCheckResults);
  113. _cachedXPackInfoJSON = json;
  114. _cachedXPackInfoJSONSignature = createHash('md5')
  115. .update(JSON.stringify(json))
  116. .digest('hex');
  117. }
  118. function _hasLicenseInfoFromElasticsearchChanged(response) {
  119. const cachedResponse = _cachedResponseFromElasticsearch;
  120. return (get(response, 'license.mode') !== get(cachedResponse, 'license.mode')
  121. || get(response, 'license.status') !== get(cachedResponse, 'license.status')
  122. || get(response, 'license.expiry_date_in_millis') !== get(cachedResponse, 'license.expiry_date_in_millis'));
  123. }
  124. function _getLicenseInfoForLog(response) {
  125. const mode = get(response, 'license.mode');
  126. const status = get(response, 'license.status');
  127. const expiryDateInMillis = get(response, 'license.expiry_date_in_millis');
  128. return [
  129. 'mode: ' + mode,
  130. 'status: ' + status,
  131. 'expiry date: ' + moment(expiryDateInMillis, 'x').format()
  132. ].join(' | ');
  133. }
  134. function _handleResponseFromElasticsearch(response) {
  135. if (_hasLicenseInfoFromElasticsearchChanged(response)) {
  136. let changed = '';
  137. if (_cachedResponseFromElasticsearch) {
  138. changed = 'changed ';
  139. }
  140. const licenseInfo = _getLicenseInfoForLog(response);
  141. const logMessage = `Imported ${changed}license information from Elasticsearch for [${clusterSource}] cluster: ${licenseInfo}`;
  142. server.log([ 'license', 'info', 'xpack' ], logMessage);
  143. }
  144. _cachedResponseFromElasticsearch = response;
  145. _updateXPackInfoJSON();
  146. }
  147. function _handleErrorFromElasticsearch(error) {
  148. server.log([ 'license', 'warning', 'xpack' ], 'License information could not be obtained from Elasticsearch. ' + error);
  149. _cachedResponseFromElasticsearch = null;
  150. _updateXPackInfoJSON();
  151. // allow tests to shutdown
  152. error.info = xpackInfoObject;
  153. throw error;
  154. }
  155. // Start polling for changes
  156. return poller.start()
  157. .then(() => xpackInfoObject);
  158. }