nucleus 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +1 -0
  3. data/.gitignore +19 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +44 -0
  6. data/.travis.yml +21 -0
  7. data/CHANGELOG.md +19 -0
  8. data/CONTRIBUTING.md +13 -0
  9. data/Gemfile +16 -0
  10. data/Guardfile +22 -0
  11. data/LICENSE +21 -0
  12. data/README.md +675 -0
  13. data/Rakefile +137 -0
  14. data/bin/nucleus +91 -0
  15. data/bin/nucleus.bat +1 -0
  16. data/config.ru +18 -0
  17. data/config/adapters/cloud_control.yml +32 -0
  18. data/config/adapters/cloud_foundry_v2.yml +61 -0
  19. data/config/adapters/heroku.yml +13 -0
  20. data/config/adapters/openshift_v2.yml +20 -0
  21. data/config/nucleus_config.rb +47 -0
  22. data/lib/nucleus.rb +13 -0
  23. data/lib/nucleus/adapter_resolver.rb +115 -0
  24. data/lib/nucleus/adapters/base_adapter.rb +109 -0
  25. data/lib/nucleus/adapters/buildpack_translator.rb +79 -0
  26. data/lib/nucleus/adapters/v1/cloud_control/application.rb +108 -0
  27. data/lib/nucleus/adapters/v1/cloud_control/authentication.rb +27 -0
  28. data/lib/nucleus/adapters/v1/cloud_control/buildpacks.rb +23 -0
  29. data/lib/nucleus/adapters/v1/cloud_control/cloud_control.rb +153 -0
  30. data/lib/nucleus/adapters/v1/cloud_control/data.rb +76 -0
  31. data/lib/nucleus/adapters/v1/cloud_control/domains.rb +68 -0
  32. data/lib/nucleus/adapters/v1/cloud_control/lifecycle.rb +27 -0
  33. data/lib/nucleus/adapters/v1/cloud_control/log_poller.rb +71 -0
  34. data/lib/nucleus/adapters/v1/cloud_control/logs.rb +103 -0
  35. data/lib/nucleus/adapters/v1/cloud_control/regions.rb +32 -0
  36. data/lib/nucleus/adapters/v1/cloud_control/scaling.rb +17 -0
  37. data/lib/nucleus/adapters/v1/cloud_control/semantic_errors.rb +31 -0
  38. data/lib/nucleus/adapters/v1/cloud_control/services.rb +162 -0
  39. data/lib/nucleus/adapters/v1/cloud_control/token.rb +17 -0
  40. data/lib/nucleus/adapters/v1/cloud_control/vars.rb +88 -0
  41. data/lib/nucleus/adapters/v1/cloud_foundry_v2/app_states.rb +28 -0
  42. data/lib/nucleus/adapters/v1/cloud_foundry_v2/application.rb +111 -0
  43. data/lib/nucleus/adapters/v1/cloud_foundry_v2/authentication.rb +17 -0
  44. data/lib/nucleus/adapters/v1/cloud_foundry_v2/buildpacks.rb +23 -0
  45. data/lib/nucleus/adapters/v1/cloud_foundry_v2/cloud_foundry_v2.rb +141 -0
  46. data/lib/nucleus/adapters/v1/cloud_foundry_v2/data.rb +97 -0
  47. data/lib/nucleus/adapters/v1/cloud_foundry_v2/domains.rb +149 -0
  48. data/lib/nucleus/adapters/v1/cloud_foundry_v2/lifecycle.rb +41 -0
  49. data/lib/nucleus/adapters/v1/cloud_foundry_v2/logs.rb +303 -0
  50. data/lib/nucleus/adapters/v1/cloud_foundry_v2/regions.rb +33 -0
  51. data/lib/nucleus/adapters/v1/cloud_foundry_v2/scaling.rb +15 -0
  52. data/lib/nucleus/adapters/v1/cloud_foundry_v2/semantic_errors.rb +27 -0
  53. data/lib/nucleus/adapters/v1/cloud_foundry_v2/services.rb +286 -0
  54. data/lib/nucleus/adapters/v1/cloud_foundry_v2/vars.rb +80 -0
  55. data/lib/nucleus/adapters/v1/heroku/app_states.rb +57 -0
  56. data/lib/nucleus/adapters/v1/heroku/application.rb +93 -0
  57. data/lib/nucleus/adapters/v1/heroku/authentication.rb +27 -0
  58. data/lib/nucleus/adapters/v1/heroku/buildpacks.rb +27 -0
  59. data/lib/nucleus/adapters/v1/heroku/data.rb +78 -0
  60. data/lib/nucleus/adapters/v1/heroku/domains.rb +43 -0
  61. data/lib/nucleus/adapters/v1/heroku/heroku.rb +146 -0
  62. data/lib/nucleus/adapters/v1/heroku/lifecycle.rb +51 -0
  63. data/lib/nucleus/adapters/v1/heroku/logs.rb +108 -0
  64. data/lib/nucleus/adapters/v1/heroku/regions.rb +42 -0
  65. data/lib/nucleus/adapters/v1/heroku/scaling.rb +28 -0
  66. data/lib/nucleus/adapters/v1/heroku/semantic_errors.rb +23 -0
  67. data/lib/nucleus/adapters/v1/heroku/services.rb +168 -0
  68. data/lib/nucleus/adapters/v1/heroku/vars.rb +65 -0
  69. data/lib/nucleus/adapters/v1/openshift_v2/app_states.rb +68 -0
  70. data/lib/nucleus/adapters/v1/openshift_v2/application.rb +108 -0
  71. data/lib/nucleus/adapters/v1/openshift_v2/authentication.rb +21 -0
  72. data/lib/nucleus/adapters/v1/openshift_v2/data.rb +96 -0
  73. data/lib/nucleus/adapters/v1/openshift_v2/domains.rb +37 -0
  74. data/lib/nucleus/adapters/v1/openshift_v2/lifecycle.rb +60 -0
  75. data/lib/nucleus/adapters/v1/openshift_v2/logs.rb +106 -0
  76. data/lib/nucleus/adapters/v1/openshift_v2/openshift_v2.rb +125 -0
  77. data/lib/nucleus/adapters/v1/openshift_v2/regions.rb +58 -0
  78. data/lib/nucleus/adapters/v1/openshift_v2/scaling.rb +39 -0
  79. data/lib/nucleus/adapters/v1/openshift_v2/semantic_errors.rb +40 -0
  80. data/lib/nucleus/adapters/v1/openshift_v2/services.rb +173 -0
  81. data/lib/nucleus/adapters/v1/openshift_v2/vars.rb +49 -0
  82. data/lib/nucleus/adapters/v1/stub_adapter.rb +464 -0
  83. data/lib/nucleus/core/adapter_authentication_inductor.rb +62 -0
  84. data/lib/nucleus/core/adapter_extensions/auth/auth_client.rb +44 -0
  85. data/lib/nucleus/core/adapter_extensions/auth/authentication_retry_wrapper.rb +79 -0
  86. data/lib/nucleus/core/adapter_extensions/auth/expiring_token_auth_client.rb +53 -0
  87. data/lib/nucleus/core/adapter_extensions/auth/http_basic_auth_client.rb +37 -0
  88. data/lib/nucleus/core/adapter_extensions/auth/o_auth2_auth_client.rb +95 -0
  89. data/lib/nucleus/core/adapter_extensions/auth/token_auth_client.rb +36 -0
  90. data/lib/nucleus/core/adapter_extensions/http_client.rb +177 -0
  91. data/lib/nucleus/core/adapter_extensions/http_tail_client.rb +26 -0
  92. data/lib/nucleus/core/adapter_extensions/tail_stopper.rb +25 -0
  93. data/lib/nucleus/core/common/errors/ambiguous_adapter_error.rb +7 -0
  94. data/lib/nucleus/core/common/errors/file_existence_error.rb +7 -0
  95. data/lib/nucleus/core/common/errors/startup_error.rb +12 -0
  96. data/lib/nucleus/core/common/exit_codes.rb +25 -0
  97. data/lib/nucleus/core/common/files/application_repo_sanitizer.rb +52 -0
  98. data/lib/nucleus/core/common/files/archive_extractor.rb +112 -0
  99. data/lib/nucleus/core/common/files/archiver.rb +91 -0
  100. data/lib/nucleus/core/common/link_generator.rb +46 -0
  101. data/lib/nucleus/core/common/logging/logging.rb +52 -0
  102. data/lib/nucleus/core/common/logging/multi_logger.rb +59 -0
  103. data/lib/nucleus/core/common/logging/request_log_formatter.rb +48 -0
  104. data/lib/nucleus/core/common/ssh_handler.rb +108 -0
  105. data/lib/nucleus/core/common/stream_callback.rb +27 -0
  106. data/lib/nucleus/core/common/thread_config_accessor.rb +85 -0
  107. data/lib/nucleus/core/common/url_converter.rb +28 -0
  108. data/lib/nucleus/core/enums/application_states.rb +26 -0
  109. data/lib/nucleus/core/enums/logfile_types.rb +28 -0
  110. data/lib/nucleus/core/error_messages.rb +127 -0
  111. data/lib/nucleus/core/errors/adapter_error.rb +13 -0
  112. data/lib/nucleus/core/errors/adapter_missing_implementation_error.rb +12 -0
  113. data/lib/nucleus/core/errors/adapter_request_error.rb +10 -0
  114. data/lib/nucleus/core/errors/adapter_resource_not_found_error.rb +10 -0
  115. data/lib/nucleus/core/errors/endpoint_authentication_error.rb +10 -0
  116. data/lib/nucleus/core/errors/platform_specific_semantic_error.rb +12 -0
  117. data/lib/nucleus/core/errors/platform_timeout_error.rb +10 -0
  118. data/lib/nucleus/core/errors/platform_unavailable_error.rb +10 -0
  119. data/lib/nucleus/core/errors/semantic_adapter_request_error.rb +19 -0
  120. data/lib/nucleus/core/errors/unknown_adapter_call_error.rb +10 -0
  121. data/lib/nucleus/core/file_handling/archive_converter.rb +29 -0
  122. data/lib/nucleus/core/file_handling/file_manager.rb +64 -0
  123. data/lib/nucleus/core/file_handling/git_deployer.rb +133 -0
  124. data/lib/nucleus/core/file_handling/git_repo_analyzer.rb +23 -0
  125. data/lib/nucleus/core/import/adapter_configuration.rb +53 -0
  126. data/lib/nucleus/core/import/vendor_parser.rb +28 -0
  127. data/lib/nucleus/core/import/version_detector.rb +18 -0
  128. data/lib/nucleus/core/models/abstract_model.rb +29 -0
  129. data/lib/nucleus/core/models/endpoint.rb +30 -0
  130. data/lib/nucleus/core/models/provider.rb +26 -0
  131. data/lib/nucleus/core/models/vendor.rb +22 -0
  132. data/lib/nucleus/ext/kernel.rb +5 -0
  133. data/lib/nucleus/ext/regexp.rb +49 -0
  134. data/lib/nucleus/os.rb +15 -0
  135. data/lib/nucleus/root_dir.rb +13 -0
  136. data/lib/nucleus/scripts/finalize.rb +8 -0
  137. data/lib/nucleus/scripts/initialize.rb +9 -0
  138. data/lib/nucleus/scripts/initialize_config_defaults.rb +26 -0
  139. data/lib/nucleus/scripts/load.rb +17 -0
  140. data/lib/nucleus/scripts/load_dependencies.rb +43 -0
  141. data/lib/nucleus/scripts/setup_config.rb +28 -0
  142. data/lib/nucleus/scripts/shutdown.rb +11 -0
  143. data/lib/nucleus/version.rb +3 -0
  144. data/nucleus.gemspec +88 -0
  145. data/public/robots.txt +2 -0
  146. data/public/swagger-ui/css/reset.css +125 -0
  147. data/public/swagger-ui/css/screen.css +1224 -0
  148. data/public/swagger-ui/images/apple-touch-icon-114x114.png +0 -0
  149. data/public/swagger-ui/images/apple-touch-icon-120x120.png +0 -0
  150. data/public/swagger-ui/images/apple-touch-icon-144x144.png +0 -0
  151. data/public/swagger-ui/images/apple-touch-icon-152x152.png +0 -0
  152. data/public/swagger-ui/images/apple-touch-icon-57x57.png +0 -0
  153. data/public/swagger-ui/images/apple-touch-icon-60x60.png +0 -0
  154. data/public/swagger-ui/images/apple-touch-icon-72x72.png +0 -0
  155. data/public/swagger-ui/images/apple-touch-icon-76x76.png +0 -0
  156. data/public/swagger-ui/images/explorer_icons.png +0 -0
  157. data/public/swagger-ui/images/favicon-128.png +0 -0
  158. data/public/swagger-ui/images/favicon-16x16.png +0 -0
  159. data/public/swagger-ui/images/favicon-196x196.png +0 -0
  160. data/public/swagger-ui/images/favicon-32x32.png +0 -0
  161. data/public/swagger-ui/images/favicon-96x96.png +0 -0
  162. data/public/swagger-ui/images/favicon.ico +0 -0
  163. data/public/swagger-ui/images/logo_small.png +0 -0
  164. data/public/swagger-ui/images/mstile-144x144.png +0 -0
  165. data/public/swagger-ui/images/mstile-150x150.png +0 -0
  166. data/public/swagger-ui/images/mstile-310x150.png +0 -0
  167. data/public/swagger-ui/images/mstile-310x310.png +0 -0
  168. data/public/swagger-ui/images/mstile-70x70.png +0 -0
  169. data/public/swagger-ui/images/pet_store_api.png +0 -0
  170. data/public/swagger-ui/images/throbber.gif +0 -0
  171. data/public/swagger-ui/images/wordnik_api.png +0 -0
  172. data/public/swagger-ui/index.html +107 -0
  173. data/public/swagger-ui/lib/backbone-min.js +38 -0
  174. data/public/swagger-ui/lib/handlebars-1.0.0.js +2278 -0
  175. data/public/swagger-ui/lib/highlight.7.3.pack.js +1 -0
  176. data/public/swagger-ui/lib/jquery-1.8.0.min.js +2 -0
  177. data/public/swagger-ui/lib/jquery.ba-bbq.min.js +18 -0
  178. data/public/swagger-ui/lib/jquery.slideto.min.js +1 -0
  179. data/public/swagger-ui/lib/jquery.wiggle.min.js +8 -0
  180. data/public/swagger-ui/lib/shred.bundle.js +2765 -0
  181. data/public/swagger-ui/lib/shred/content.js +193 -0
  182. data/public/swagger-ui/lib/swagger-oauth.js +211 -0
  183. data/public/swagger-ui/lib/swagger.js +1653 -0
  184. data/public/swagger-ui/lib/underscore-min.js +32 -0
  185. data/public/swagger-ui/o2c.html +15 -0
  186. data/public/swagger-ui/redirect.html +14 -0
  187. data/public/swagger-ui/swagger-ui.js +2324 -0
  188. data/public/swagger-ui/swagger-ui.min.js +1 -0
  189. data/schemas/api.adapter.schema.yml +31 -0
  190. data/schemas/api.requirements.schema.yml +17 -0
  191. data/spec/factories/models.rb +61 -0
  192. data/spec/integration/api/auth_spec.rb +58 -0
  193. data/spec/integration/api/endpoints_spec.rb +167 -0
  194. data/spec/integration/api/errors_spec.rb +47 -0
  195. data/spec/integration/api/providers_spec.rb +157 -0
  196. data/spec/integration/api/swagger_schema_spec.rb +64 -0
  197. data/spec/integration/api/vendors_spec.rb +45 -0
  198. data/spec/integration/integration_spec_helper.rb +27 -0
  199. data/spec/integration/test_data_generator.rb +55 -0
  200. data/spec/nucleus_git_key.pem +51 -0
  201. data/spec/spec_helper.rb +98 -0
  202. data/spec/support/shared_example_request_types.rb +99 -0
  203. data/spec/test_suites.rake +31 -0
  204. data/spec/unit/adapters/archive_converter_spec.rb +25 -0
  205. data/spec/unit/adapters/file_manager_spec.rb +93 -0
  206. data/spec/unit/adapters/git_deployer_spec.rb +262 -0
  207. data/spec/unit/adapters/v1/stub_spec.rb +14 -0
  208. data/spec/unit/common/helpers/auth_helper_spec.rb +73 -0
  209. data/spec/unit/common/oauth2_auth_client_spec.rb +108 -0
  210. data/spec/unit/common/regexp_spec.rb +33 -0
  211. data/spec/unit/common/request_log_formatter_spec.rb +108 -0
  212. data/spec/unit/common/thread_config_accessor_spec.rb +97 -0
  213. data/spec/unit/models/endpoint_spec.rb +83 -0
  214. data/spec/unit/models/provider_spec.rb +102 -0
  215. data/spec/unit/models/vendor_spec.rb +100 -0
  216. data/spec/unit/schemas/adapter_schema_spec.rb +16 -0
  217. data/spec/unit/schemas/adapter_validation_spec.rb +56 -0
  218. data/spec/unit/schemas/requirements_schema_spec.rb +16 -0
  219. data/spec/unit/unit_spec_helper.rb +11 -0
  220. data/tasks/compatibility.rake +113 -0
  221. data/tasks/evaluation.rake +162 -0
  222. data/wiki/adapter_tests.md +99 -0
  223. data/wiki/implement_new_adapter.md +155 -0
  224. metadata +836 -0
