PageRenderTime 40ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/test/instance_agent/plugins/codedeploy/hook_executor_test.rb

https://gitlab.com/vectorci/aws-codedeploy-agent
Ruby | 326 lines | 311 code | 15 blank | 0 comment | 0 complexity | d2cd04c8551795f90cdee0656615d4ac MD5 | raw file
  1. require 'test_helper'
  2. require 'stringio'
  3. require 'fileutils'
  4. class HookExecutorTest < InstanceAgentTestCase
  5. include InstanceAgent::Plugins::CodeDeployPlugin
  6. def create_full_hook_executor
  7. HookExecutor.new ({:lifecycle_event => @lifecycle_event,
  8. :application_name => @application_name,
  9. :deployment_id => @deployment_id,
  10. :deployment_group_name => @deployment_group_name,
  11. :deployment_group_id => @deployment_group_id,
  12. :deployment_root_dir => @deployment_root_dir,
  13. :last_successful_deployment_dir => @last_successful_deployment_dir,
  14. :app_spec_path => @app_spec_path})
  15. end
  16. context "testing hook executor" do
  17. setup do
  18. @deployment_id='12345'
  19. @application_name='TestApplication'
  20. @deployment_group_name='TestDeploymentGroup'
  21. @deployment_group_id='foo'
  22. @deployment_root_dir = "deployment/root/dir"
  23. @last_successful_deployment_dir = "last/deployment/root/dir"
  24. @app_spec_path = "app_spec"
  25. @app_spec = { "version" => 0.0, "os" => "linux" }
  26. YAML.stubs(:load).returns(@app_spec)
  27. @root_dir = '/tmp/codedeploy'
  28. logger = mock
  29. logger.stubs(:log)
  30. InstanceAgent::DeploymentLog.stubs(:instance).returns(logger)
  31. end
  32. context "when creating a hook command" do
  33. context "first deployment pre-download scripts" do
  34. setup do
  35. File.stubs(:exist?).returns(false)
  36. @lifecycle_event = "ApplicationStop"
  37. end
  38. should "do nothing" do
  39. @hook_executor = HookExecutor.new ({:lifecycle_event => @lifecycle_event,
  40. :application_name => @application_name,
  41. :deployment_id => @deployment_id,
  42. :deployment_group_name => @deployment_group_name,
  43. :deployment_group_id => @deployment_group_id,
  44. :deployment_root_dir => @deployment_root_dir,
  45. :app_spec_path => @app_spec_path})
  46. end
  47. end
  48. context "first deployment post-download scripts" do
  49. setup do
  50. File.stubs(:exist?).returns(false)
  51. @lifecycle_event = "ValidateService"
  52. end
  53. should "raise an error" do
  54. assert_raise do
  55. @hook_executor = HookExecutor.new ({:lifecycle_event => @lifecycle_event,
  56. :application_name => @application_name,
  57. :deployment_group_name => @deployment_group_name,
  58. :deployment_group_id => @deployment_group_id,
  59. :deployment_id => @deployment_id,
  60. :deployment_root_dir => @deployment_root_dir})
  61. end
  62. end
  63. end
  64. context "all information provided" do
  65. setup do
  66. @lifecycle_event = "ValidateService"
  67. ApplicationSpecification.stubs(:parse)
  68. end
  69. should "parse an app spec from the current deployments directory" do
  70. File.expects(:read).with(File.join(@deployment_root_dir, 'deployment-archive', @app_spec_path))
  71. @hook_executor = create_full_hook_executor
  72. end
  73. context "hook is before download bundle" do
  74. setup do
  75. @lifecycle_event = "ApplicationStop"
  76. end
  77. should "parse an app spec from the previous deployment's directory" do
  78. File.expects(:read).with(File.join(@last_successful_deployment_dir, 'deployment-archive', @app_spec_path))
  79. @hook_executor = create_full_hook_executor
  80. end
  81. end
  82. end
  83. end
  84. context "when executing a hook command" do
  85. setup do
  86. @lifecycle_event = "ValidateService"
  87. File.stubs(:read).with(File.join(@deployment_root_dir, 'deployment-archive', @app_spec_path))
  88. @child_env={'LIFECYCLE_EVENT' => @lifecycle_event.to_s,
  89. 'DEPLOYMENT_ID' => @deployment_id.to_s,
  90. 'APPLICATION_NAME' => @application_name.to_s,
  91. 'DEPLOYMENT_GROUP_NAME' => @deployment_group_name.to_s,
  92. 'DEPLOYMENT_GROUP_ID' => @deployment_group_id.to_s}
  93. end
  94. context "no scripts to run for a given hook" do
  95. setup do
  96. @app_spec = {"version" => 0.0, "os" => "linux", "hooks" => {}}
  97. YAML.stubs(:load).returns(@app_spec)
  98. @hook_executor = create_full_hook_executor
  99. end
  100. should "do nothing" do
  101. @hook_executor.execute
  102. end
  103. end
  104. context "running with a single basic script" do
  105. setup do
  106. @app_spec = {"version" => 0.0, "os" => "linux", "hooks" => {'ValidateService'=>[{'location'=>'test'}]}}
  107. YAML.stubs(:load).returns(@app_spec)
  108. @script_location = File.join(@deployment_root_dir, 'deployment-archive', 'test')
  109. @hook_executor = create_full_hook_executor
  110. end
  111. context "when hook script doesn't exist" do
  112. setup do
  113. File.stubs(:exist?).with(@script_location).returns(false)
  114. end
  115. should "raise and exception" do
  116. assert_raised_with_message("Script does not exist at specified location: #{File.expand_path(@deployment_root_dir)}/deployment-archive/test", ScriptError)do
  117. @hook_executor.execute
  118. end
  119. end
  120. end
  121. context "when the file exists" do
  122. setup do
  123. File.stubs(:exist?).with(@script_location).returns(true)
  124. end
  125. context "and isn't executable" do
  126. setup do
  127. File.stubs(:executable?).with(@script_location).returns(false)
  128. InstanceAgent::Log.expects(:send).with(:warn, 'InstanceAgent::Plugins::CodeDeployPlugin::HookExecutor: Script at specified location: test is not executable. Trying to make it executable.')
  129. end
  130. should "log and make the hook script executable" do
  131. FileUtils.expects(:chmod)#.with("+x", @script_location)
  132. assert_raised_with_message('No such file or directory - deployment/root/dir/deployment-archive/test', Errno::ENOENT) do
  133. @hook_executor.execute
  134. end
  135. end
  136. context "and setting executable fails" do
  137. setup do
  138. FileUtils.stubs(:chmod).raises("An exception")
  139. end
  140. should "raise an exception" do
  141. assert_raised_with_message('Unable to set script at specified location: test as executable', ScriptError) do
  142. @hook_executor.execute
  143. end
  144. end
  145. end
  146. end
  147. context "files are executable (both intial checks pass)" do
  148. setup do
  149. File.stubs(:executable?).with(@script_location).returns(true)
  150. @mock_pipe = mock
  151. dummy_array = mock
  152. @mock_pipe.stubs(:each_line).returns(dummy_array)
  153. @mock_pipe.stubs(:close)
  154. dummy_array.stubs(:each).returns(nil)
  155. @wait_thr = mock
  156. @value = mock
  157. @wait_thr.stubs(:value).returns(@value)
  158. @wait_thr.stubs(:join).returns(1000)
  159. end
  160. context "scripts timeout" do
  161. setup do
  162. @app_spec = { "version" => 0.0, "os" => "linux", "hooks" => {'ValidateService'=> [{"location"=>"test", "timeout"=>"30"}]}}
  163. YAML.stubs(:load).returns(@app_spec)
  164. @hook_executor = create_full_hook_executor
  165. @wait_thr.stubs(:join).with(30).returns(nil)
  166. @wait_thr.stubs(:pid).returns(1234)
  167. mock_pipe = mock
  168. end
  169. context "with process group support" do
  170. setup do
  171. Open3.stubs(:popen3).with(@child_env, @script_location, :pgroup => true).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
  172. InstanceAgent::LinuxUtil.stubs(:supports_process_groups?).returns(true)
  173. end
  174. should "raise an exception" do
  175. Process.expects(:kill).with('-TERM', 1234)
  176. assert_raised_with_message('Script at specified location: test failed to complete in 30 seconds', ScriptError) do
  177. @hook_executor.execute
  178. end
  179. end
  180. end
  181. context "without process group support" do
  182. setup do
  183. Open3.stubs(:popen3).with(@child_env, @script_location, {}).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
  184. InstanceAgent::LinuxUtil.stubs(:supports_process_groups?).returns(false)
  185. end
  186. should "raise an exception" do
  187. Process.expects(:kill).with('KILL', 1234)
  188. assert_raised_with_message('Script at specified location: test failed to complete in 30 seconds', ScriptError) do
  189. @hook_executor.execute
  190. end
  191. end
  192. end
  193. end
  194. context "Scripts run with a runas" do
  195. setup do
  196. @app_spec = { "version" => 0.0, "os" => "linux", "hooks" => {'ValidateService'=> [{"location"=>"test", "runas"=>"user"}]}}
  197. YAML.stubs(:load).returns(@app_spec)
  198. @hook_executor = create_full_hook_executor
  199. mock_pipe = mock
  200. Open3.stubs(:popen3).with(@child_env, 'su user -c ' + @script_location, :pgroup => true).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
  201. end
  202. context "scripts fail" do
  203. setup do
  204. @value.stubs(:exitstatus).returns(1)
  205. end
  206. should "raise an exception" do
  207. assert_raised_with_message('Script at specified location: test run as user user failed with exit code 1', ScriptError) do
  208. @hook_executor.execute
  209. end
  210. end
  211. end
  212. context "scripts pass" do
  213. setup do
  214. @value.stubs(:exitstatus).returns(0)
  215. end
  216. should "execute script with runas" do
  217. Open3.expects(:popen3).with(@child_env, 'su user -c ' + @script_location, :pgroup => true).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
  218. @hook_executor.execute
  219. end
  220. end
  221. end
  222. context "Scripts run without a runas" do
  223. setup do
  224. @app_spec = { "version" => 0.0, "os" => "linux", "hooks" => {'ValidateService'=> [{"location"=>"test"}]}}
  225. YAML.stubs(:load).returns(@app_spec)
  226. @hook_executor = create_full_hook_executor
  227. Open3.stubs(:popen3).with(@child_env, @script_location, :pgroup => true).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
  228. end
  229. context "Scripts fail" do
  230. setup do
  231. @value.stubs(:exitstatus).returns(1)
  232. end
  233. should "raise an exception" do
  234. assert_raised_with_message('Script at specified location: test failed with exit code 1', ScriptError) do
  235. @hook_executor.execute
  236. end
  237. end
  238. end
  239. context "Scripts pass" do
  240. setup do
  241. @value.stubs(:exitstatus).returns(0)
  242. end
  243. should "execute script" do
  244. Open3.expects(:popen3).with(@child_env, @script_location, :pgroup => true).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
  245. @hook_executor.execute
  246. end
  247. end
  248. end
  249. context "Scripts run without process group support" do
  250. setup do
  251. @app_spec = { "version" => 0.0, "os" => "linux", "hooks" => {'ValidateService'=> [{"location"=>"test"}]}}
  252. YAML.stubs(:load).returns(@app_spec)
  253. @hook_executor = create_full_hook_executor
  254. Open3.stubs(:popen3).with(@child_env, @script_location, {}).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
  255. InstanceAgent::LinuxUtil.stubs(:supports_process_groups?).returns(false)
  256. end
  257. context "Scripts fail" do
  258. setup do
  259. @value.stubs(:exitstatus).returns(1)
  260. end
  261. should "raise an exception" do
  262. assert_raised_with_message('Script at specified location: test failed with exit code 1', ScriptError) do
  263. @hook_executor.execute
  264. end
  265. end
  266. end
  267. context "Scripts pass" do
  268. setup do
  269. @value.stubs(:exitstatus).returns(0)
  270. end
  271. should "execute script" do
  272. Open3.expects(:popen3).with(@child_env, @script_location, {}).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
  273. @hook_executor.execute
  274. end
  275. end
  276. end
  277. end
  278. end
  279. end
  280. end
  281. end
  282. end