• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

aws / aws-codedeploy-agent / 4600315001

pending completion
4600315001

push

github

GitHub
Updating latest version info for master branch

1129 of 2362 relevant lines covered (47.8%)

2.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

26.05
/lib/instance_agent/plugins/codedeploy/install_instruction.rb
1
require 'etc'
1✔
2
require 'fileutils'
1✔
3
require 'json'
1✔
4

5
module InstanceAgent
1✔
6
  module Plugins
1✔
7
    module CodeDeployPlugin
1✔
8
      class InstallInstruction
1✔
9
        def self.generate_commands_from_file(file)
1✔
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)
1✔
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 "The deployment failed because the application specification file specifies a role, but roles are not supported. Remove the role from the AppSpec file, and then try again."
×
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)
1✔
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()
1✔
67
          command_builder = CommandBuilder.new()
×
68
          yield(command_builder)
×
69
          command_builder
×
70
        end
71
      end
72

73
      class CommandBuilder
1✔
74
        attr_reader :command_array
1✔
75
        def initialize()
1✔
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)
1✔
83
          destination = sanitize_dir_path(destination)
×
84
          log(:debug, "Copying #{source} to #{destination}")
×
85
          raise "The deployment failed because the application specification file specifies two source files named #{source} and #{@copy_targets[destination]} for the same destination (#{destination}). Remove one of the source file paths from the AppSpec file, and then try again." if @copy_targets.has_key?(destination)
×
86
          raise "The deployment failed because the application specification file calls for installing the file #{source}, but a file with that name already exists at the location (#{destination}). Update your AppSpec file or directory structure, and then try again." if @mkdir_targets.include?(destination)
×
87
          @command_array << CopyCommand.new(source, destination)
×
88
          @copy_targets[destination] = source
×
89
        end
90

91
        def mkdir(destination)
1✔
92
          destination = sanitize_dir_path(destination)
×
93
          log(:debug, "Making directory #{destination}")
×
94
          raise "The deployment failed because the application specification file includes an mkdir command more than once for the same destination path (#{destination}) from (#{@copy_targets[destination]}). Update the files section of the AppSpec file, and then try again." 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)
1✔
100
          object = sanitize_dir_path(object)
×
101
          log(:debug, "Setting permissions on #{object}")
×
102
          raise "The deployment failed because the permissions setting for (#{object}) is specified more than once in the application specification file. Update the files section of the AppSpec file, and then try again." 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)
1✔
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)
1✔
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)
1✔
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
1✔
164
          command_json = @command_array.map(&:to_h)
×
165
          {:instructions => command_json}.to_json
×
166
        end
167

168
        def each(&block)
1✔
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()
1✔
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
1✔
185
        def sanitize_dir_path(path)
1✔
186
          File.expand_path(path)
×
187
        end
188

189
        private
1✔
190
        def description
1✔
191
          self.class.to_s
×
192
        end
193

194
        private
1✔
195
        def log(severity, message)
1✔
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
1✔
202
        def initialize(location)
1✔
203
          @file_path = location
×
204
        end
205

206
        def execute
1✔
207
          #If the file doesn't exist the command is ignored
208
          if File.symlink?(@file_path)
×
209
            FileUtils.rm(@file_path)
×
210
          elsif File.exist?(@file_path)
×
211
            if File.directory?(@file_path)
×
212
              begin
213
                FileUtils.rmdir(@file_path)
×
214
              rescue Errno::ENOTEMPTY
×
215
              end
216
            else
217
              FileUtils.rm(@file_path)
×
218
            end
219
          end
220
        end
221
      end
222

223
      class CopyCommand
1✔
224
        attr_reader :destination, :source
1✔
225
        def initialize(source, destination)
1✔
226
          @source = source
×
227
          @destination = destination
×
228
        end
229

230
        def execute(cleanup_file)
1✔
231
          # NO need to check if file already exists in here, because if that's the case,
232
          # the CopyCommand entry should not even be created by Installer
233
          cleanup_file.puts(@destination)
×
234
          if File.symlink?(@source)
×
235
            FileUtils.symlink(File.readlink(@source), @destination)
×
236
          else
237
            FileUtils.copy(@source, @destination, :preserve => true)
×
238
          end
239
        end
240

241
        def to_h
1✔
242
          {:type => :copy, :source => @source, :destination => @destination}
×
243
        end
244
      end
245

246
      class MakeDirectoryCommand
1✔
247
        def initialize(destination)
1✔
248
          @directory = destination
×
249
        end
250

251
        def execute(cleanup_file)
1✔
252
          # NO need to check if file already exists in here, because if that's the case,
253
          # the MakeDirectoryCommand entry should not even be created by Installer
254
          FileUtils.mkdir(@directory)
×
255
          cleanup_file.puts(@directory)
×
256
        end
257

258
        def to_h
1✔
259
          {:type => :mkdir, :directory => @directory}
×
260
        end
261
      end
262