@@ -0,0 +1,25 @@
1
+ describe Nucleus::Adapters::ArchiveConverter, memfs: true do
2
+ subject { Nucleus::Adapters::ArchiveConverter }
3
+ let(:zip) { 'zip' }
4
+ let(:tar_gz) { 'tar.gz' }
5
+ let(:input_file) { File.join(Dir.tmpdir, 'archive_converter_spec_input_file.tar.gz') }
6
+ it 'extracts, sanitizes and archives the input file' do
7
+ sanitizer = double(Nucleus::ApplicationRepoSanitizer)
8
+ expect(sanitizer).to receive(:sanitize).once
9
+
10
+ extractor = double(Nucleus::ArchiveExtractor)
11
+ expect(extractor).to receive(:extract).with(input_file, any_args, tar_gz).once
12
+
13
+ archiver = double(Nucleus::Archiver)
14
+ expect(archiver).to receive(:compress).with(any_args, zip).once
15
+
16
+ expect(Nucleus::Archiver).to receive(:new) { archiver }
17
+ expect(Nucleus::ArchiveExtractor).to receive(:new) { extractor }
18
+ expect(Nucleus::ApplicationRepoSanitizer).to receive(:new) { sanitizer }
19
+
20
+ # make sure working files get deleted
21
+ expect(FileUtils).to receive(:rm_rf).once
22
+
23
+ subject.convert(input_file, tar_gz, zip, true)
24
+ end
25
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec/unit/unit_spec_helper'
2
+
3
+ describe Nucleus::Adapters::FileManager, memfs: true do
4
+ let(:dir) { File.join(Dir.tmpdir, 'file_manager_load_file_test_dir') }
5
+ let(:file) { File.join(dir, 'file_manager_load_file_test') }
6
+ let(:contents) { 'Heja BVB, Heja BVB, Heja Heja BVB' }
7
+
8
+ # make sure file is deleted before the next attempt
9
+ before(:each) { FileUtils.rm_rf(dir) }
10
+
11
+ describe '#load_file' do
12
+ before do
13
+ FileUtils.mkdir_p(dir)
14
+ File.open(file, 'wb') { |f| f.write contents }
15
+ @result = Nucleus::Adapters::FileManager.load_file(file)
16
+ end
17
+ it 'returns the actual file contents' do
18
+ expect(@result.read).to eql(contents)
19
+ end
20
+ it 'returns a StringIO object' do
21
+ expect(@result).to be_a(StringIO)
22
+ end
23
+ end
24
+
25
+ describe '#save_file_from_data' do
26
+ let(:io) { StringIO.new(contents) }
27
+ let(:md5) { Digest::MD5.hexdigest(contents) }
28
+ context 'file already exists' do
29
+ context 'and force: false' do
30
+ let(:force) { false }
31
+ before { FileUtils.mkdir_p(dir) }
32
+ it 'fails if the file already exists' do
33
+ # prepare already existing file
34
+ File.open(file, 'wb') { |f| f.write contents }
35
+
36
+ expect do
37
+ Nucleus::Adapters::FileManager.save_file_from_data(file, io, force)
38
+ end.to raise_error(Nucleus::FileExistenceError)
39
+ end
40
+
41
+ it 'fails if the file already exists but content is different than anticipated' do
42
+ # prepare already existing file
43
+ File.open(file, 'wb') { |f| f.write StringIO.new("#{contents} - manipulated") }
44
+
45
+ expect do
46
+ Nucleus::Adapters::FileManager.save_file_from_data(file, io, force, md5)
47
+ end.to raise_error(ArgumentError)
48
+ end
49
+
50
+ it 'succeeds if the file already exists and has the anticipated contents' do
51
+ # prepare already existing file
52
+ File.open(file, 'wb') { |f| f.write contents }
53
+
54
+ # call
55
+ Nucleus::Adapters::FileManager.save_file_from_data(file, io, force, md5)
56
+
57
+ # expect file exists with valid content
58
+ expect(File.exist?(file)).to eql(true)
59
+ expect(File.read(file)).to eql(contents)
60
+ end
61
+ end
62
+
63
+ context 'and force: true' do
64
+ let(:force) { true }
65
+ it 'succeeds' do
66
+ # call
67
+ Nucleus::Adapters::FileManager.save_file_from_data(file, io, force)
68
+ # expect file exists with valid content
69
+ expect(File.exist?(file)).to eql(true)
70
+ expect(File.read(file)).to eql(contents)
71
+ end
72
+ end
73
+ end
74
+
75
+ context 'file does not exist yet' do
76
+ it 'succeeds if the file already exists' do
77
+ # call
78
+ Nucleus::Adapters::FileManager.save_file_from_data(file, io)
79
+ # expect file exists with valid content
80
+ expect(File.exist?(file)).to eql(true)
81
+ expect(File.read(file)).to eql(contents)
82
+ end
83
+
84
+ it 'succeeds' do
85
+ # call
86
+ Nucleus::Adapters::FileManager.save_file_from_data(file, io)
87
+ # expect file exists with valid content
88
+ expect(File.exist?(file)).to eql(true)
89
+ expect(File.read(file)).to eql(contents)
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,262 @@
1
+ require 'spec/unit/unit_spec_helper'
2
+
3
+ describe Nucleus::Adapters::GitDeployer do
4
+ let(:repo_name) { 'my_repository' }
5
+ let(:repo_url) { 'http://repo.example.org' }
6
+ let(:user_email) { 'myuser@example.org' }
7
+ let(:repo) { double(Git::Base) }
8
+ let(:zip) { 'zip' }
9
+ let(:file) { File.join('random', 'file', 'path') }
10
+
11
+ it '#deploy fails early with invalid format' do
12
+ subject = Nucleus::Adapters::GitDeployer.new(repo_name, repo_url, user_email)
13
+ extractor = double(Nucleus::ArchiveExtractor)
14
+ expect(Nucleus::ArchiveExtractor).to receive(:new).with(any_args).once { extractor }
15
+ # we assume format is supported
16
+ expect(extractor).to receive(:supports?).with(kind_of(String)).once { false }
17
+ # call should fail
18
+ expect { subject.deploy(nil, 'xyz') }.to raise_error(Nucleus::Errors::AdapterRequestError)
19
+ end
20
+
21
+ describe 'repository actions' do
22
+ before do
23
+ # every action needs to clone the repository
24
+ expect(Git).to receive(:clone).with(repo_url, repo_name, path: kind_of(String)).once { repo }
25
+ # verify cleanup is performed for each method call
26
+ expect(FileUtils).to receive(:rm_rf).with(File.join(Dir.tmpdir, repo_name)).once
27
+ end
28
+
29
+ describe '#download' do
30
+ let!(:exclude_git) { true }
31
+
32
+ context 'with master branch' do
33
+ subject { Nucleus::Adapters::GitDeployer.new(repo_name, repo_url, user_email) }
34
+ let(:repo_branch) { 'master' }
35
+ it 'returns archived repository' do
36
+ # verify archiver is called to return the response
37
+ archiver = double(Nucleus::Archiver)
38
+ expect(Nucleus::Archiver).to receive(:new).with(exclude_git) { archiver }
39
+ expect(archiver).to receive(:compress).with(kind_of(String), zip).once { 'response' }
40
+
41
+ # execute the call
42
+ response = subject.download(zip, exclude_git)
43
+ expect(response).to eql('response')
44
+ end
45
+ end
46
+
47
+ context 'with nucleus branch' do
48
+ let(:repo_branch) { 'nucleus' }
49
+ subject { Nucleus::Adapters::GitDeployer.new(repo_name, repo_url, user_email, repo_branch) }
50
+ it 'returns archived repository' do
51
+ branch_mock = double(Git::Branch)
52
+ expect(repo).to receive(:branch).with(repo_branch).once { branch_mock }
53
+ expect(repo).to receive(:checkout).with(branch_mock).once { repo }
54
+
55
+ # verify archiver is called to return the response
56
+ archiver = double(Nucleus::Archiver)
57
+ expect(Nucleus::Archiver).to receive(:new).with(exclude_git) { archiver }
58
+ expect(archiver).to receive(:compress).with(kind_of(String), zip).once { 'response' }
59
+
60
+ # execute the call
61
+ response = subject.download(zip, exclude_git)
62
+ expect(response).to eql('response')
63
+ end
64
+
65
+ it 'returns archived repository even if master did not contain any commit' do
66
+ # branch fails for master without commits
67
+ expect(repo).to receive(:branch).with(repo_branch).once { fail StandardError, 'No commit for master' }
68
+ # then we rely on the fallback, checkout new branch
69
+ expect(repo).to receive(:checkout).with(repo_branch, new_branch: true).once { repo }
70
+
71
+ # verify archiver is called to return the response
72
+ archiver = double(Nucleus::Archiver)
73
+ expect(Nucleus::Archiver).to receive(:new).with(exclude_git) { archiver }
74
+ expect(archiver).to receive(:compress).with(kind_of(String), zip).once { 'response' }
75
+
76
+ # execute the call
77
+ response = subject.download(zip, exclude_git)
78
+ expect(response).to eql('response')
79
+ end
80
+ end
81
+ end
82
+
83
+ describe '#trigger_build' do
84
+ # set username and email
85
+ before { expect(repo).to receive(:config).with(kind_of(String), kind_of(String)).twice }
86
+
87
+ it 'succeeds for master branch' do
88
+ subject = Nucleus::Adapters::GitDeployer.new(repo_name, repo_url, user_email)
89
+ expect(repo).to receive(:add).with(all: true).once
90
+ expect(repo).to receive(:commit).with(kind_of(String)).once
91
+ expect(repo).to receive(:repack).with(no_args).once
92
+ expect(repo).to receive(:push).with(kind_of(String), 'master', force: true).once
93
+
94
+ # there shall be the attempt to write a file to the repository
95
+ expect(Nucleus::Adapters::FileManager).to receive(:save_file_from_data).with(any_args).once
96
+ # trigger the method
97
+ subject.trigger_build
98
+ end
99
+
100
+ it 'succeeds for nucleus branch' do
101
+ subject = Nucleus::Adapters::GitDeployer.new(repo_name, repo_url, user_email, 'nucleus')
102
+ expect(repo).to receive(:add).with(all: true).once
103
+ expect(repo).to receive(:commit).with(kind_of(String)).once
104
+ expect(repo).to receive(:repack).with(no_args).once
105
+ expect(repo).to receive(:push).with(kind_of(String), 'nucleus', force: true).once
106
+
107
+ branch_mock = double(Git::Branch)
108
+ expect(repo).to receive(:branch).with('nucleus').once { branch_mock }
109
+ expect(repo).to receive(:checkout).with(branch_mock).once { repo }
110
+
111
+ # there shall be the attempt to write a file to the repository
112
+ expect(Nucleus::Adapters::FileManager).to receive(:save_file_from_data)
113
+ # trigger the method
114
+ subject.trigger_build
115
+ end
116
+ end
117
+
118
+ describe '#deploy', memfs: true do
119
+ before { expect(repo).to receive(:config).with(kind_of(String), kind_of(String)).twice }
120
+ context 'with master branch' do
121
+ let(:repo_branch) { 'master' }
122
+ subject { Nucleus::Adapters::GitDeployer.new(repo_name, repo_url, user_email) }
123
+
124
+ describe 'fails' do
125
+ it 'if no files were extracted' do
126
+ extractor = double(Nucleus::ArchiveExtractor)
127
+ expect(Nucleus::ArchiveExtractor).to receive(:new).once { extractor }
128
+ # we assume format is supported
129
+ expect(extractor).to receive(:supports?).with(kind_of(String)).once { true }
130
+ # assumes number of extracted files
131
+ expect(extractor).to receive(:extract).with(file, kind_of(String), zip) { 0 }
132
+
133
+ # write files to disk, as the extractor would do
134
+ repo_dir = File.join(Dir.tmpdir, repo_name)
135
+ git_dir = File.join(repo_dir, '.git')
136
+ FileUtils.mkdir_p(git_dir)
137
+ FileUtils.touch(File.join(git_dir, 'some_git_file.txt'))
138
+
139
+ # make sure files actually exist
140
+ expect(File.file?(File.join(git_dir, 'some_git_file.txt'))).to eql(true)
141
+ expect(File.directory?(git_dir)).to eql(true)
142
+
143
+ # call should fail
144
+ expect { subject.deploy(file, zip) }.to raise_error(Nucleus::Errors::AdapterRequestError)
145
+ end
146
+ end
147
+
148
+ it 'succeeds' do
149
+ expect(repo).to receive(:add).with(all: true).once
150
+ expect(repo).to receive(:commit).with(kind_of(String)).once
151
+ expect(repo).to receive(:repack).with(no_args).once
152
+ expect(repo).to receive(:push).with(kind_of(String), repo_branch, force: true).once
153
+
154
+ extractor = double(Nucleus::ArchiveExtractor)
155
+ expect(Nucleus::ArchiveExtractor).to receive(:new).once { extractor }
156
+ # we assume format is supported
157
+ expect(extractor).to receive(:supports?).with(kind_of(String)).once { true }
158
+ # assumes number of extracted files
159
+ expect(extractor).to receive(:extract).with(file, kind_of(String), zip) { 9 }
160
+
161
+ # write files to disk, as the extractor would do
162
+ repo_dir = File.join(Dir.tmpdir, repo_name)
163
+ git_dir = File.join(repo_dir, '.git')
164
+ other_dir = File.join(repo_dir, 'another_dir')
165
+ FileUtils.mkdir_p(git_dir)
166
+ FileUtils.mkdir_p(other_dir)
167
+ FileUtils.touch(File.join(repo_dir, 'file_1.txt'))
168
+ FileUtils.touch(File.join(other_dir, 'file_2.txt'))
169
+ FileUtils.touch(File.join(git_dir, 'some_git_file.txt'))
170
+
171
+ # make sure files actually exist
172
+ expect(File.file?(File.join(repo_dir, 'file_1.txt'))).to eql(true)
173
+ expect(File.file?(File.join(other_dir, 'file_2.txt'))).to eql(true)
174
+ expect(File.file?(File.join(git_dir, 'some_git_file.txt'))).to eql(true)
175
+ expect(File.directory?(git_dir)).to eql(true)
176
+ expect(File.directory?(other_dir)).to eql(true)
177
+
178
+ # expect(Find).to receive(:find).with(kind_of(String)).once
179
+ expect(FileUtils).to receive(:rm_rf).with(other_dir).once.and_call_original
180
+ expect(FileUtils).to receive(:rm_f).with(File.join(repo_dir, 'file_1.txt')).once.and_call_original
181
+
182
+ sanitizer = double(Nucleus::ApplicationRepoSanitizer)
183
+ expect(Nucleus::ApplicationRepoSanitizer).to receive(:new).once { sanitizer }
184
+ # we assume format is supported
185
+ expect(sanitizer).to receive(:sanitize).with(kind_of(String)).once
186
+
187
+ # call should succeed
188
+ subject.deploy(file, zip)
189
+
190
+ # normal files should be deleted
191
+ expect(File.exist?(File.join(repo_dir, 'file_1.txt'))).to eql(false)
192
+ expect(File.exist?(File.join(other_dir, 'file_2.txt'))).to eql(false)
193
+ expect(File.exist?(other_dir)).to eql(false)
194
+
195
+ # whereas git files should still exist
196
+ expect(File.exist?(File.join(git_dir, 'some_git_file.txt'))).to eql(true)
197
+ expect(File.exist?(git_dir)).to eql(true)
198
+ end
199
+ end
200
+
201
+ context 'with nucleus branch' do
202
+ let(:repo_branch) { 'nucleus' }
203
+ subject { Nucleus::Adapters::GitDeployer.new(repo_name, repo_url, user_email, repo_branch) }
204
+ it 'succeeds' do
205
+ expect(repo).to receive(:add).with(all: true).once
206
+ expect(repo).to receive(:commit).with(kind_of(String)).once
207
+ expect(repo).to receive(:repack).with(no_args).once
208
+ expect(repo).to receive(:push).with(kind_of(String), repo_branch, force: true).once
209
+
210
+ branch_mock = double(Git::Branch)
211
+ expect(repo).to receive(:branch).with(repo_branch).once { branch_mock }
212
+ expect(repo).to receive(:checkout).with(branch_mock).once { repo }
213
+
214
+ extractor = double(Nucleus::ArchiveExtractor)
215
+ expect(Nucleus::ArchiveExtractor).to receive(:new).once { extractor }
216
+ # we assume format is supported
217
+ expect(extractor).to receive(:supports?).with(kind_of(String)).once { true }
218
+ # assumes number of extracted files
219
+ expect(extractor).to receive(:extract).with(file, kind_of(String), zip) { 9 }
220
+
221
+ # write files to disk, as the extractor would do
222
+ repo_dir = File.join(Dir.tmpdir, repo_name)
223
+ git_dir = File.join(repo_dir, '.git')
224
+ other_dir = File.join(repo_dir, 'another_dir')
225
+ FileUtils.mkdir_p(git_dir)
226
+ FileUtils.mkdir_p(other_dir)
227
+ FileUtils.touch(File.join(repo_dir, 'file_1.txt'))
228
+ FileUtils.touch(File.join(other_dir, 'file_2.txt'))
229
+ FileUtils.touch(File.join(git_dir, 'some_git_file.txt'))
230
+
231
+ # make sure files actually exist
232
+ expect(File.file?(File.join(repo_dir, 'file_1.txt'))).to eql(true)
233
+ expect(File.file?(File.join(other_dir, 'file_2.txt'))).to eql(true)
234
+ expect(File.file?(File.join(git_dir, 'some_git_file.txt'))).to eql(true)
235
+ expect(File.directory?(git_dir)).to eql(true)
236
+ expect(File.directory?(other_dir)).to eql(true)
237
+
238
+ # expect(Find).to receive(:find).with(kind_of(String)).once
239
+ expect(FileUtils).to receive(:rm_rf).with(other_dir).once.and_call_original
240
+ expect(FileUtils).to receive(:rm_f).with(File.join(repo_dir, 'file_1.txt')).once.and_call_original
241
+
242
+ sanitizer = double(Nucleus::ApplicationRepoSanitizer)
243
+ expect(Nucleus::ApplicationRepoSanitizer).to receive(:new).once { sanitizer }
244
+ # we assume format is supported
245
+ expect(sanitizer).to receive(:sanitize).with(kind_of(String)).once
246
+
247
+ # call should succeed
248
+ subject.deploy(file, zip)
249
+
250
+ # normal files should be deleted
251
+ expect(File.exist?(File.join(repo_dir, 'file_1.txt'))).to eql(false)
252
+ expect(File.exist?(File.join(other_dir, 'file_2.txt'))).to eql(false)
253
+ expect(File.exist?(other_dir)).to eql(false)
254
+
255
+ # whereas git files should still exist
256
+ expect(File.exist?(File.join(git_dir, 'some_git_file.txt'))).to eql(true)
257
+ expect(File.exist?(git_dir)).to eql(true)
258
+ end
259
+ end
260
+ end
261
+ end
262
+ end
@@ -0,0 +1,14 @@
1
+ # Here we test that the Stub adapter always fails if an adapter implementation is missing
2
+ describe Nucleus::Adapters::V1::Stub do
3
+ subject { Nucleus::Adapters::V1::Stub.new('https://example.org') }
4
+ Nucleus::Adapters::V1::Stub.instance_methods(false).each do |method_name|
5
+ it "#{method_name} shall fail with AdapterMissingImplementationError" do
6
+ if subject.method(method_name).arity > 0
7
+ args = Array.new(subject.method(method_name).arity, nil)
8
+ expect { subject.send(method_name, *args) }.to raise_error(Nucleus::Errors::AdapterMissingImplementationError)
9
+ else
10
+ expect { subject.send(method_name) }.to raise_error(Nucleus::Errors::AdapterMissingImplementationError)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ describe Nucleus::Adapters::AuthenticationRetryWrapper do
4
+ let!(:adapter) { double('adapter') }
5
+ let!(:auth_client) { double('auth_client') }
6
+ let!(:calculator) { double('calculator') }
7
+ let!(:user) { 'my fictionary user' }
8
+ let!(:pass) { 'my fictionary password' }
9
+ let!(:fake_env) { { 'HTTP_AUTHORIZATION' => 'Basic ' + ["#{user}:#{pass}"].pack('m*').gsub(/\n/, '') } }
10
+ before do
11
+ cache_key = 'a unique cache key!'
12
+ cache_dao = double(Nucleus::API::DB::CacheDao)
13
+ allow(cache_dao).to receive(:get) do |key|
14
+ key.end_with?('adapter') ? adapter : cache_key
15
+ end
16
+ RequestStore.store[:cache_key] = cache_key
17
+ allow(adapter).to receive(:cache)
18
+ allow(adapter).to receive(:cache_key)
19
+ allow(adapter).to receive(:cached) { auth_client }
20
+ end
21
+
22
+ describe '#with_authentication' do
23
+ context 'when cache is outdated' do
24
+ before do
25
+ counted = 0
26
+ expect = 1
27
+ allow(calculator).to receive(:calc) do
28
+ fail(Nucleus::Errors::EndpointAuthenticationError.new('error', auth_client)) if (counted += 1) <= expect
29
+ 1
30
+ end
31
+ end
32
+
33
+ context 'and refresh was ok' do
34
+ it 'response is returned in repeated call' do
35
+ expect(auth_client).to receive(:refresh).once
36
+ expect(calculator).to receive(:calc).exactly(2).times
37
+ Nucleus::Adapters::AuthenticationRetryWrapper.with_authentication(adapter, fake_env) { calculator.calc }
38
+ end
39
+ end
40
+ context 'and refresh failed' do
41
+ before do
42
+ allow(auth_client).to receive(:refresh).and_raise(
43
+ Nucleus::Errors::EndpointAuthenticationError.new('error', auth_client))
44
+ end
45
+ context 'but authentication succeeded' do
46
+ before { allow(adapter).to receive(:authenticate) { 'authentication result' } }
47
+ it 'response is returned in repeated call after the authentication' do
48
+ expect(auth_client).to receive(:authenticate).once
49
+ expect(calculator).to receive(:calc).exactly(2).times
50
+ Nucleus::Adapters::AuthenticationRetryWrapper.with_authentication(adapter, fake_env) { calculator.calc }
51
+ end
52
+ end
53
+ context 'and authentication failed' do
54
+ it 'finally fails' do
55
+ expect(auth_client).to receive(:authenticate).once.and_raise(Nucleus::Errors::EndpointAuthenticationError,
56
+ 'error')
57
+ expect(calculator).to receive(:calc).exactly(1).times
58
+ expect do
59
+ Nucleus::Adapters::AuthenticationRetryWrapper.with_authentication(adapter, fake_env) { calculator.calc }
60
+ end.to raise_error(Nucleus::Errors::EndpointAuthenticationError)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ describe '#re_authenticate' do
68
+ it 'calls authentication on the adapter' do
69
+ expect(auth_client).to receive(:authenticate).once.with(user, pass)
70
+ Nucleus::Adapters::AuthenticationRetryWrapper.re_authenticate(adapter, fake_env)
71
+ end
72
+ end
73
+ end