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