aws-codedeploy-agent 0.0.2 → 0.0.3

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 (91) hide show
  1. data/aws-codedeploy-agent.gemspec +5 -5
  2. data/certs/host-agent-deployment-signer-ca-chain.pem +30 -0
  3. data/conf/codedeployagent.yml +0 -1
  4. data/lib/instance_agent.rb +1 -13
  5. data/lib/instance_agent/agent/base.rb +38 -12
  6. data/lib/instance_agent/agent/plugin.rb +21 -0
  7. data/lib/instance_agent/config.rb +2 -1
  8. data/lib/instance_agent/platform/linux_util.rb +4 -0
  9. data/lib/instance_agent/plugins/codedeploy/application_specification/ace_info.rb +133 -0
  10. data/lib/instance_agent/plugins/codedeploy/application_specification/acl_info.rb +163 -0
  11. data/lib/instance_agent/plugins/codedeploy/application_specification/application_specification.rb +143 -0
  12. data/lib/instance_agent/plugins/codedeploy/application_specification/context_info.rb +23 -0
  13. data/lib/instance_agent/plugins/codedeploy/application_specification/file_info.rb +23 -0
  14. data/lib/instance_agent/plugins/codedeploy/application_specification/linux_permission_info.rb +121 -0
  15. data/lib/instance_agent/plugins/codedeploy/application_specification/mode_info.rb +66 -0
  16. data/lib/instance_agent/plugins/codedeploy/application_specification/range_info.rb +134 -0
  17. data/lib/instance_agent/plugins/codedeploy/application_specification/script_info.rb +27 -0
  18. data/lib/instance_agent/plugins/codedeploy/codedeploy_control.rb +100 -0
  19. data/lib/instance_agent/plugins/codedeploy/command_executor.rb +359 -0
  20. data/lib/instance_agent/plugins/codedeploy/command_poller.rb +178 -0
  21. data/lib/instance_agent/plugins/codedeploy/deployment_specification.rb +161 -0
  22. data/lib/instance_agent/plugins/codedeploy/hook_executor.rb +226 -0
  23. data/lib/instance_agent/plugins/codedeploy/install_instruction.rb +389 -0
  24. data/lib/instance_agent/plugins/codedeploy/installer.rb +147 -0
  25. data/lib/instance_agent/plugins/codedeploy/onpremise_config.rb +42 -0
  26. data/lib/instance_agent/plugins/codedeploy/register_plugin.rb +17 -0
  27. data/lib/instance_agent/runner/child.rb +20 -5
  28. data/lib/instance_agent/runner/master.rb +2 -15
  29. data/lib/instance_metadata.rb +2 -2
  30. data/test/certificate_helper.rb +1 -1
  31. data/test/helpers/instance_agent_helper.rb +1 -0
  32. data/test/instance_agent/agent/base_test.rb +16 -3
  33. data/test/instance_agent/config_test.rb +2 -1
  34. data/test/instance_agent/plugins/codedeploy/application_specification_test.rb +1713 -0
  35. data/test/instance_agent/{codedeploy_plugin → plugins/codedeploy}/codedeploy_control_test.rb +1 -1
  36. data/test/instance_agent/{codedeploy_plugin → plugins/codedeploy}/command_executor_test.rb +32 -9
  37. data/test/instance_agent/{codedeploy_plugin → plugins/codedeploy}/command_poller_test.rb +13 -14
  38. data/test/instance_agent/{codedeploy_plugin → plugins/codedeploy}/deployment_specification_test.rb +98 -25
  39. data/test/instance_agent/{codedeploy_plugin → plugins/codedeploy}/hook_executor_test.rb +83 -15
  40. data/test/instance_agent/plugins/codedeploy/install_instruction_test.rb +568 -0
  41. data/test/instance_agent/{codedeploy_plugin → plugins/codedeploy}/installer_test.rb +12 -9
  42. data/test/instance_agent/plugins/codedeploy/onpremise_config_test.rb +72 -0
  43. data/test/instance_agent/runner/child_test.rb +1 -1
  44. data/vendor/gems/.codedeploy-commands-1.0.0.created.rid +1 -1
  45. data/vendor/gems/codedeploy-commands/lib/aws/plugins/deploy_control_endpoint.rb +4 -0
  46. data/vendor/gems/jmespath-1.0.1/lib/jmespath.rb +41 -0
  47. data/vendor/gems/jmespath-1.0.1/lib/jmespath/caching_parser.rb +30 -0
  48. data/vendor/gems/jmespath-1.0.1/lib/jmespath/errors.rb +17 -0
  49. data/vendor/gems/jmespath-1.0.1/lib/jmespath/expr_node.rb +15 -0
  50. data/vendor/gems/jmespath-1.0.1/lib/jmespath/lexer.rb +116 -0
  51. data/vendor/gems/jmespath-1.0.1/lib/jmespath/parser.rb +347 -0
  52. data/vendor/gems/jmespath-1.0.1/lib/jmespath/runtime.rb +71 -0
  53. data/vendor/gems/jmespath-1.0.1/lib/jmespath/token.rb +41 -0
  54. data/vendor/gems/jmespath-1.0.1/lib/jmespath/token_stream.rb +60 -0
  55. data/vendor/gems/jmespath-1.0.1/lib/jmespath/tree_interpreter.rb +523 -0
  56. data/vendor/gems/jmespath-1.0.1/lib/jmespath/version.rb +3 -0
  57. data/vendor/gems/process_manager/lib/process_manager/master.rb +16 -5
  58. data/vendor/specifications/{aws-sdk-core-2.0.5.gemspec → aws-sdk-core-2.0.42.gemspec} +9 -11
  59. data/vendor/specifications/builder-3.2.2.gemspec +1 -1
  60. data/vendor/specifications/codedeploy-commands-1.0.0.gemspec +7 -6
  61. data/vendor/specifications/gli-2.5.6.gemspec +1 -1
  62. data/vendor/specifications/jmespath-1.0.1.gemspec +29 -0
  63. data/vendor/specifications/little-plugger-1.1.3.gemspec +1 -1
  64. data/vendor/specifications/logging-1.8.1.gemspec +1 -1
  65. data/vendor/specifications/multi_json-1.7.7.gemspec +1 -1
  66. data/vendor/specifications/multi_json-1.8.4.gemspec +1 -1
  67. data/vendor/specifications/multi_xml-0.5.5.gemspec +1 -1
  68. data/vendor/specifications/process_manager-0.0.13.gemspec +1 -1
  69. data/vendor/specifications/simple_pid-0.2.1.gemspec +1 -1
  70. metadata +76 -63
  71. data/lib/instance_agent/codedeploy_plugin/application_specification/ace_info.rb +0 -133
  72. data/lib/instance_agent/codedeploy_plugin/application_specification/acl_info.rb +0 -163
  73. data/lib/instance_agent/codedeploy_plugin/application_specification/application_specification.rb +0 -142
  74. data/lib/instance_agent/codedeploy_plugin/application_specification/context_info.rb +0 -23
  75. data/lib/instance_agent/codedeploy_plugin/application_specification/file_info.rb +0 -23
  76. data/lib/instance_agent/codedeploy_plugin/application_specification/linux_permission_info.rb +0 -121
  77. data/lib/instance_agent/codedeploy_plugin/application_specification/mode_info.rb +0 -66
  78. data/lib/instance_agent/codedeploy_plugin/application_specification/range_info.rb +0 -134
  79. data/lib/instance_agent/codedeploy_plugin/application_specification/script_info.rb +0 -27
  80. data/lib/instance_agent/codedeploy_plugin/codedeploy_control.rb +0 -72
  81. data/lib/instance_agent/codedeploy_plugin/command_executor.rb +0 -357
  82. data/lib/instance_agent/codedeploy_plugin/command_poller.rb +0 -170
  83. data/lib/instance_agent/codedeploy_plugin/deployment_specification.rb +0 -150
  84. data/lib/instance_agent/codedeploy_plugin/hook_executor.rb +0 -206
  85. data/lib/instance_agent/codedeploy_plugin/install_instruction.rb +0 -374
  86. data/lib/instance_agent/codedeploy_plugin/installer.rb +0 -143
  87. data/lib/instance_agent/codedeploy_plugin/request_helper.rb +0 -28
  88. data/test/instance_agent/codedeploy_plugin/application_specification_test.rb +0 -1710
  89. data/test/instance_agent/codedeploy_plugin/install_instruction_test.rb +0 -566
  90. data/test/instance_agent/codedeploy_plugin/request_helper_test.rb +0 -37
  91. data/vendor/specifications/jamespath-0.5.1.gemspec +0 -35
