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
@@ -0,0 +1,389 @@
1
+ require 'etc'
2
+ require 'fileutils'
3
+ require 'json'
4
+
5
+ module InstanceAgent
6
+ module Plugins
7
+ module CodeDeployPlugin
8
+ class InstallInstruction
9
+ def self.generate_commands_from_file(file)
10
+ name = File.basename(file.path)
11
+ file = File.open(file.path, 'r')
12
+ contents = file.read
13
+ file.close
14
+ if name =~ /^*-install.json/
15
+ parse_install_commands(contents)
16
+ elsif name =~ /^*-cleanup/
17
+ parse_remove_commands(contents)
18
+ end
19
+ end
20
+
21
+ def self.parse_install_commands(contents)
22
+ instructions = JSON.parse(contents)['instructions']
23
+ commands = []
24
+ instructions.each do |mapping|
25
+ case mapping['type']
26
+ when "copy"
27
+ commands << CopyCommand.new(mapping["source"], mapping["destination"])
28
+ when "mkdir"
29
+ commands << MakeDirectoryCommand.new(mapping["directory"])
30
+ when "chmod"
31
+ commands << ChangeModeCommand.new(mapping['file'], mapping['mode'])
32
+ when "chown"
33
+ commands << ChangeOwnerCommand.new(mapping['file'], mapping['owner'], mapping['group'])
34
+ when "setfacl"
35
+ commands << ChangeAclCommand.new(mapping['file'], InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::AclInfo.new(mapping['acl']))
36
+ when "semanage"
37
+ if !mapping['context']['role'].nil?
38
+ raise "Attempt to set role on object, not supported"
39
+ end
40
+ commands << ChangeContextCommand.new(mapping['file'], InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::ContextInfo.new(mapping['context']))
41
+ else
42
+ raise "Unknown command: #{mapping}"
43
+ end
44
+ end
45
+ commands
46
+ end
47
+
48
+ def self.parse_remove_commands(contents)
49
+ return [] if contents.empty?
50
+ #remove the unfinished paths
51
+ lines = contents.lines.to_a
52
+ if lines.last[lines.last.length-1] != "\n"
53
+ lines.pop
54
+ end
55
+ commands = []
56
+ lines.each do |command|
57
+ if command.start_with?("semanage\0")
58
+ commands << RemoveContextCommand.new(command.split("\0",2)[1].strip)
59
+ else
60
+ commands << RemoveCommand.new(command.strip)
61
+ end
62
+ end
63
+ commands.reverse
64
+ end
65
+
66
+ def self.generate_instructions()
67
+ command_builder = CommandBuilder.new()
68
+ yield(command_builder)
69
+ command_builder
70
+ end
71
+ end
72
+
73
+ class CommandBuilder
74
+ attr_reader :command_array
75
+ def initialize()
76
+ @command_array = []
77
+ @copy_targets = Hash.new
78
+ @mkdir_targets = Set.new
79
+ @permission_targets = Set.new
80
+ end
81
+
82
+ def copy(source, destination)
83
+ destination = sanitize_dir_path(destination)
84
+ log(:debug, "Copying #{source} to #{destination}")
85
+ raise "Duplicate copy instruction to #{destination} from #{source} and #{@copy_targets[destination]}" if @copy_targets.has_key?(destination)
86
+ raise "Duplicate copy instruction to #{destination} from #{source} which is already being installed as a directory" if @mkdir_targets.include?(destination)
87
+ @command_array << CopyCommand.new(source, destination)
88
+ @copy_targets[destination] = source
89
+ end
90
+
91
+ def mkdir(destination)
92
+ destination = sanitize_dir_path(destination)
93
+ log(:debug, "Making directory #{destination}")
94
+ raise "Duplicate mkdir instruction for #{destination} which is already being copied from #{@copy_targets[destination]}" if @copy_targets.has_key?(destination)
95
+ @command_array << MakeDirectoryCommand.new(destination) unless @mkdir_targets.include?(destination)
96
+ @mkdir_targets.add(destination)
97
+ end
98
+
99
+ def set_permissions(object, permission)
100
+ object = sanitize_dir_path(object)
101
+ log(:debug, "Setting permissions on #{object}")
102
+ raise "Duplicate permission setting instructions for #{object}" if @permission_targets.include?(object)
103
+ @permission_targets.add(object)
104
+
105
+ if !permission.mode.nil?
106
+ log(:debug, "Setting mode on #{object}")
107
+ @command_array << ChangeModeCommand.new(object, permission.mode.mode)
108
+ end
109
+
110
+ if !permission.acls.nil?
111
+ log(:debug, "Setting acl on #{object}")
112
+ @command_array << ChangeAclCommand.new(object, permission.acls)
113
+ end
114
+
115
+ if !permission.context.nil?
116
+ log(:debug, "Setting context on #{object}")
117
+ @command_array << ChangeContextCommand.new(object, permission.context)
118
+ end
119
+
120
+ if !permission.owner.nil? || !permission.group.nil?
121
+ log(:debug, "Setting ownership of #{object}")
122
+ @command_array << ChangeOwnerCommand.new(object, permission.owner, permission.group)
123
+ end
124
+ end
125
+
126
+ def copying_file?(file)
127
+ file = sanitize_dir_path(file)
128
+ log(:debug, "Checking for #{file} in #{@copy_targets.keys.inspect}")
129
+ @copy_targets.has_key?(file)
130
+ end
131
+
132
+ def making_directory?(dir)
133
+ dir = sanitize_dir_path(dir)
134
+ log(:debug, "Checking for #{dir} in #{@mkdir_targets.inspect}")
135
+ @mkdir_targets.include?(dir)
136
+ end
137
+
138
+ def find_matches(permission)
139
+ log(:debug, "Finding matches for #{permission.object}")
140
+ matches = []
141
+ if permission.type.include?("file")
142
+ @copy_targets.keys.each do |object|
143
+ log(:debug, "Checking #{object}")
144
+ if (permission.matches_pattern?(object) && !permission.matches_except?(object))
145
+ log(:debug, "Found match #{object}")
146
+ permission.validate_file_acl(object)
147
+ matches << object
148
+ end
149
+ end
150
+ end
151
+ if permission.type.include?("directory")
152
+ @mkdir_targets.each do |object|
153
+ log(:debug, "Checking #{object}")
154
+ if (permission.matches_pattern?(object) && !permission.matches_except?(object))
155
+ log(:debug, "Found match #{object}")
156
+ matches << object
157
+ end
158
+ end
159
+ end
160
+ matches
161
+ end
162
+
163
+ def to_json
164
+ command_json = @command_array.map(&:to_h)
165
+ {:instructions => command_json}.to_json
166
+ end
167
+
168
+ def each(&block)
169
+ @command_array.each(&block)
170
+ end
171
+
172
+ # Clean up explicitly since these can grow to tens of MBs if the deployment archive is large.
173
+ def cleanup()
174
+ @command_array.clear
175
+ @command_array = nil
176
+ @copy_targets.clear
177
+ @copy_targets = nil
178
+ @mkdir_targets.clear
179
+ @mkdir_targets = nil
180
+ @permission_targets.clear
181
+ @permission_targets = nil
182
+ end
183
+
184
+ private
185
+ def sanitize_dir_path(path)
186
+ File.expand_path(path)
187
+ end
188
+
189
+ private
190
+ def description
191
+ self.class.to_s
192
+ end
193
+
194
+ private
195
+ def log(severity, message)
196
+ raise ArgumentError, "Unknown severity #{severity.inspect}" unless InstanceAgent::Log::SEVERITIES.include?(severity.to_s)
197
+ InstanceAgent::Log.send(severity.to_sym, "#{description}: #{message}")
198
+ end
199
+ end
200
+
201
+ class RemoveCommand
202
+ def initialize(location)
203
+ @file_path = location
204
+ end
205
+
206
+ def execute
207
+ #If the file doesn't exist the command is ignored
208
+ if File.exist?(@file_path)
209
+ if File.directory?(@file_path)
210
+ # TODO (AWSGLUE-713): handle the exception if the directory is non-empty;
211
+ # this might mean the customer has put files in this directory and we should
212
+ # probably ignore the error and move on
213
+ FileUtils.rmdir(@file_path)
214
+ else
215
+ FileUtils.rm(@file_path)
216
+ end
217
+ end
218
+ end
219
+ end
220
+
221
+ class CopyCommand
222
+ attr_reader :destination, :source
223
+ def initialize(source, destination)
224
+ @source = source
225
+ @destination = destination
226
+ end
227
+
228
+ def execute(cleanup_file)
229
+ raise "File already exists at #{@destination}" if File.exists?(@destination)
230
+ cleanup_file.puts(@destination)
231
+ if File.symlink?(@source)
232
+ FileUtils.symlink(File.readlink(@source), @destination)
233
+ else
234
+ FileUtils.copy(@source, @destination, :preserve => true)
235
+ end
236
+ end
237
+
238
+ def to_h
239
+ {:type => :copy, :source => @source, :destination => @destination}
240
+ end
241
+ end
242
+
243
+ class MakeDirectoryCommand
244
+ def initialize(destination)
245
+ @directory = destination
246
+ end
247
+
248
+ def execute(cleanup_file)
249
+ raise "File already exists at #{@directory}" if
250
+ File.exists?(@directory)
251
+ FileUtils.mkdir(@directory)
252
+ cleanup_file.puts(@directory)
253
+ end
254
+
255
+ def to_h
256
+ {:type => :mkdir, :directory => @directory}
257
+ end
258
+ end
259
+
260
+ class ChangeModeCommand
261
+ def initialize(object, mode)
262
+ @object = object
263
+ @mode = mode
264
+ end
265
+
266
+ def execute(cleanup_file)
267
+ File.chmod(@mode.to_i(8), @object)
268
+ end
269
+
270
+ def to_h
271
+ {:type => :chmod, :mode => @mode, :file => @object}
272
+ end
273
+ end
274
+
275
+ class ChangeAclCommand
276
+ def initialize(object, acl)
277
+ @object = object
278
+ @acl = acl
279
+ end
280
+
281
+ def execute(cleanup_file)
282
+ begin
283
+ get_full_acl
284
+ acl = @acl.get_acl.join(",")
285
+ if !system("setfacl --set #{acl} #{@object}")
286
+ raise "Unable to set acl correctly: setfacl --set #{acl} #{@object}, exit code: #{$?}"
287
+ end
288
+ ensure
289
+ clear_full_acl
290
+ end
291
+ end
292
+
293
+ def clear_full_acl
294
+ @acl.clear_additional
295
+ end
296
+
297
+ def get_full_acl()
298
+ perm = "%o" % File.stat(@object).mode
299
+ perm = perm[-3,3]
300
+ @acl.add_ace(":#{perm[0]}")
301
+ @acl.add_ace("g::#{perm[1]}")
302
+ @acl.add_ace("o::#{perm[2]}")
303
+ if @acl.has_base_named? && !@acl.has_base_mask?
304
+ @acl.add_ace("m::#{perm[1]}")
305
+ end
306
+ if @acl.has_default?
307
+ if !@acl.has_default_user?
308
+ @acl.add_ace("d::#{perm[0]}")
309
+ end
310
+ if !@acl.has_default_group?
311
+ @acl.add_ace("d:g::#{perm[1]}")
312
+ end
313
+ if !@acl.has_default_other?
314
+ @acl.add_ace("d:o:#{perm[2]}")
315
+ end
316
+ if @acl.has_default_named? && !@acl.has_default_mask?
317
+ @acl.add_ace(@acl.get_default_group_ace.sub("group:","mask"))
318
+ end
319
+ end
320
+ end
321
+
322
+ def to_h
323
+ {:type => :setfacl, :acl => @acl.get_acl, :file => @object}
324
+ end
325
+ end
326
+
327
+ class ChangeOwnerCommand
328
+ def initialize(object, owner, group)
329
+ @object = object
330
+ @owner = owner
331
+ @group = group
332
+ end
333
+
334
+ def execute(cleanup_file)
335
+ ownerid = Etc.getpwnam(@owner).uid if @owner
336
+ groupid = Etc.getgrnam(@group).gid if @group
337
+ File.chown(ownerid, groupid, @object)
338
+ end
339
+
340
+ def to_h
341
+ {:type => :chown, :owner => @owner, :group => @group, :file => @object}
342
+ end
343
+ end
344
+
345
+ class ChangeContextCommand
346
+ def initialize(object, context)
347
+ @object = object
348
+ @context = context
349
+ end
350
+
351
+ def execute(cleanup_file)
352
+ if !@context.role.nil?
353
+ raise "Attempt to set role on object, not supported"
354
+ end
355
+ args = "-t #{@context.type}"
356
+ if (!@context.user.nil?)
357
+ args = "-s #{@context.user} " + args
358
+ end
359
+ if (!@context.range.nil?)
360
+ args = args + " -r #{@context.range.get_range}"
361
+ end
362
+
363
+ object = File.realpath(@object)
364
+ if !system("semanage fcontext -a #{args} #{object}")
365
+ raise "Unable to set context: semanage fcontext -a #{args} #{object}, exit code: #{$?}"
366
+ end
367
+ if !system("restorecon -v #{object}")
368
+ raise "Unable to apply context: restorcecon -v #{object}, exit code: #{$?}"
369
+ end
370
+ cleanup_file.puts("semanage\0#{object}")
371
+ end
372
+
373
+ def to_h
374
+ {:type => :semanage, :context => {:user => @context.user, :role => @context.role, :type => @context.type, :range => @context.range.get_range}, :file => @object}
375
+ end
376
+ end
377
+
378
+ class RemoveContextCommand
379
+ def initialize(object)
380
+ @object = object
381
+ end
382
+
383
+ def execute
384
+ system("semanage fcontext -d #{@object}")
385
+ end
386
+ end
387
+ end
388
+ end
389
+ end
@@ -0,0 +1,147 @@
1
+ module InstanceAgent
2
+ module Plugins
3
+ module CodeDeployPlugin
4
+ # Manages install and cleanup files. Also generates and executes
5
+ # install instructions based on the files section of the
6
+ # application specification file.
7
+ class Installer
8
+
9
+ attr_reader :deployment_archive_dir
10
+ attr_reader :deployment_instructions_dir
11
+ def initialize(opts = {})
12
+ raise "the deployment_archive_dir option is required" if
13
+ opts[:deployment_archive_dir].nil?
14
+ raise "the deployment_instructions_dir option is required" if
15
+ opts[:deployment_instructions_dir].nil?
16
+
17
+ @deployment_archive_dir = opts[:deployment_archive_dir]
18
+ @deployment_instructions_dir = opts[:deployment_instructions_dir]
19
+ end
20
+
21
+ def install(deployment_group_id, application_specification)
22
+ cleanup_file = File.join(deployment_instructions_dir, "#{deployment_group_id}-cleanup")
23
+
24
+ if File.exists?(cleanup_file)
25
+ commands = InstallInstruction.parse_remove_commands(File.read(cleanup_file))
26
+ commands.each do |cmd|
27
+ cmd.execute
28
+ end
29
+
30
+ commands.clear
31
+ FileUtils.rm(cleanup_file)
32
+ end
33
+
34
+ instructions = generate_instructions(application_specification)
35
+
36
+ install_file = File.join(deployment_instructions_dir, "#{deployment_group_id}-install.json")
37
+ File.open(install_file, "w") do |f|
38
+ f.write(instructions.to_json)
39
+ end
40
+
41
+ File.open(cleanup_file, "w") do |f|
42
+ instructions.command_array.each do |cmd|
43
+ cmd.execute(f)
44
+ end
45
+ end
46
+
47
+ #Unlink references to the CommandBuilder instance that was yielded to the Proc object(code block) in generate_instructions()
48
+ instructions.cleanup
49
+ instructions = nil
50
+ end
51
+
52
+ private
53
+ def generate_instructions(application_specification)
54
+ InstallInstruction.generate_instructions() do |i|
55
+ application_specification.files.each do |fi|
56
+
57
+ absolute_source_path = File.join(deployment_archive_dir,
58
+ fi.source)
59
+
60
+ log(:debug, "generating instructions for copying #{fi.source} to #{fi.destination}")
61
+ if File.directory?(absolute_source_path)
62
+ fill_in_missing_ancestors(i, fi.destination)
63
+ generate_directory_copy(i, absolute_source_path, fi.destination)
64
+ else
65
+ file_destination = File.join(fi.destination, File.basename(absolute_source_path))
66
+ fill_in_missing_ancestors(i, file_destination)
67
+ generate_normal_copy(i, absolute_source_path, file_destination)
68
+ end
69
+ end
70
+
71
+ (application_specification.permissions || []).each do |permission|
72
+ object = permission.object
73
+
74
+ log(:debug, "generating instructions for setting permissions on object #{object}")
75
+ log(:debug, "it is an existing directory - #{File.directory?(object)}")
76
+ if i.copying_file?(object)
77
+ if permission.type.include?("file")
78
+ log(:debug, "found matching file #{object} to set permissions on")
79
+ permission.validate_file_permission
80
+ permission.validate_file_acl(object)
81
+ i.set_permissions(object, permission)
82
+ end
83
+ elsif (i.making_directory?(object) || File.directory?(object))
84
+ log(:debug, "found matching directory #{object} to search for objects to set permissions on")
85
+ i.find_matches(permission).each do|match|
86
+ log(:debug, "found matching object #{match} to set permissions on")
87
+ i.set_permissions(match, permission)
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ private
95
+ def generate_directory_copy(i, absolute_source_path, destination)
96
+ unless File.directory?(destination)
97
+ i.mkdir(destination)
98
+ end
99
+
100
+ (Dir.entries(absolute_source_path) - [".", ".."]).each do |entry|
101
+ absolute_entry_path = File.join(absolute_source_path, entry)
102
+ entry_destination = File.join(destination, entry)
103
+ if File.directory?(absolute_entry_path)
104
+ generate_directory_copy(i, absolute_entry_path, entry_destination)
105
+ else
106
+ generate_normal_copy(i, absolute_entry_path, entry_destination)
107
+ end
108
+ end
109
+ end
110
+
111
+ private
112
+ def generate_normal_copy(i, absolute_source_path, destination)
113
+ raise "File already exists at location #{destination}" if
114
+ File.exists?(destination)
115
+
116
+ i.copy(absolute_source_path, destination)
117
+ end
118
+
119
+ private
120
+ def fill_in_missing_ancestors(i, destination)
121
+ missing_ancestors = []
122
+ parent_dir = File.dirname(destination)
123
+ while !File.exists?(parent_dir) &&
124
+ parent_dir != "." && parent_dir != "/"
125
+ missing_ancestors.unshift(parent_dir)
126
+ parent_dir = File.dirname(parent_dir)
127
+ end
128
+
129
+ missing_ancestors.each do |dir|
130
+ i.mkdir(dir)
131
+ end
132
+ end
133
+
134
+ private
135
+ def description
136
+ self.class.to_s
137
+ end
138
+
139
+ private
140
+ def log(severity, message)
141
+ raise ArgumentError, "Unknown severity #{severity.inspect}" unless InstanceAgent::Log::SEVERITIES.include?(severity.to_s)
142
+ InstanceAgent::Log.send(severity.to_sym, "#{description}: #{message}")
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end