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.
Files changed (84) hide show
  1. data/.gitignore +2 -0
  2. data/CHANGES.md +3 -0
  3. data/Gemfile +13 -0
  4. data/LICENSE +177 -0
  5. data/NOTICE +2 -0
  6. data/README.md +16 -0
  7. data/aws-codedeploy-agent.gemspec +39 -0
  8. data/bin/codedeploy-agent +78 -0
  9. data/bin/codedeploy-install +15 -0
  10. data/bin/codedeploy-uninstall +13 -0
  11. data/certs/host-agent-deployment-signer-ca-chain.pem +76 -0
  12. data/conf/codedeployagent.yml +9 -0
  13. data/init.d/codedeploy-agent +61 -0
  14. data/lib/core_ext.rb +71 -0
  15. data/lib/instance_agent.rb +35 -0
  16. data/lib/instance_agent/agent/base.rb +34 -0
  17. data/lib/instance_agent/codedeploy_plugin/application_specification/ace_info.rb +133 -0
  18. data/lib/instance_agent/codedeploy_plugin/application_specification/acl_info.rb +163 -0
  19. data/lib/instance_agent/codedeploy_plugin/application_specification/application_specification.rb +142 -0
  20. data/lib/instance_agent/codedeploy_plugin/application_specification/context_info.rb +23 -0
  21. data/lib/instance_agent/codedeploy_plugin/application_specification/file_info.rb +23 -0
  22. data/lib/instance_agent/codedeploy_plugin/application_specification/linux_permission_info.rb +121 -0
  23. data/lib/instance_agent/codedeploy_plugin/application_specification/mode_info.rb +66 -0
  24. data/lib/instance_agent/codedeploy_plugin/application_specification/range_info.rb +134 -0
  25. data/lib/instance_agent/codedeploy_plugin/application_specification/script_info.rb +27 -0
  26. data/lib/instance_agent/codedeploy_plugin/codedeploy_control.rb +72 -0
  27. data/lib/instance_agent/codedeploy_plugin/command_executor.rb +357 -0
  28. data/lib/instance_agent/codedeploy_plugin/command_poller.rb +146 -0
  29. data/lib/instance_agent/codedeploy_plugin/deployment_specification.rb +150 -0
  30. data/lib/instance_agent/codedeploy_plugin/hook_executor.rb +206 -0
  31. data/lib/instance_agent/codedeploy_plugin/install_instruction.rb +374 -0
  32. data/lib/instance_agent/codedeploy_plugin/installer.rb +143 -0
  33. data/lib/instance_agent/codedeploy_plugin/request_helper.rb +28 -0
  34. data/lib/instance_agent/config.rb +43 -0
  35. data/lib/instance_agent/log.rb +3 -0
  36. data/lib/instance_agent/platform.rb +17 -0
  37. data/lib/instance_agent/platform/linux_util.rb +57 -0
  38. data/lib/instance_agent/runner/child.rb +57 -0
  39. data/lib/instance_agent/runner/master.rb +103 -0
  40. data/lib/instance_metadata.rb +47 -0
  41. data/test/certificate_helper.rb +120 -0
  42. data/test/helpers/instance_agent_helper.rb +25 -0
  43. data/test/instance_agent/agent/base_test.rb +49 -0
  44. data/test/instance_agent/codedeploy_plugin/application_specification_test.rb +1710 -0
  45. data/test/instance_agent/codedeploy_plugin/codedeploy_control_test.rb +51 -0
  46. data/test/instance_agent/codedeploy_plugin/command_executor_test.rb +513 -0
  47. data/test/instance_agent/codedeploy_plugin/command_poller_test.rb +459 -0
  48. data/test/instance_agent/codedeploy_plugin/deployment_specification_test.rb +335 -0
  49. data/test/instance_agent/codedeploy_plugin/hook_executor_test.rb +250 -0
  50. data/test/instance_agent/codedeploy_plugin/install_instruction_test.rb +566 -0
  51. data/test/instance_agent/codedeploy_plugin/installer_test.rb +519 -0
  52. data/test/instance_agent/codedeploy_plugin/request_helper_test.rb +37 -0
  53. data/test/instance_agent/config_test.rb +64 -0
  54. data/test/instance_agent/runner/child_test.rb +87 -0
  55. data/test/instance_metadata_test.rb +97 -0
  56. data/test/test_helper.rb +16 -0
  57. data/vendor/gems/.codedeploy-commands-1.0.0.created.rid +1 -0
  58. data/vendor/gems/codedeploy-commands/apis/CodeDeployCommand.api.json +372 -0
  59. data/vendor/gems/codedeploy-commands/codedeploy-commands-1.0.0.gemspec +28 -0
  60. data/vendor/gems/codedeploy-commands/lib/aws/codedeploy_commands.rb +18 -0
  61. data/vendor/gems/codedeploy-commands/lib/aws/plugins/certificate_authority.rb +12 -0
  62. data/vendor/gems/codedeploy-commands/lib/aws/plugins/deploy_control_endpoint.rb +22 -0
  63. data/vendor/gems/process_manager/README.md +1 -0
  64. data/vendor/gems/process_manager/lib/blank.rb +153 -0
  65. data/vendor/gems/process_manager/lib/core_ext.rb +73 -0
  66. data/vendor/gems/process_manager/lib/process_manager.rb +49 -0
  67. data/vendor/gems/process_manager/lib/process_manager/child.rb +119 -0
  68. data/vendor/gems/process_manager/lib/process_manager/config.rb +112 -0
  69. data/vendor/gems/process_manager/lib/process_manager/log.rb +107 -0
  70. data/vendor/gems/process_manager/lib/process_manager/master.rb +322 -0
  71. data/vendor/gems/process_manager/process_manager-0.0.13.gemspec +42 -0
  72. data/vendor/specifications/aws-sdk-core-2.0.5.gemspec +39 -0
  73. data/vendor/specifications/builder-3.2.2.gemspec +29 -0
  74. data/vendor/specifications/codedeploy-commands-1.0.0.gemspec +28 -0
  75. data/vendor/specifications/gli-2.5.6.gemspec +51 -0
  76. data/vendor/specifications/jamespath-0.5.1.gemspec +35 -0
  77. data/vendor/specifications/little-plugger-1.1.3.gemspec +32 -0
  78. data/vendor/specifications/logging-1.8.1.gemspec +44 -0
  79. data/vendor/specifications/multi_json-1.7.7.gemspec +30 -0
  80. data/vendor/specifications/multi_json-1.8.4.gemspec +30 -0
  81. data/vendor/specifications/multi_xml-0.5.5.gemspec +30 -0
  82. data/vendor/specifications/process_manager-0.0.13.gemspec +42 -0
  83. data/vendor/specifications/simple_pid-0.2.1.gemspec +28 -0
  84. metadata +377 -0