263
      class ChangeModeCommand
1✔
264
        def initialize(object, mode)
1✔
265
          @object = object
×
266
          @mode = mode
×
267
        end
268

269
        def execute(cleanup_file)
1✔
270
          File.chmod(@mode.to_i(8), @object)
×
271
        end
272

273
        def to_h
1✔
274
          {:type => :chmod, :mode => @mode, :file => @object}
×
275
        end
276
      end
277

278
      class ChangeAclCommand
1✔
279
        def initialize(object, acl)
1✔
280
          @object = object
×
281
          @acl = acl
×
282
        end
283

284
        def execute(cleanup_file)
1✔
285
          begin
286
            get_full_acl
×
287
            acl = @acl.get_acl.join(",")
×
288
            if !system("setfacl --set #{acl} #{@object}")
×
289
              raise "The deployment failed because of a problem with the acls permission settings in the application specification file for this object: #{@object}. Failed command: setfacl --set #{acl} #{@object}. Exit code: #{$?}"
×
290
            end
291
          ensure
292
            clear_full_acl
×
293
          end
294
        end
295

296
        def clear_full_acl
1✔
297
          @acl.clear_additional
×
298
        end
299

300
        def get_full_acl()
1✔
301
          perm = "%o" % File.stat(@object).mode
×
302
          perm = perm[-3,3]
×
303
          @acl.add_ace(":#{perm[0]}")
×
304
          @acl.add_ace("g::#{perm[1]}")
×
305
          @acl.add_ace("o::#{perm[2]}")
×
306
          if @acl.has_base_named? && !@acl.has_base_mask?
×
307
            @acl.add_ace("m::#{perm[1]}")
×
308
          end
309
          if @acl.has_default?
×
310
            if !@acl.has_default_user?
×
311
              @acl.add_ace("d::#{perm[0]}")
×
312
            end
313
            if !@acl.has_default_group?
×
314
              @acl.add_ace("d:g::#{perm[1]}")
×
315
            end
316
            if !@acl.has_default_other?
×
317
              @acl.add_ace("d:o:#{perm[2]}")
×
318
            end
319
            if @acl.has_default_named? && !@acl.has_default_mask?
×
320
              @acl.add_ace(@acl.get_default_group_ace.sub("group:","mask"))
×
321
            end
322
          end
323
        end
324

325
        def to_h
1✔
326
          {:type => :setfacl, :acl => @acl.get_acl, :file => @object}
×
327
        end
328
      end
329

330
      class ChangeOwnerCommand
1✔
331
        def initialize(object, owner, group)
1✔
332
          @object = object
×
333
          @owner = owner
×
334
          @group = group
×
335
        end
336

337
        def execute(cleanup_file)
1✔
338
          ownerid = Etc.getpwnam(@owner).uid if @owner
×
339
          groupid = Etc.getgrnam(@group).gid if @group
×
340
          File.chown(ownerid, groupid, @object)
×
341
        end
342

343
        def to_h
1✔
344
          {:type => :chown, :owner => @owner, :group => @group, :file => @object}
×
345
        end
346
      end
347

348
      class ChangeContextCommand
1✔
349
        def initialize(object, context)
1✔
350
          @object = object
×
351
          @context = context
×
352
        end
353

354
        def execute(cleanup_file)
1✔
355
          if !@context.role.nil?
×
356
            raise "The deployment failed because the application specification file specifies a role, but roles are not supported. Remove the role from the AppSpec file, and then try again."
×
357
          end
358
          args = "-t #{@context.type}"
×
359
          if (!@context.user.nil?)
×
360
            args = "-s #{@context.user} " + args
×
361
          end
362
          if (!@context.range.nil?)
×
363
            args = args + " -r #{@context.range.get_range}"
×
364
          end
365

366
          object = File.realpath(@object)
×
367
          if !system("semanage fcontext -a #{args} #{object}")
×
368
            raise "The deployment failed because the application specification file contains an error in the settings for the context parameter. Update the permissions section of the AppSpec file, and then try again. Failed command: semanage fcontext -a #{args} #{object}. Exit code: #{$?}"
×
369
          end
370
          if !system("restorecon -v #{object}")
×
371
            raise "The deployment failed because the application specification file contains an error in the settings for the context parameter. Update the permissions section of the AppSpec file, and then try again. Failed command: restorecon -v #{object}. Exit code: #{$?}"
×
372
          end
373
          cleanup_file.puts("semanage\0#{object}")
×
374
        end
375

376
        def to_h
1✔
377
          {:type => :semanage, :context => {:user => @context.user, :role => @context.role, :type => @context.type, :range => @context.range.get_range}, :file => @object}
×
378
        end
379
      end
380

381
      class RemoveContextCommand
1✔
382
        def initialize(object)
1✔
383
          @object = object
×
384
        end
385

386
        def execute
1✔
387
          system("semanage fcontext -d #{@object}")
×
388
        end
389
      end
390
    end
391
  end
392
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc