s3_setup.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. 'use strict';
  2. module.exports = exports;
  3. const url = require('url');
  4. const fs = require('fs');
  5. const path = require('path');
  6. module.exports.detect = function(opts, config) {
  7. const to = opts.hosted_path;
  8. const uri = url.parse(to);
  9. config.prefix = (!uri.pathname || uri.pathname === '/') ? '' : uri.pathname.replace('/', '');
  10. if (opts.bucket && opts.region) {
  11. config.bucket = opts.bucket;
  12. config.region = opts.region;
  13. config.endpoint = opts.host;
  14. config.s3ForcePathStyle = opts.s3ForcePathStyle;
  15. } else {
  16. const parts = uri.hostname.split('.s3');
  17. const bucket = parts[0];
  18. if (!bucket) {
  19. return;
  20. }
  21. if (!config.bucket) {
  22. config.bucket = bucket;
  23. }
  24. if (!config.region) {
  25. const region = parts[1].slice(1).split('.')[0];
  26. if (region === 'amazonaws') {
  27. config.region = 'us-east-1';
  28. } else {
  29. config.region = region;
  30. }
  31. }
  32. }
  33. };
  34. module.exports.get_s3 = function(config) {
  35. if (process.env.node_pre_gyp_mock_s3) {
  36. // here we're mocking. node_pre_gyp_mock_s3 is the scratch directory
  37. // for the mock code.
  38. const AWSMock = require('mock-aws-s3');
  39. const os = require('os');
  40. AWSMock.config.basePath = `${os.tmpdir()}/mock`;
  41. const s3 = AWSMock.S3();
  42. // wrapped callback maker. fs calls return code of ENOENT but AWS.S3 returns
  43. // NotFound.
  44. const wcb = (fn) => (err, ...args) => {
  45. if (err && err.code === 'ENOENT') {
  46. err.code = 'NotFound';
  47. }
  48. return fn(err, ...args);
  49. };
  50. return {
  51. listObjects(params, callback) {
  52. return s3.listObjects(params, wcb(callback));
  53. },
  54. headObject(params, callback) {
  55. return s3.headObject(params, wcb(callback));
  56. },
  57. deleteObject(params, callback) {
  58. return s3.deleteObject(params, wcb(callback));
  59. },
  60. putObject(params, callback) {
  61. return s3.putObject(params, wcb(callback));
  62. }
  63. };
  64. }
  65. // if not mocking then setup real s3.
  66. const AWS = require('aws-sdk');
  67. AWS.config.update(config);
  68. const s3 = new AWS.S3();
  69. // need to change if additional options need to be specified.
  70. return {
  71. listObjects(params, callback) {
  72. return s3.listObjects(params, callback);
  73. },
  74. headObject(params, callback) {
  75. return s3.headObject(params, callback);
  76. },
  77. deleteObject(params, callback) {
  78. return s3.deleteObject(params, callback);
  79. },
  80. putObject(params, callback) {
  81. return s3.putObject(params, callback);
  82. }
  83. };
  84. };
  85. //
  86. // function to get the mocking control function. if not mocking it returns a no-op.
  87. //
  88. // if mocking it sets up the mock http interceptors that use the mocked s3 file system
  89. // to fulfill reponses.
  90. module.exports.get_mockS3Http = function() {
  91. let mock_s3 = false;
  92. if (!process.env.node_pre_gyp_mock_s3) {
  93. return () => mock_s3;
  94. }
  95. const nock = require('nock');
  96. // the bucket used for testing, as addressed by https.
  97. const host = 'https://mapbox-node-pre-gyp-public-testing-bucket.s3.us-east-1.amazonaws.com';
  98. const mockDir = process.env.node_pre_gyp_mock_s3 + '/mapbox-node-pre-gyp-public-testing-bucket';
  99. // function to setup interceptors. they are "turned off" by setting mock_s3 to false.
  100. const mock_http = () => {
  101. // eslint-disable-next-line no-unused-vars
  102. function get(uri, requestBody) {
  103. const filepath = path.join(mockDir, uri.replace('%2B', '+'));
  104. try {
  105. fs.accessSync(filepath, fs.constants.R_OK);
  106. } catch (e) {
  107. return [404, 'not found\n'];
  108. }
  109. // the mock s3 functions just write to disk, so just read from it.
  110. return [200, fs.createReadStream(filepath)];
  111. }
  112. // eslint-disable-next-line no-unused-vars
  113. return nock(host)
  114. .persist()
  115. .get(() => mock_s3) // mock any uri for s3 when true
  116. .reply(get);
  117. };
  118. // setup interceptors. they check the mock_s3 flag to determine whether to intercept.
  119. mock_http(nock, host, mockDir);
  120. // function to turn matching all requests to s3 on/off.
  121. const mockS3Http = (action) => {
  122. const previous = mock_s3;
  123. if (action === 'off') {
  124. mock_s3 = false;
  125. } else if (action === 'on') {
  126. mock_s3 = true;
  127. } else if (action !== 'get') {
  128. throw new Error(`illegal action for setMockHttp ${action}`);
  129. }
  130. return previous;
  131. };
  132. // call mockS3Http with the argument
  133. // - 'on' - turn it on
  134. // - 'off' - turn it off (used by fetch.test.js so it doesn't interfere with redirects)
  135. // - 'get' - return true or false for 'on' or 'off'
  136. return mockS3Http;
  137. };