@@ -1,15 +1,17 @@
1
1
  require 'test_helper'
2
2
  require 'stringio'
3
3
  require 'fileutils'
4
- require 'instance_agent/codedeploy_plugin/installer'
5
4
  require 'com/amazon/codedeploy/command/v20141006/host_command_instance'
6
5
 
7
6
  class HookExecutorTest < InstanceAgentTestCase
8
7
 
9
- include InstanceAgent::CodeDeployPlugin
8
+ include InstanceAgent::Plugins::CodeDeployPlugin
10
9
 
11
10
  def create_full_hook_executor
12
11
  HookExecutor.new ({:lifecycle_event => @lifecycle_event,
12
+ :application_name => @application_name,
13
+ :deployment_id => @deployment_id,
14
+ :deployment_group_name => @deployment_group_name,
13
15
  :deployment_root_dir => @deployment_root_dir,
14
16
  :last_successful_deployment_dir => @last_successful_deployment_dir,
15
17
  :app_spec_path => @app_spec_path})
@@ -17,6 +19,9 @@ class HookExecutorTest < InstanceAgentTestCase
17
19
 
18
20
  context "testing hook executor" do
19
21
  setup do
22
+ @deployment_id='12345'
23
+ @application_name='TestApplication'
24
+ @deployment_group_name='TestDeploymentGroup'
20
25
  @deployment_root_dir = "deployment/root/dir"
21
26
  @last_successful_deployment_dir = "last/deployment/root/dir"
22
27
  @app_spec_path = "app_spec"
@@ -33,6 +38,9 @@ class HookExecutorTest < InstanceAgentTestCase
33
38
 
34
39
  should "do nothing" do
35
40
  @hook_executor = HookExecutor.new ({:lifecycle_event => @lifecycle_event,
41
+ :application_name => @application_name,
42
+ :deployment_id => @deployment_id,
43
+ :deployment_group_name => @deployment_group_name,
36
44
  :deployment_root_dir => @deployment_root_dir,
37
45
  :app_spec_path => @app_spec_path})
38
46
  end
@@ -47,7 +55,10 @@ class HookExecutorTest < InstanceAgentTestCase
47
55
  should "raise an error" do
48
56
  assert_raise do
49
57
  @hook_executor = HookExecutor.new ({:lifecycle_event => @lifecycle_event,
50
- :deployment_root_dir => @deployment_root_dir})
58
+ :application_name => @application_name,
59
+ :deployment_group_name => @deployment_gtroup_name,
60
+ :deployment_id => @deployment_id,
61
+ :deployment_root_dir => @deployment_root_dir})
51
62
  end
52
63
  end
53
64
  end
@@ -80,6 +91,10 @@ class HookExecutorTest < InstanceAgentTestCase
80
91
  setup do
81
92
  @lifecycle_event = "ValidateService"
82
93
  File.stubs(:read).with(File.join(@deployment_root_dir, 'deployment-archive', @app_spec_path))
94
+ @child_env={'LIFECYCLE_EVENT' => @lifecycle_event.to_s,
95
+ 'DEPLOYMENT_ID' => @deployment_id.to_s,
96
+ 'APPLICATION_NAME' => @application_name.to_s,
97
+ 'DEPLOYMENT_GROUP_NAME' => @deployment_group_name.to_s}
83
98
  end
84
99
 
85
100
  context "no scripts to run for a given hook" do
@@ -122,7 +137,7 @@ class HookExecutorTest < InstanceAgentTestCase
122
137
  context "and isn't executable" do
123
138
  setup do
124
139
  File.stubs(:executable?).with(@script_location).returns(false)
125
- InstanceAgent::Log.expects(:send).with(:warn, 'InstanceAgent::CodeDeployPlugin::HookExecutor: Script at specified location: test is not executable. Trying to make it executable.')
140
+ InstanceAgent::Log.expects(:send).with(:warn, 'InstanceAgent::Plugins::CodeDeployPlugin::HookExecutor: Script at specified location: test is not executable. Trying to make it executable.')
126
141
  end
127
142
 
128
143
  should "log and make the hook script executable" do
@@ -165,26 +180,46 @@ class HookExecutorTest < InstanceAgentTestCase
165
180
  YAML.stubs(:load).returns(@app_spec)
166
181
  @hook_executor = create_full_hook_executor
167
182
  @wait_thr.stubs(:join).with(30).returns(nil)
168
- @wait_thr.stubs(:pid)
183
+ @wait_thr.stubs(:pid).returns(1234)
169
184
  mock_pipe = mock
170
- Open3.stubs(:popen3).with(@script_location, :pgroup=>true).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
171
- Process.stubs(:kill)
172
185
  end
173
186
 
174
- should "raise and exception" do
175
- assert_raised_with_message('Script at specified location: test failed to complete in 30 seconds', ScriptError) do
176
- @hook_executor.execute
187
+ context "with process group support" do
188
+ setup do
189
+ Open3.stubs(:popen3).with(@child_env, @script_location, :pgroup => true).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
190
+ InstanceAgent::LinuxUtil.stubs(:supports_process_groups?).returns(true)
191
+ end
192
+
193
+ should "raise an exception" do
194
+ Process.expects(:kill).with('-TERM', 1234)
195
+ assert_raised_with_message('Script at specified location: test failed to complete in 30 seconds', ScriptError) do
196
+ @hook_executor.execute
197
+ end
177
198
  end
178
199
  end
179
- end
180
200
 
201
+ context "without process group support" do
202
+ setup do
203
+ Open3.stubs(:popen3).with(@child_env, @script_location, {}).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
204
+ InstanceAgent::LinuxUtil.stubs(:supports_process_groups?).returns(false)
205
+ end
206
+
207
+ should "raise an exception" do
208
+ Process.expects(:kill).with('KILL', 1234)
209
+ assert_raised_with_message('Script at specified location: test failed to complete in 30 seconds', ScriptError) do
210
+ @hook_executor.execute
211
+ end
212
+ end
213
+ end
214
+ end
215
+
181
216
  context "Scripts run with a runas" do
182
217
  setup do
183
218
  @app_spec = { "version" => 0.0, "os" => "linux", "hooks" => {'ValidateService'=> [{"location"=>"test", "runas"=>"user"}]}}
184
219
  YAML.stubs(:load).returns(@app_spec)
185
220
  @hook_executor = create_full_hook_executor
186
221
  mock_pipe = mock
187
- Open3.stubs(:popen3).with('su user -c ' + @script_location, :pgroup=>true).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
222
+ Open3.stubs(:popen3).with(@child_env, 'su user -c ' + @script_location, :pgroup => true).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
188
223
  end
189
224
 
190
225
  context "scripts fail" do
@@ -205,7 +240,7 @@ class HookExecutorTest < InstanceAgentTestCase
205
240
  end
206
241
 
207
242
  should "execute script with runas" do
208
- Open3.expects(:popen3).with('su user -c ' + @script_location, :pgroup=>true).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
243
+ Open3.expects(:popen3).with(@child_env, 'su user -c ' + @script_location, :pgroup => true).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
209
244
  @hook_executor.execute
210
245
  end
211
246
  end
@@ -216,7 +251,40 @@ class HookExecutorTest < InstanceAgentTestCase
216
251
  @app_spec = { "version" => 0.0, "os" => "linux", "hooks" => {'ValidateService'=> [{"location"=>"test"}]}}
217
252
  YAML.stubs(:load).returns(@app_spec)
218
253
  @hook_executor = create_full_hook_executor
