aws-codedeploy-agent 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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