@@ -0,0 +1,28 @@
1
+ require 'instance_metadata'
2
+
3
+ module InstanceAgent
4
+ module CodeDeployPlugin
5
+ class RequestHelper
6
+
7
+ def initialize(options = {})
8
+ @deploy_control_client = options[:deploy_control_client]
9
+ end
10
+
11
+ def verify_clients_configuration
12
+ errors = []
13
+ errors << "Invalid aws sdk security configuration" unless valid_aws_sdk_security_config?
14
+ errors << "Invalid server certificate" unless valid_server_certificate?
15
+ errors
16
+ end
17
+
18
+ def valid_aws_sdk_security_config?
19
+ @deploy_control_client.ssl_verify_peer
20
+ end
21
+
22
+ def valid_server_certificate?
23
+ @deploy_control_client.verify_cert_fields
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,43 @@
1
+ # encoding: UTF-8
2
+ require 'process_manager/config'
3
+
4
+ module InstanceAgent
5
+ class Config < ProcessManager::Config
6
+ def self.init
7
+ @config = Config.new
8
+ ProcessManager::Config.instance_variable_set("@config", @config)
9
+ end
10
+
11
+ def validate
12
+ errors = super
13
+ validate_children(errors)
14
+ errors
15
+ end
16
+
17
+ def initialize
18
+ super
19
+ @config.update({
20
+ :program_name => 'codedeploy-agent',
21
+ :wait_between_spawning_children => 1,
22
+ :log_dir => nil,
23
+ :pid_dir => nil,
24
+ :shared_dir => nil,
25
+ :user => nil,
26
+ :children => 1,
27
+ :http_read_timeout => 80,
28
+ :instance_service_region => nil,
29
+ :instance_service_endpoint => nil,
30
+ :instance_service_port => nil,
31
+ :wait_between_runs => 30,
32
+ :wait_after_error => 30,
33
+ :codedeploy_test_profile => 'prod'
34
+ })
35
+ end
36
+
37
+ def validate_children(errors = [])
38
+ errors << 'children can only be set to 1' unless config[:children] == 1
39
+ errors
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ require 'process_manager/log'
2
+
3
+ InstanceAgent::Log = ProcessManager::Log
@@ -0,0 +1,17 @@
1
+ module InstanceAgent
2
+
3
+ class Platform
4
+
5
+ attr_accessor :util
6
+
7
+ def self.util
8
+ @util
9
+ end
10
+
11
+ def self.util=(klass)
12
+ @util = klass
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,57 @@
1
+ module InstanceAgent
2
+ class LinuxUtil
3
+ def self.supported_versions()
4
+ [0.0]
5
+ end
6
+
7
+ def self.supported_oses()
8
+ ['linux']
9
+ end
10
+
11
+ def self.prepare_script_command(script, absolute_path)
12
+ script_command = absolute_path
13
+ if(!script.runas.nil?)
14
+ script_command = 'su ' + script.runas + ' -c ' + absolute_path
15
+ end
16
+ script_command
17
+ end
18
+
19
+ def self.script_executable?(path)
20
+ File.executable?(path)
21
+ end
22
+
23
+ def self.extract_tar(bundle_file, dst)
24
+ FileUtils.mkdir_p(dst)
25
+ execute_tar_command("/bin/tar -xpsf #{bundle_file} -C #{dst}")
26
+ end
27
+
28
+ def self.extract_tgz(bundle_file, dst)
29
+ FileUtils.mkdir_p(dst)
30
+ execute_tar_command("/bin/tar -zxpsf #{bundle_file} -C #{dst}")
31
+ end
32
+
33
+ private
34
+ def self.execute_tar_command(cmd)
35
+ log(:debug, "Executing #{cmd}")
36
+
37
+ output = `#{cmd} 2>&1`
38
+ exit_status = $?.exitstatus
39
+
40
+ log(:debug, "Command status: #{$?}")
41
+ log(:debug, "Command output: #{output}")
42
+
43
+ if exit_status != 0
44
+ msg = "Error extracting tar archive: #{exit_status}"
45
+ log(:error, msg)
46
+ raise msg
47
+ end
48
+ end
49
+
50
+ private
51
+ def self.log(severity, message)
52
+ raise ArgumentError, "Unknown severity #{severity.inspect}" unless InstanceAgent::Log::SEVERITIES.include?(severity.to_s)
53
+ InstanceAgent::Log.send(severity.to_sym, "#{self.to_s}: #{message}")
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,57 @@
1
+ # encoding: UTF-8
2
+ require 'process_manager/child'
3
+
4
+ module InstanceAgent
5
+ module Runner
6
+ class Child < ProcessManager::Daemon::Child
7
+ AGENTS = {
8
+ 0 => InstanceAgent::CodeDeployPlugin::CommandPoller
9
+ }
10
+
11
+ attr_accessor :runner
12
+
13
+ def prepare_run
14
+ validate_index
15
+ with_error_handling do
16
+ @runner = AGENTS[index].runner
17
+ ProcessManager.set_program_name(description)
18
+ end
19
+ end
20
+
21
+ def run
22
+ with_error_handling do
23
+ runner.run
24
+ end
25
+ end
26
+
27
+ def description
28
+ if runner
29
+ "#{runner.description} of master #{master_pid.inspect}"
30
+ else
31
+ 'booting child'
32
+ end
33
+ end
34
+
35
+ def validate_index
36
+ raise ArgumentError, "Invalid index #{index.inspect}: only 0-2 possible" unless AGENTS.keys.include?(index)
37
+ end
38
+
39
+ def with_error_handling
40
+ yield
41
+ rescue SocketError => e
42
+ ProcessManager::Log.info "#{description}: failed to run as the connection failed! #{e.class} - #{e.message} - #{e.backtrace.join("\n")}"
43
+ sleep ProcessManager::Config.config[:wait_after_connection_problem]
44
+ exit 1
45
+ rescue Exception => e
46
+ if (e.message.to_s.match(/throttle/i) || e.message.to_s.match(/rateexceeded/i) rescue false)
47
+ ProcessManager::Log.error "#{description}: ran into throttling - waiting for #{ProcessManager::Config.config[:wait_after_throttle_error]}s until retrying"
48
+ sleep ProcessManager::Config.config[:wait_after_throttle_error]
49
+ else
50
+ ProcessManager::Log.error "#{description}: error during start or run: #{e.class} - #{e.message} - #{e.backtrace.join("\n")}"
51
+ end
52
+ exit 1
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,103 @@
1
+ # encoding: UTF-8
2
+ require 'process_manager/master'
3
+ require 'instance_agent/codedeploy_plugin/request_helper'
4
+ require 'instance_agent/codedeploy_plugin/codedeploy_control'
5
+ require 'instance_metadata'
6
+
7
+ module InstanceAgent
8
+ module Runner
9
+ class Master < ProcessManager::Daemon::Master
10
+
11
+ ChildTerminationMaxWaitTime = 80
12
+
13
+ def self.description(pid = $$)
14
+ "master #{pid}"
15
+ end
16
+
17
+ def self.child_class
18
+ ::InstanceAgent::Runner::Child
19
+ end
20
+
21
+ def self.pid_description
22
+ ProcessManager::Config.config[:program_name]
23
+ end
24
+
25
+ def validate_ssl_config
26
+ if !InstanceMetadata.instance_id.blank?
27
+ region = InstanceMetadata.region
28
+ request_helper = InstanceAgent::CodeDeployPlugin::RequestHelper.new(:deploy_control_client => InstanceAgent::CodeDeployPlugin::CodeDeployControl.new(:region => region))
29
+
30
+ if (errors = request_helper.verify_clients_configuration)
31
+ errors.each{|error| ProcessManager::Log.error("Stopping CodeDeploy agent. " + error)}
32
+ end
33
+ self.class.abort unless errors.empty?
34
+ end
35
+ end
36
+
37
+ def self.log_file
38
+ File.join(ProcessManager::Config.config[:log_dir], "#{ProcessManager::Config.config[:program_name]}.log")
39
+ end
40
+
41
+ def self.pid_file
42
+ File.join(ProcessManager::Config.config[:pid_dir], "#{ProcessManager::Config.config[:program_name]}.pid")
43
+ end
44
+
45
+ def stop
46
+ if (pid = self.class.find_pid)
47
+ puts "Stopping #{description(pid)}"
48
+ ProcessManager::Log.info("Stopping #{description(pid)}")
49
+ begin
50
+ Process.kill('TERM', pid)
51
+ rescue Errno::ESRCH
52
+ end
53
+
54
+ begin
55
+ Timeout.timeout(ChildTerminationMaxWaitTime) do
56
+ loop do
57
+ begin
58
+ Process.kill(0, pid)
59
+ sleep(1)
60
+ rescue Errno::ESRCH
61
+ break
62
+ end
63
+ end
64
+ end
65
+ rescue Timeout::Error
66
+ puts "Child processes still running. Master going down."
67
+ ProcessManager::Log.warn("Master process (#{pid}) going down before terminating child")
68
+ end
69
+ else
70
+ puts "Nothing running that could be stopped"
71
+ end
72
+ end
73
+
74
+ def kill_children(sig)
75
+ children.each do |index, child_pid|
76
+ begin
77
+ Process.kill(sig, child_pid)
78
+ rescue Errno::ESRCH
79
+ end
80
+ end
81
+
82
+ begin
83
+ Timeout.timeout(ChildTerminationMaxWaitTime) do
84
+ children.each do |index, child_pid|
85
+ begin
86
+ Process.wait(child_pid)
87
+ rescue Errno::ESRCH
88
+ end
89
+ end
90
+ end
91
+ rescue Timeout::Error
92
+ children.each do |index, child_pid|
93
+ if ProcessManager.process_running?(child_pid)
94
+ puts "Stopping #{ProcessManager::Config.config[:program_name]} agent(#{pid}) but child(#{child_pid}) still processing."
95
+ ProcessManager::Log.warn("Stopping #{ProcessManager::Config.config[:program_name]} agent(#{pid}) but child(#{child_pid}) is still processing.")
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,47 @@
1
+ require 'net/http'
2
+ require 'json'
3
+ require 'instance_agent'
4
+
5
+ class InstanceMetadata
6
+
7
+ IP_ADDRESS = '169.254.169.254'
8
+ PORT = 80
9
+
10
+ def self.host_identifier
11
+ doc = JSON.parse(http_get('/latest/dynamic/instance-identity/document').strip)
12
+ "arn:aws:ec2:#{doc['region']}:#{doc['accountId']}:instance/#{doc['instanceId']}"
13
+ end
14
+
15
+ def self.region
16
+ az = http_get('/latest/meta-data/placement/availability-zone').strip
17
+ raise "Invalid availability zone name: #{az}" unless
18
+ az =~ /[a-z]{2}-[a-z]+-\d+[a-z]/
19
+ az.chop
20
+ end
21
+
22
+ def self.instance_id
23
+ begin
24
+ Net::HTTP.start(IP_ADDRESS, PORT) do |http|
25
+ response = http.get('/latest/meta-data/instance-id')
26
+ if response.code.to_i != 200
27
+ return nil
28
+ end
29
+ return response.body
30
+ end
31
+ rescue
32
+ return nil
33
+ end
34
+ end
35
+
36
+ private
37
+ def self.http_get(path)
38
+ Net::HTTP.start(IP_ADDRESS, PORT) do |http|
39
+ response = http.get(path)
40
+ if response.code.to_i != 200
41
+ InstanceAgent::Log.send(:debug, "HTTP error from metadata service, code #{reponse.code}")
42
+ raise "HTTP error from metadata service, code #{reponse.code}"
43
+ end
44
+ return response.body
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,120 @@
1
+ require 'openssl'
2
+ require 'tempfile'
3
+
4
+ class CertificateHelper
5
+ def initialize()
6
+ generate_root()
7
+ generate_intermediate()
8
+ generate_signer()
9
+
10
+ ca_chain_file = generate_ca_chain()
11
+
12
+ ENV['AWS_REGION'] = 'us-east-1'
13
+ InstanceAgent::CodeDeployPlugin::DeploymentSpecification.init_cert_store(ca_chain_file)
14
+ end
15
+
16
+ def generate_root()
17
+ @root_key = OpenSSL::PKey::RSA.new(1024)
18
+ @root_cert = OpenSSL::X509::Certificate.new
19
+ @root_cert.version = 2
20
+ @root_cert.serial = 1
21
+ @root_cert.subject = OpenSSL::X509::Name.new [
22
+ ['C', 'US'], ['ST', 'Washington'], ['L', 'Seattle'],
23
+ ['O', 'Amazon.com, Inc.'], ['CN', 'Host Agent TEST CA Root G1']
24
+ ]
25
+ @root_cert.issuer = @root_cert.subject
26
+ @root_cert.public_key = @root_key.public_key
27
+ @root_cert.not_before = Time.now
28
+ @root_cert.not_after = Time.now + 60
29
+ ef = OpenSSL::X509::ExtensionFactory.new
30
+
31
+ ef.subject_certificate = @root_cert
32
+ ef.issuer_certificate = @root_cert
33
+ @root_cert.extensions = [
34
+ ef.create_extension("basicConstraints","CA:TRUE",true),
35
+ ef.create_extension("keyUsage","keyCertSign, cRLSign", true),
36
+ ef.create_extension("subjectKeyIdentifier","hash",false),
37
+ ]
38
+
39
+ @root_cert.sign(@root_key, OpenSSL::Digest::SHA1.new)
40
+
41
+ return @root_cert
42
+ end
43
+
44
+ def generate_intermediate()
45
+ @intermediate_key = OpenSSL::PKey::RSA.new(1024)
46
+ @intermediate_cert = OpenSSL::X509::Certificate.new
47
+ @intermediate_cert.version = 2
48
+ @intermediate_cert.serial = 1
49
+ @intermediate_cert.subject = OpenSSL::X509::Name.new [
50
+ ['C', 'US'], ['ST', 'Washington'], ['L', 'Seattle'],
51
+ ['O', 'Amazon.com, Inc.'], ['CN', 'Host Agent TEST CA Intermediate G1']
52
+ ]
53
+ @intermediate_cert.issuer = @root_cert.subject
54
+ @intermediate_cert.public_key = @intermediate_key.public_key
55
+ @intermediate_cert.not_before = Time.now
56
+ @intermediate_cert.not_after = Time.now + 60
57
+
58
+ ef = OpenSSL::X509::ExtensionFactory.new
59
+ ef.subject_certificate = @intermediate_cert
60
+ ef.issuer_certificate = @root_cert
61
+ @intermediate_cert.extensions = [
62
+ ef.create_extension("basicConstraints","CA:TRUE",true),
63
+ ef.create_extension("keyUsage","keyCertSign, cRLSign", true),
64
+ ef.create_extension("subjectKeyIdentifier","hash",false)
65
+ ]
66
+
67
+ @intermediate_cert.sign(@root_key, OpenSSL::Digest::SHA1.new)
68
+
69
+ return @intermediate_cert
70
+ end
71
+
72
+ def generate_signer()
73
+ @signer_key = OpenSSL::PKey::RSA.new(1024)
74
+ @signer_cert = OpenSSL::X509::Certificate.new
75
+ @signer_cert.version = 2
76
+ @signer_cert.serial = 1
77
+ @signer_cert.subject = OpenSSL::X509::Name.new [
78
+ ['C', 'US'], ['ST', 'Washington'], ['L', 'Seattle'],
79
+ ['O', 'Amazon.com, Inc.'], ['CN', 'codedeploy-signer-us-east-1.amazonaws.com']
80
+ ]
81
+ @signer_cert.issuer = @intermediate_cert.subject
82
+ @signer_cert.public_key = @signer_key.public_key
83
+ @signer_cert.not_before = Time.now
84
+ @signer_cert.not_after = Time.now + 60
85
+
86
+ ef = OpenSSL::X509::ExtensionFactory.new
87
+ ef.subject_certificate = @signer_cert
88
+ ef.issuer_certificate = @intermediate_cert
89
+ @signer_cert.extensions = [
90
+ ef.create_extension('basicConstraints', 'CA:FALSE', true),
91
+ ef.create_extension('keyUsage', 'keyEncipherment,dataEncipherment,digitalSignature', true),
92
+ ef.create_extension('subjectKeyIdentifier', 'hash')
93
+ ]
94
+
95
+ @signer_cert.sign(@intermediate_key, OpenSSL::Digest::SHA1.new)
96
+
97
+ return @signer_cert
98
+ end
99
+
100
+ def generate_ca_chain()
101
+ ca_chain_file = Tempfile.new('host-agent-deployment-signer-ca-chain')
102
+
103
+ File.open(ca_chain_file.path, "wb") do |ca_chain|
104
+ ca_chain.print @root_cert.to_pem
105
+ ca_chain.print @intermediate_cert.to_pem
106
+ end
107
+
108
+ return ca_chain_file.path
109
+ end
110
+
111
+ def sign_message(message)
112
+ if @signer_key.nil?
113
+ raise "Signer key not initialized"
114
+ end
115
+
116
+ pkcs7 = OpenSSL::PKCS7::sign(@signer_cert, @signer_key, message, [], OpenSSL::PKCS7::BINARY)
117
+
118
+ return pkcs7.to_pem
119
+ end
120
+ end