219
- Open3.stubs(:popen3).with(@script_location, :pgroup=>true).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
254
+ Open3.stubs(:popen3).with(@child_env, @script_location, :pgroup => true).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
255
+ end
256
+
257
+ context "Scripts fail" do
258
+ setup do
259
+ @value.stubs(:exitstatus).returns(1)
260
+ end
261
+
262
+ should "raise an exception" do
263
+ assert_raised_with_message('Script at specified location: test failed with exit code 1', ScriptError) do
264
+ @hook_executor.execute
265
+ end
266
+ end
267
+ end
268
+
269
+ context "Scripts pass" do
270
+ setup do
271
+ @value.stubs(:exitstatus).returns(0)
272
+ end
273
+
274
+ should "execute script" do
275
+ Open3.expects(:popen3).with(@child_env, @script_location, :pgroup => true).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
276
+ @hook_executor.execute
277
+ end
278
+ end
279
+ end
280
+
281
+ context "Scripts run without process group support" do
282
+ setup do
283
+ @app_spec = { "version" => 0.0, "os" => "linux", "hooks" => {'ValidateService'=> [{"location"=>"test"}]}}
284
+ YAML.stubs(:load).returns(@app_spec)
285
+ @hook_executor = create_full_hook_executor
286
+ Open3.stubs(:popen3).with(@child_env, @script_location, {}).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
287
+ InstanceAgent::LinuxUtil.stubs(:supports_process_groups?).returns(false)
220
288
  end
221
289
 
222
290
  context "Scripts fail" do
@@ -237,7 +305,7 @@ class HookExecutorTest < InstanceAgentTestCase
237
305
  end
238
306
 
239
307
  should "execute script" do
240
- Open3.expects(:popen3).with(@script_location, :pgroup=>true).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
308
+ Open3.expects(:popen3).with(@child_env, @script_location, {}).yields([@mock_pipe,@mock_pipe,@mock_pipe,@wait_thr])
241
309
  @hook_executor.execute
242
310
  end
243
311
  end
