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
@@ -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