@@ -0,0 +1,568 @@
1
+ require 'test_helper'
2
+ require 'json'
3
+ require 'fileutils'
4
+
5
+ module InstanceAgent
6
+ module Plugins
7
+ module CodeDeployPlugin
8
+ class InstallInstructionTest < InstanceAgentTestCase
9
+ context "parsing an install file" do
10
+ context "a single mapped file" do
11
+ setup do
12
+ install_instructions = { "revisionId" => "foo" , 'instructions' => [{"type" => :copy, "source" => "test_source", "destination" => "test_destination"}]}
13
+ @parse_string = JSON.dump(install_instructions)
14
+ @mock_file = mock
15
+ @mock_file.stubs(:puts)
16
+ @mock_file.stubs(:size).returns(0)
17
+ @mock_file.stubs(:close)
18
+ File.stubs(:open).returns(@mock_file)
19
+ end
20
+
21
+ should "return a collection containing a single CopyCommand which copies from test_source to test_destination" do
22
+ commands = InstallInstruction.parse_install_commands(@parse_string)
23
+ FileUtils.expects(:copy).with("test_source","test_destination", :preserve => true)
24
+ assert_not_equal nil, commands
25
+ commands.each do |command|
26
+ command.execute(@mock_file)
27
+ end
28
+ end
29
+ end
30
+
31
+ context "multiple mapped files" do
32
+ setup do
33
+ install_instructions = { "revisionId" => "foo" , "instructions" => [{"type" => "copy", "source" => "test_source", "destination" => "test_destination"}, {"type" => "copy", "source" => "source_2", "destination"=>"destination_2"}]}
34
+ @parse_string = JSON.dump(install_instructions)
35
+ @mock_file = mock
36
+ @mock_file.stubs(:puts)
37
+ @mock_file.stubs(:size).returns(0)
38
+ @mock_file.stubs(:close)
39
+ File.stubs(:open).returns(@mock_file)
40
+ end
41
+
42
+ should "return a collection containing multiple copy commands" do
43
+ commands = InstallInstruction.parse_install_commands(@parse_string)
44
+ FileUtils.expects(:copy).with("test_source","test_destination", :preserve => true)
45
+ FileUtils.expects(:copy).with("source_2","destination_2", :preserve => true)
46
+ assert_not_equal nil, commands
47
+ commands.each do |command|
48
+ command.execute(@mock_file)
49
+ end
50
+ end
51
+ end
52
+
53
+ context "contains a mkdir command" do
54
+ setup do
55
+ install_instructions = { "revisionId" => "foo" , "instructions" => [{"type" => "copy", "source" => "test_source", "destination" => "test_destination"}, {"type" => "mkdir", "directory"=>"directory"}]}
56
+ @parse_string = JSON.dump(install_instructions)
57
+ @mock_file = mock
58
+ @mock_file.stubs(:puts)
59
+ @mock_file.stubs(:size).returns(0)
60
+ @mock_file.stubs(:close)
61
+ @mock_file.stubs(:exist?).returns(false)
62
+ File.stubs(:open).returns(@mock_file)
63
+ File.stubs(:exists?).returns(false)
64
+ end
65
+
66
+ should "return a collection containing a copy command and a mkdir command" do
67
+ commands = InstallInstruction.parse_install_commands(@parse_string)
68
+ FileUtils.expects(:copy).with("test_source","test_destination", :preserve => true)
69
+ FileUtils.expects(:mkdir).with("directory")
70
+ assert_not_equal nil, commands
71
+ commands.each do |command|
72
+ command.execute(@mock_file)
73
+ end
74
+ end
75
+
76
+ should "raise an error if a file exists at the mkdir location" do
77
+ commands = InstallInstruction.parse_install_commands(@parse_string)
78
+ File.stubs(:exists?).with("directory").returns(true)
79
+ FileUtils.stubs(:copy)
80
+
81
+ assert_raised_with_message("File already exists at directory") do
82
+ commands.each do |command|
83
+ command.execute(@mock_file)
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ context "correctly determines method from file type" do
90
+ setup do
91
+ install_instructions = { "revisionId" => "foo" , "instructions" => [{"type" => "copy", "source" => "test_source", "destination" => "test_destination"}, {"type" => "copy", "source" => "source_2", "destination"=>"destination_2"}]}
92
+ @parse_string = JSON.dump(install_instructions)
93
+ @instruction_file = mock
94
+ @instruction_file.stubs(:read).returns(@parse_string)
95
+ @instruction_file.stubs(:path).returns("test/123-install.json")
96
+ @instruction_file.stubs(:close)
97
+ File.stubs(:open).with("test/123-install.json", 'r').returns(@instruction_file)
98
+ end
99
+
100
+ should "call parse_install_commands" do
101
+ InstallInstruction.expects(:parse_install_commands).with(@parse_string)
102
+ commands = InstallInstruction.generate_commands_from_file(@instruction_file)
103
+ end
104
+ end
105
+
106
+ context "contains a chmod command" do
107
+ setup do
108
+ install_instructions = { "revisionId" => "foo" , "instructions" => [{"type" => "chmod", "mode" => "0740", "file" => "testfile.txt"}]}
109
+ @parse_string = JSON.dump(install_instructions)
110
+ @mock_file = mock
111
+ File.stubs(:chmod)
112
+ end
113
+
114
+ should "set the mode of the object" do
115
+ commands = InstallInstruction.parse_install_commands(@parse_string)
116
+ File.expects(:chmod).with("740".to_i(8), "testfile.txt")
117
+ assert_not_equal nil, commands
118
+ commands.each do |command|
119
+ command.execute(@mock_file)
120
+ end
121
+ end
122
+ end
123
+
124
+ context "contains a chown command" do
125
+ setup do
126
+ install_instructions = { "revisionId" => "foo" , "instructions" => [{"type" => "chown", "owner" => "bob", "group" => "dev", "file" => "testfile.txt"}]}
127
+ @parse_string = JSON.dump(install_instructions)
128
+ @mock_etc = mock
129
+ @mock_etc.stubs(:gid).returns(222)
130
+ @mock_etc.stubs(:uid).returns(111)
131
+ @mock_file = mock
132
+ Etc.stubs(:getpwnam).with("bob").returns(@mock_etc)
133
+ Etc.stubs(:getgrnam).with("dev").returns(@mock_etc)
134
+ File.stubs(:chchown)
135
+ end
136
+
137
+ should "set the owner of the object" do
138
+ commands = InstallInstruction.parse_install_commands(@parse_string)
139
+ File.expects(:chown).with(111, 222, "testfile.txt")
140
+ assert_not_equal nil, commands
141
+ commands.each do |command|
142
+ command.execute(@mock_file)
143
+ end
144
+ end
145
+ end
146
+
147
+ context "contains a setfacl command" do
148
+ setup do
149
+ install_instructions = { "revisionId" => "foo" , "instructions" => [{"type" => "setfacl", "acl" => ["user:bob:rwx","default:user:bob:rwx"], "file" => "testfile.txt"}]}
150
+ @parse_string = JSON.dump(install_instructions)
151
+ @mock_file = mock
152
+ @mock_stat = mock
153
+ @mock_stat.stubs(:mode).returns("100421".to_i(8))
154
+ ChangeAclCommand.any_instance.stubs(:system).returns(false)
155
+ @full_acl = "user:bob:rwx,default:user:bob:rwx,user::r--,group::-w-,other::--x,mask::-w-,default:user::r--,default:group::-w-,default:other::--x,default:mask::-w-"
156
+ File.stubs(:stat).returns(@mock_stat)
157
+ end
158
+
159
+ should "set the acl of the object" do
160
+ commands = InstallInstruction.parse_install_commands(@parse_string)
161
+ ChangeAclCommand.any_instance.expects(:system).with("setfacl --set #{@full_acl} testfile.txt").returns(true)
162
+ assert_not_equal nil, commands
163
+ commands.each do |command|
164
+ command.execute(@mock_file)
165
+ end
166
+ end
167
+
168
+ should "throw if system call fails" do
169
+ commands = InstallInstruction.parse_install_commands(@parse_string)
170
+ ChangeAclCommand.any_instance.expects(:system).with("setfacl --set #{@full_acl} testfile.txt").returns(false)
171
+ assert_not_equal nil, commands
172
+ assert_raise(RuntimeError) do
173
+ commands.each do |command|
174
+ command.execute(@mock_file)
175
+ end
176
+ end
177
+ end
178
+ end
179
+
180
+ context "contains a semanage command" do
181
+
182
+ context "with a role" do
183
+ setup do
184
+ install_instructions = { "revisionId" => "foo" , "instructions" => [{"type" => "semanage", "context" => {"name" => "name", "role" => "role", "type" => "type", "range" => "s0" }, "file" => "testfile.txt"}]}
185
+ @parse_string = JSON.dump(install_instructions)
186
+ ChangeContextCommand.any_instance.stubs(:system).returns(true)
187
+ @mock_file = mock
188
+ end
189
+
190
+ should "raise an exception" do
191
+ assert_raise(RuntimeError) do
192
+ commands = InstallInstruction.parse_install_commands(@parse_string)
193
+ end
194
+ end
195
+ end
196
+
197
+ context "which is valid" do
198
+ setup do
199
+ install_instructions = { "revisionId" => "foo" , "instructions" => [{"type" => "semanage", "context" => {"name" => "name", "role" => nil, "type" => "type", "range" => "s0" }, "file" => "testfile.txt"}]}
200
+ @parse_string = JSON.dump(install_instructions)
201
+ @mock_file = mock
202
+ ChangeContextCommand.any_instance.stubs(:system).returns(false)
203
+ File.stubs(:realpath).returns("testfile.txt")
204
+ end
205
+
206
+ should "set the context of the object" do
207
+ commands = InstallInstruction.parse_install_commands(@parse_string)
208
+ ChangeContextCommand.any_instance.expects(:system).with("semanage fcontext -a -s name -t type -r s0 testfile.txt").returns(true)
209
+ ChangeContextCommand.any_instance.expects(:system).with("restorecon -v testfile.txt").returns(true)
210
+ @mock_file.expects(:puts).with("semanage\0testfile.txt")
211
+ assert_not_equal nil, commands
212
+ commands.each do |command|
213
+ command.execute(@mock_file)
214
+ end
215
+ end
216
+
217
+ should "throw if semanage system call fails" do
218
+ commands = InstallInstruction.parse_install_commands(@parse_string)
219
+ ChangeContextCommand.any_instance.expects(:system).with("semanage fcontext -a -s name -t type -r s0 testfile.txt").returns(false)
220
+ assert_not_equal nil, commands
221
+ assert_raise(RuntimeError) do
222
+ commands.each do |command|
223
+ command.execute(@mock_file)
224
+ end
225
+ end
226
+ end
227
+
228
+ should "throw if system call fails" do
229
+ commands = InstallInstruction.parse_install_commands(@parse_string)
230
+ ChangeContextCommand.any_instance.expects(:system).with("semanage fcontext -a -s name -t type -r s0 testfile.txt").returns(true)
231
+ ChangeContextCommand.any_instance.expects(:system).with("restorecon -v testfile.txt").returns(false)
232
+ assert_not_equal nil, commands
233
+ assert_raise(RuntimeError) do
234
+ commands.each do |command|
235
+ command.execute(@mock_file)
236
+ end
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
242
+
243
+ context "Parsing a delete file" do
244
+ context "an empty delete file" do
245
+ setup do
246
+ @parse_string = <<-END
247
+ END
248
+ end
249
+
250
+ should "return an empty command collection" do
251
+ commands = InstallInstruction.parse_remove_commands(@parse_string)
252
+ assert_equal 0, commands.length
253
+ end
254
+ end
255
+
256
+ context "a single file to delete" do
257
+ setup do
258
+ @parse_string = <<-END
259
+ test_delete_path
260
+ END
261
+ File.stubs(:exist?).with("test_delete_path").returns(true)
262
+ end
263
+
264
+ should "produce a command that deletes test_delete_path" do
265
+ commands = InstallInstruction.parse_remove_commands(@parse_string)
266
+ FileUtils.expects(:rm).with("test_delete_path")
267
+ assert_not_equal nil, commands
268
+ commands.each do |command|
269
+ command.execute
270
+ end
271
+ end
272
+ end
273
+
274
+ context "multiple files to delete" do
275
+ setup do
276
+ @parse_string = <<-END
277
+ test_delete_path
278
+ another_delete_path
279
+ END
280
+ File.stubs(:directory?).returns(false)
281
+ File.stubs(:exist?).with("test_delete_path").returns(true)
282
+ File.stubs(:exist?).with("another_delete_path").returns(true)
283
+ end
284
+
285
+ should "produce a command that deletes test_delete_path" do
286
+ commands = InstallInstruction.parse_remove_commands(@parse_string)
287
+ FileUtils.expects(:rm).with("test_delete_path")
288
+ FileUtils.expects(:rm).with("another_delete_path")
289
+ assert_not_equal nil, commands
290
+ commands.each do |command|
291
+ command.execute
292
+ end
293
+ end
294
+
295
+ should "use rmdir for directories" do
296
+ File.stubs(:directory?).with("test_delete_path").returns(true)
297
+
298
+ commands = InstallInstruction.parse_remove_commands(@parse_string)
299
+ FileUtils.expects(:rmdir).with("test_delete_path")
300
+ FileUtils.expects(:rm).with("another_delete_path")
301
+
302
+ commands.each do |command|
303
+ command.execute
304
+ end
305
+ end
306
+ end
307
+
308
+ context "removes mangled line at the end" do
309
+ setup do
310
+ @parse_string = <<-END
311
+ test_delete_path
312
+ another_delete_path
313
+ END
314
+ @parse_string << "mangled"
315
+ File.stubs(:exist?).with("test_delete_path").returns(true)
316
+ File.stubs(:exist?).with("another_delete_path").returns(true)
317
+ end
318
+
319
+ should "produce a command that deletes test_delete_path" do
320
+ commands = InstallInstruction.parse_remove_commands(@parse_string)
321
+ FileUtils.expects(:rm).with("test_delete_path")
322
+ FileUtils.expects(:rm).with("another_delete_path")
323
+ FileUtils.expects(:rm).with("mangled").never
324
+ assert_not_equal nil, commands
325
+ commands.each do |command|
326
+ command.execute
327
+ end
328
+ end
329
+ end
330
+
331
+ context "correctly determines method from file type" do
332
+ setup do
333
+ @parse_string = 'foo'
334
+ @instruction_file = mock
335
+ @instruction_file.stubs(:path).returns("test/123-cleanup")
336
+ File.stubs(:open).with("test/123-cleanup", 'r').returns(@instruction_file)
337
+ @instruction_file.stubs(:read).returns(@parse_string)
338
+ @instruction_file.stubs(:close)
339
+ end
340
+
341
+ should "call parse_remove_commands" do
342
+ InstallInstruction.expects(:parse_remove_commands).with(@parse_string)
343
+ commands = InstallInstruction.generate_commands_from_file(@instruction_file)
344
+ end
345
+ end
346
+
347
+ context "with a semanage command" do
348
+ setup do
349
+ @parse_string = "semanage\0testfile.txt\n"
350
+ RemoveContextCommand.any_instance.stubs(:system)
351
+ end
352
+
353
+ should "remove the context of the object" do
354
+ commands = InstallInstruction.parse_remove_commands(@parse_string)
355
+ RemoveContextCommand.any_instance.expects(:system).with("semanage fcontext -d testfile.txt")
356
+ assert_not_equal nil, commands
357
+ commands.each do |command|
358
+ command.execute
359
+ end
360
+ end
361
+ end
362
+ end
363
+
364
+ context "Testing the command builder" do
365
+ setup do
366
+ @command_builder = CommandBuilder.new()
367
+ Dir.chdir "/tmp"
368
+ end
369
+
370
+ should "Have an empty command array" do
371
+ assert_equal @command_builder.command_array, []
372
+ end
373
+
374
+ context "with a single copy command" do
375
+ setup do
376
+ @command_builder = CommandBuilder.new()
377
+ @command_builder.copy("source", "destination")
378
+ @expected_json = {"instructions"=>[{"type"=>"copy","source"=>"source","destination"=>"/tmp/destination"}]}.to_json
379
+ end
380
+
381
+ should "have a single copy in the returned JSON" do
382
+ assert_equal JSON.parse(@expected_json), JSON.parse(@command_builder.to_json)
383
+ end
384
+
385
+ should "raise a duplicate exception when a copy collides with another copy" do
386
+ assert_raised_with_message("Duplicate copy instruction to /tmp/destination from source and source") do
387
+ @command_builder.copy("source", "destination")
388
+ end
389
+ end
390
+ end
391
+
392
+ context "with a single mkdir command" do
393
+ setup do
394
+ @command_builder = CommandBuilder.new()
395
+ @command_builder.mkdir("directory")
396
+ @expected_json = {"instructions"=>[{"type"=>"mkdir","directory"=>"/tmp/directory"}]}.to_json
397
+ end
398
+
399
+ should "have a single mkdir in the returned JSON" do
400
+ assert_equal JSON.parse(@expected_json), JSON.parse(@command_builder.to_json)
401
+ end
402
+
403
+ should "raise a duplicate exception when trying to create a directory collides with a copy" do
404
+ @command_builder.copy("source", "directory/dir1")
405
+ assert_raised_with_message("Duplicate mkdir instruction for /tmp/directory/dir1 which is already being copied from source") do
406
+ @command_builder.mkdir("directory/dir1")
407
+ end
408
+ end
409
+ end
410
+
411
+ context "with one of each command (copy and mkdir)" do
412
+ setup do
413
+ @command_builder = CommandBuilder.new()
414
+ @command_builder.mkdir("directory/target")
415
+ @command_builder.copy( "file_target", "directory/target/file_target")
416
+ end
417
+
418
+ should "raise a duplicate exception when trying to make a copy collides with a mkdir" do
419
+ assert_raised_with_message("Duplicate copy instruction to /tmp/directory/target from target which is already being installed as a directory") do
420
+ @command_builder.copy( "target", "directory/target")
421
+ end
422
+ end
423
+
424
+ should "say it is copying the appropriate file" do
425
+ assert @command_builder.copying_file?("/tmp/directory/target/file_target")
426
+ assert !@command_builder.copying_file?("/tmp/directory/target")
427
+ end
428
+
429
+ should "say it is making the appropriate directory" do
430
+ assert !@command_builder.making_directory?("/tmp/directory/target/file_target")
431
+ assert @command_builder.making_directory?("/tmp/directory/target")
432
+ end
433
+
434
+ should "match the file when appropriate" do
435
+ permission = InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::LinuxPermissionInfo.new("/tmp/directory/target", {
436
+ :type => ["file"],
437
+ :pattern => "file*",
438
+ :except => []})
439
+ assert @command_builder.find_matches(permission).include?("/tmp/directory/target/file_target")
440
+
441
+ permission = InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::LinuxPermissionInfo.new("/tmp/directory/target", {
442
+ :type => ["directory"],
443
+ :pattern => "file*",
444
+ :except => []})
445
+ assert !@command_builder.find_matches(permission).include?("/tmp/directory/target/file_target")
446
+
447
+ permission = InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::LinuxPermissionInfo.new("/tmp/directory/target", {
448
+ :type => ["file"],
449
+ :pattern => "filefile*",
450
+ :except => []})
451
+ assert !@command_builder.find_matches(permission).include?("/tmp/directory/target/file_target")
452
+
453
+ permission = InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::LinuxPermissionInfo.new("/tmp/directory/target", {
454
+ :type => ["file"],
455
+ :pattern => "file*",
456
+ :except => ["*target"]})
457
+ assert !@command_builder.find_matches(permission).include?("/tmp/directory/target/file_target")
458
+ end
459
+
460
+ should "match the directory when appropriate" do
461
+ permission = InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::LinuxPermissionInfo.new("/tmp/directory/", {
462
+ :type => ["directory"],
463
+ :pattern => "tar*",
464
+ :except => []})
465
+ assert @command_builder.find_matches(permission).include?("/tmp/directory/target")
466
+
467
+ permission = InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::LinuxPermissionInfo.new("/tmp/directory/", {
468
+ :type => ["file"],
469
+ :pattern => "tar*",
470
+ :except => []})
471
+ assert !@command_builder.find_matches(permission).include?("/tmp/directory/target")
472
+
473
+ permission = InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::LinuxPermissionInfo.new("/tmp/directory/", {
474
+ :type => ["directory"],
475
+ :pattern => "tarr*",
476
+ :except => []})
477
+ assert !@command_builder.find_matches(permission).include?("/tmp/directory/target")
478
+
479
+ permission = InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::LinuxPermissionInfo.new("/tmp/directory/", {
480
+ :type => ["directory"],
481
+ :pattern => "tar*",
482
+ :except => ["*et"]})
483
+ assert !@command_builder.find_matches(permission).include?("/tmp/directory/target")
484
+ end
485
+ end
486
+
487
+ context "two mkdirs to the same place" do
488
+ setup do
489
+ @command_builder = CommandBuilder.new()
490
+ @command_builder.mkdir("directory")
491
+ @command_builder.mkdir("directory")
492
+ @expected_json = {"instructions"=>[{"type"=>"mkdir","directory"=>"/tmp/directory"}]}.to_json
493
+ end
494
+
495
+ should "have a single mkdir in the returned JSON" do
496
+ assert_equal JSON.parse(@expected_json), JSON.parse(@command_builder.to_json)
497
+ end
498
+ end
499
+
500
+ context "two mkdirs to the same place one has a trailing /" do
501
+ setup do
502
+ @command_builder = CommandBuilder.new()
503
+ @command_builder.mkdir("directory")
504
+ @command_builder.mkdir("directory/")
505
+ @expected_json = {"instructions"=>[{"type"=>"mkdir","directory"=>"/tmp/directory"}]}.to_json
506
+ end
507
+
508
+ should "have a single mkdir in the returned JSON" do
509
+ assert_equal JSON.parse(@expected_json), JSON.parse(@command_builder.to_json)
510
+ end
511
+ end
512
+
513
+ context "setting permissions" do
514
+ should "raise a duplicate exception when trying to set permissions twice" do
515
+ @permission = InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::LinuxPermissionInfo.new("testfile.txt")
516
+ @command_builder = CommandBuilder.new()
517
+ @command_builder.set_permissions("testfile.txt", @permission)
518
+ assert_raised_with_message("Duplicate permission setting instructions for /tmp/testfile.txt") do
519
+ @command_builder.set_permissions("testfile.txt", @permission)
520
+ end
521
+ end
522
+
523
+ should "not add any commands for empty permissions" do
524
+ @permission = InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::LinuxPermissionInfo.new("testfile.txt")
525
+ @command_builder = CommandBuilder.new()
526
+ @command_builder.set_permissions("testfile.txt", @permission)
527
+ @expected_json = {"instructions"=>[]}.to_json
528
+ assert_equal JSON.parse(@expected_json), JSON.parse(@command_builder.to_json)
529
+ end
530
+
531
+ should "add commands for each part of permisssions" do
532
+ @permission = InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::LinuxPermissionInfo.new("testfile.txt", {
533
+ :mode=>InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::ModeInfo.new(744),
534
+ :acls=>InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::AclInfo.new(["u:bob:7","d:g:dev:4"]),
535
+ :context=>InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::ContextInfo.new({"name"=>"name","type"=>"type","range"=>"s2-s3:c0,c2.c4,c6"}),
536
+ :owner=>"bob",
537
+ :group=>"dev"})
538
+ @command_builder = CommandBuilder.new()
539
+ @command_builder.set_permissions("testfile.txt", @permission)
540
+ @expected_json = {"instructions"=>[{"type"=>"chmod","mode"=>"744","file"=>"/tmp/testfile.txt"},
541
+ {"type"=>"setfacl","acl"=>["user:bob:rwx","default:group:dev:r--"],"file"=>"/tmp/testfile.txt"},
542
+ {"type"=>"semanage","context"=>{"user"=>"name","role"=>nil,"type"=>"type","range"=>"s2-s3:c0,c2.c4,c6"},"file"=>"/tmp/testfile.txt"},
543
+ {"type"=>"chown","owner"=>"bob","group"=>"dev","file"=>"/tmp/testfile.txt"}
544
+ ]}.to_json
545
+ assert_equal JSON.parse(@expected_json), JSON.parse(@command_builder.to_json)
546
+ end
547
+
548
+ should "add chown command with just owner" do
549
+ @permission = InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::LinuxPermissionInfo.new("testfile.txt", {:owner=>"bob"})
550
+ @command_builder = CommandBuilder.new()
551
+ @command_builder.set_permissions("testfile.txt", @permission)
552
+ @expected_json = {"instructions"=>[{"type"=>"chown","owner"=>"bob","group"=>nil,"file"=>"/tmp/testfile.txt"}]}.to_json
553
+ assert_equal JSON.parse(@expected_json), JSON.parse(@command_builder.to_json)
554
+ end
555
+
556
+ should "add chown command with just group" do
557
+ @permission = InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::LinuxPermissionInfo.new("testfile.txt", {:group=>"dev"})
558
+ @command_builder = CommandBuilder.new()
559
+ @command_builder.set_permissions("testfile.txt", @permission)
560
+ @expected_json = {"instructions"=>[{"type"=>"chown","owner"=>nil,"group"=>"dev","file"=>"/tmp/testfile.txt"}]}.to_json
561
+ assert_equal JSON.parse(@expected_json), JSON.parse(@command_builder.to_json)
562
+ end
563
+ end
564
+ end
565
+ end
566
+ end
567
+ end
568
+ end