cf_deployer 1.2.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +29 -0
  3. data/ChangeLog.md +16 -0
  4. data/DETAILS.md +268 -0
  5. data/FAQ.md +61 -0
  6. data/Gemfile +10 -0
  7. data/Gemfile.lock +51 -0
  8. data/LICENSE +22 -0
  9. data/QUICKSTART.md +96 -0
  10. data/README.md +36 -0
  11. data/Rakefile +32 -0
  12. data/bin/cf_deploy +10 -0
  13. data/cf_deployer.gemspec +23 -0
  14. data/lib/cf_deployer/application.rb +74 -0
  15. data/lib/cf_deployer/application_error.rb +4 -0
  16. data/lib/cf_deployer/aws_constants.rb +3 -0
  17. data/lib/cf_deployer/cli.rb +111 -0
  18. data/lib/cf_deployer/component.rb +103 -0
  19. data/lib/cf_deployer/config_loader.rb +189 -0
  20. data/lib/cf_deployer/config_validation.rb +138 -0
  21. data/lib/cf_deployer/defaults.rb +10 -0
  22. data/lib/cf_deployer/deployment_strategy/auto_scaling_group_swap.rb +102 -0
  23. data/lib/cf_deployer/deployment_strategy/base.rb +88 -0
  24. data/lib/cf_deployer/deployment_strategy/blue_green.rb +70 -0
  25. data/lib/cf_deployer/deployment_strategy/cname_swap.rb +108 -0
  26. data/lib/cf_deployer/deployment_strategy/create_or_update.rb +57 -0
  27. data/lib/cf_deployer/driver/auto_scaling_group.rb +86 -0
  28. data/lib/cf_deployer/driver/cloud_formation_driver.rb +85 -0
  29. data/lib/cf_deployer/driver/dry_run.rb +27 -0
  30. data/lib/cf_deployer/driver/elb_driver.rb +17 -0
  31. data/lib/cf_deployer/driver/instance.rb +29 -0
  32. data/lib/cf_deployer/driver/route53_driver.rb +79 -0
  33. data/lib/cf_deployer/driver/verisign_driver.rb +21 -0
  34. data/lib/cf_deployer/hook.rb +32 -0
  35. data/lib/cf_deployer/logger.rb +34 -0
  36. data/lib/cf_deployer/stack.rb +154 -0
  37. data/lib/cf_deployer/status_presenter.rb +195 -0
  38. data/lib/cf_deployer/version.rb +3 -0
  39. data/lib/cf_deployer.rb +97 -0
  40. data/spec/fakes/instance.rb +32 -0
  41. data/spec/fakes/route53_client.rb +23 -0
  42. data/spec/fakes/stack.rb +65 -0
  43. data/spec/functional/deploy_spec.rb +73 -0
  44. data/spec/functional/kill_inactive_spec.rb +57 -0
  45. data/spec/functional_spec_helper.rb +3 -0
  46. data/spec/spec_helper.rb +8 -0
  47. data/spec/unit/application_spec.rb +191 -0
  48. data/spec/unit/component_spec.rb +142 -0
  49. data/spec/unit/config_loader_spec.rb +356 -0
  50. data/spec/unit/config_validation_spec.rb +480 -0
  51. data/spec/unit/deployment_strategy/auto_scaling_group_swap_spec.rb +435 -0
  52. data/spec/unit/deployment_strategy/base_spec.rb +44 -0
  53. data/spec/unit/deployment_strategy/cname_swap_spec.rb +294 -0
  54. data/spec/unit/deployment_strategy/create_or_update_spec.rb +113 -0
  55. data/spec/unit/deployment_strategy/deployment_strategy_spec.rb +29 -0
  56. data/spec/unit/driver/auto_scaling_group_spec.rb +127 -0
  57. data/spec/unit/driver/cloud_formation_spec.rb +32 -0
  58. data/spec/unit/driver/elb_spec.rb +11 -0
  59. data/spec/unit/driver/instance_spec.rb +30 -0
  60. data/spec/unit/driver/route53_spec.rb +85 -0
  61. data/spec/unit/driver/verisign_spec.rb +18 -0
  62. data/spec/unit/hook_spec.rb +64 -0
  63. data/spec/unit/stack_spec.rb +150 -0
  64. data/spec/unit/status_presenter_spec.rb +108 -0
  65. metadata +197 -0
@@ -0,0 +1,79 @@
1
+ module CfDeployer
2
+ module Driver
3
+ class Route53
4
+ def initialize(aws_route53 = nil)
5
+ @aws_route53 = aws_route53 || AWS::Route53.new
6
+ end
7
+
8
+ def find_alias_target(target_zone_name, target_host_name)
9
+ target_zone = @aws_route53.hosted_zones.find { |z| z.name == trailing_dot(target_zone_name.downcase) }
10
+ raise ApplicationError.new('Target zone not found!') if target_zone.nil?
11
+
12
+ target_host = target_zone.resource_record_sets.find { |r| r.name == trailing_dot(target_host_name.downcase) }
13
+ return nil if target_host.nil? || target_host.alias_target.nil?
14
+
15
+ remove_trailing_dot(target_host.alias_target[:dns_name])
16
+ end
17
+
18
+ def set_alias_target(target_zone_name, target_host_name, elb_hosted_zone_id, elb_dnsname)
19
+ Log.info "set alias target --Hosted Zone: #{target_zone_name} --Host Name: #{target_host_name} --ELB DNS Name: #{elb_dnsname} --ELB Zone ID: #{elb_hosted_zone_id}"
20
+ target_zone_name = trailing_dot(target_zone_name)
21
+ target_host_name = trailing_dot(target_host_name)
22
+ target_zone = @aws_route53.hosted_zones.find { |z| z.name == target_zone_name }
23
+ raise ApplicationError.new('Target zone not found!') if target_zone.nil?
24
+
25
+ change = {
26
+ action: "UPSERT",
27
+ resource_record_set: {
28
+ name: target_host_name,
29
+ type: "A",
30
+ alias_target: {
31
+ dns_name: elb_dnsname,
32
+ hosted_zone_id: elb_hosted_zone_id,
33
+ evaluate_target_health: false
34
+ }
35
+ }
36
+ }
37
+
38
+ batch = {
39
+ hosted_zone_id: target_zone.path,
40
+ change_batch: {
41
+ changes: [change]
42
+ }
43
+ }
44
+
45
+ CfDeployer::Driver::DryRun.guard "Skipping Route53 DNS update" do
46
+ change_resource_record_sets_with_retry(batch)
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def change_resource_record_sets_with_retry(batch)
53
+ attempts = 0
54
+ while attempts < 20
55
+ begin
56
+ attempts = attempts + 1
57
+ @aws_route53.client.change_resource_record_sets(batch)
58
+ return
59
+ rescue Exception => e
60
+ Log.info "Failed to update alias target, trying again in 20 seconds."
61
+ sleep(20)
62
+ end
63
+ end
64
+
65
+ raise ApplicationError.new('Failed to update Route53 alias target record!')
66
+ end
67
+
68
+ def trailing_dot(text)
69
+ return text if text[-1] == '.'
70
+ "#{text}."
71
+ end
72
+
73
+ def remove_trailing_dot(text)
74
+ return text[0..-2] if text && text[-1] == '.'
75
+ text
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,21 @@
1
+ ## To use this driver instead of Route53 (the default), use the setting 'dns-driver'
2
+
3
+ module CfDeployer
4
+ module Driver
5
+ class Verisign
6
+
7
+ def find_alias_target dns_zone, dns_fqdn
8
+ raise "Not Implemented"
9
+ end
10
+
11
+ def set_alias_target dns_zone, dns_fqdn, elb_hosted_zone_id, elb_dnsname
12
+ raise "Not Implemented"
13
+
14
+ CfDeployer::Driver::DryRun.guard "Skipping Verisign DNS update" do
15
+ # do update here
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ module CfDeployer
2
+ class Hook
3
+ def initialize(name, body)
4
+ @name = name
5
+ @body = body
6
+ end
7
+
8
+ def run(context)
9
+ case @body
10
+ when Hash
11
+ if @body[:file]
12
+ file = File.expand_path(@body[:file], context[:config_dir])
13
+ execute(File.read(file), context, @body[:timeout])
14
+ else
15
+ execute(@body[:code], context, @body[:timeout])
16
+ end
17
+ when String
18
+ execute(@body, context)
19
+ end
20
+ end
21
+
22
+ private
23
+ def execute(hook, context, timeout = nil)
24
+ CfDeployer::Log.info("Running hook #{@name}")
25
+ context = context.dup
26
+ timeout = timeout || Defaults::Timeout
27
+ Timeout.timeout(timeout.to_f) do
28
+ eval(hook, binding)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,34 @@
1
+ module CfDeployer
2
+ class Log
3
+ require 'log4r'
4
+ include Log4r
5
+
6
+ def self.debug(message)
7
+ log.debug message
8
+ end
9
+
10
+ def self.info(message)
11
+ log.info message
12
+ end
13
+
14
+ def self.log
15
+ return @log if @log
16
+ @log = Logger.new('cf_deployer')
17
+ outputter = Outputter.stdout
18
+ outputter.formatter = PatternFormatter.new(:pattern => "%d [%l] (%c) %M", :date_pattern => "%y-%m-%d %H:%M:%S")
19
+ @log.outputters = outputter
20
+ @log.level = Log4r::INFO
21
+ @log
22
+ end
23
+
24
+ def self.level=(trace_level)
25
+ trace_level ||= 'info'
26
+ case trace_level.downcase
27
+ when 'debug'
28
+ log.level = Log4r::DEBUG
29
+ else
30
+ log.level = Log4r::INFO
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,154 @@
1
+ module CfDeployer
2
+ class ResourceNotInReadyState < ApplicationError
3
+ end
4
+
5
+ class Stack
6
+ SUCCESS_STATS = [:create_complete, :update_complete, :update_rollback_complete, :delete_complete]
7
+ READY_STATS = SUCCESS_STATS - [:delete_complete]
8
+ FAILED_STATS = [:create_failed, :update_failed, :delete_failed]
9
+
10
+
11
+ def initialize(stack_name, component, context)
12
+ @stack_name = stack_name
13
+ @cf_driver = context[:cf_driver] || CfDeployer::Driver::CloudFormation.new(stack_name)
14
+ @context = context
15
+ @component = component
16
+ end
17
+
18
+ def deploy
19
+ config_dir = @context[:config_dir]
20
+ template = CfDeployer::ConfigLoader.component_json(@component, @context)
21
+ capabilities = @context[:capabilities] || []
22
+ tags = @context[:tags] || []
23
+ params = to_str(@context[:inputs].select{|key, value| @context[:defined_parameters].keys.include?(key)})
24
+ CfDeployer::Driver::DryRun.guard "Skipping deploy" do
25
+ exists? ? update_stack(template, params, capabilities, tags) : create_stack(template, params, capabilities, tags)
26
+ end
27
+ end
28
+
29
+ def outputs
30
+ return {} unless ready?
31
+ @cf_driver.outputs
32
+ end
33
+
34
+ def parameters
35
+ return {} unless ready?
36
+ @cf_driver.parameters
37
+ end
38
+
39
+ def output key
40
+ begin
41
+ @cf_driver.query_output(key) || (raise ApplicationError.new("'#{key}' is empty from stack #{name} output"))
42
+ rescue AWS::CloudFormation::Errors::ValidationError => e
43
+ raise ResourceNotInReadyState.new("Resource stack not in ready state yet, perhaps you should provision it first?")
44
+ end
45
+ end
46
+
47
+ def delete
48
+ if exists?
49
+ CfDeployer::Driver::DryRun.guard "Skipping delete" do
50
+ Log.info "deleting stack #{@stack_name}"
51
+ @cf_driver.delete_stack
52
+ wait_for_stack_to_delete
53
+ end
54
+ end
55
+ end
56
+
57
+ def exists?
58
+ @cf_driver.stack_exists?
59
+ end
60
+
61
+ def ready?
62
+ READY_STATS.include? @cf_driver.stack_status
63
+ end
64
+
65
+ def status
66
+ if exists?
67
+ ready? ? :ready : :exists
68
+ else
69
+ :does_not_exist
70
+ end
71
+ end
72
+
73
+ def resource_statuses
74
+ AWS.memoize do
75
+ resources = @cf_driver.resource_statuses.merge( { :asg_instances => {}, :instances => {} } )
76
+ if resources['AWS::AutoScaling::AutoScalingGroup']
77
+ resources['AWS::AutoScaling::AutoScalingGroup'].keys.each do |asg_name|
78
+ resources[:asg_instances][asg_name] = CfDeployer::Driver::AutoScalingGroup.new(asg_name).instance_statuses
79
+ end
80
+ end
81
+ if resources['AWS::EC2::Instance']
82
+ resources['AWS::EC2::Instance'].keys.each do |instance_id|
83
+ resources[:instances][instance_id] = CfDeployer::Driver::Instance.new(instance_id).status
84
+ end
85
+ end
86
+ resources
87
+ end
88
+ end
89
+
90
+ def name
91
+ @stack_name
92
+ end
93
+
94
+
95
+ private
96
+
97
+ def to_str(hash)
98
+ hash.each { |k,v| hash[k] = v.to_s }
99
+ end
100
+
101
+ def update_stack(template, params, capabilities, tags)
102
+ Log.info "Updating stack #{@stack_name}..."
103
+ @cf_driver.update_stack template,
104
+ :capabilities => capabilities,
105
+ :parameters => params
106
+ wait_for_stack_op_terminate
107
+ end
108
+
109
+ def create_stack(template, params, capabilities, tags)
110
+ Log.info "Creating stack #{@stack_name}..."
111
+ @cf_driver.create_stack template,
112
+ :disable_rollback => true,
113
+ :capabilities => capabilities,
114
+ :tags => reformat_tags(tags),
115
+ :parameters => params
116
+ wait_for_stack_op_terminate
117
+ end
118
+
119
+ def stack_status
120
+ @cf_driver.stack_status
121
+ end
122
+
123
+ def wait_for_stack_op_terminate
124
+ stats = stack_status
125
+ while !SUCCESS_STATS.include?(stats)
126
+ sleep 15
127
+ stats = stack_status
128
+ raise ApplicationError.new("Resource stack update failed!") if FAILED_STATS.include? stats
129
+ Log.info "current status: #{stack_status}"
130
+ end
131
+ end
132
+
133
+ def wait_for_stack_to_delete
134
+ Timeout::timeout(900){
135
+ while exists?
136
+ begin
137
+ Log.info "current status: #{stack_status}"
138
+ sleep 15
139
+ rescue AWS::CloudFormation::Errors::ValidationError => e
140
+ if e.message =~ /does not exist/
141
+ break # This is what we wanted anyways
142
+ else
143
+ raise e
144
+ end
145
+ end
146
+ end
147
+ }
148
+ end
149
+
150
+ def reformat_tags tags_hash
151
+ tags_hash.keys.map { |key| { 'Key' => key.to_s, 'Value' => tags_hash[key].to_s } }
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,195 @@
1
+ require 'rainbow'
2
+ require 'rainbow/ext/string'
3
+
4
+ module CfDeployer
5
+ class StatusPresenter
6
+
7
+ VERBOSITY_3_SKIP = ['AWS::AutoScaling::AutoScalingGroup','AWS::EC2::Instance',:asg_instances, :instances]
8
+
9
+ PAD = " "
10
+ UNPADDED_TABLE_CELL_WIDTH = 85
11
+
12
+ def initialize status_info, verbosity
13
+ @verbosity = verbosity
14
+ @info = status_info
15
+ @output = []
16
+ end
17
+
18
+ def to_json
19
+ filter_for_verbosity(@info).to_json
20
+ end
21
+
22
+ def output
23
+ @output << table_seperator
24
+ @info.each do |component_name, stacks_hash|
25
+ @output << "\n#{centered(component_name.upcase)}\n"
26
+ @output << table_seperator
27
+ stack_cells = []
28
+ stacks_hash.each do |stack_name, stack_hash|
29
+ stack_output = ['']
30
+
31
+ stack_output << PAD + [ colorized_stack_name(stack_name, stack_hash),
32
+ stack_active_str(stack_hash[:active]).ljust(15),
33
+ stack_hash[:status].capitalize
34
+ ].join(PAD)
35
+
36
+ if stack_hash[:resources] && @verbosity != 'stacks'
37
+ instances_status stack_output, component_name, stack_name, stack_hash[:resources][:instances], false
38
+ asgs_status stack_output, component_name, stack_name, stack_hash[:resources][:asg_instances]
39
+ resource_status stack_output, stack_hash[:resources] if @verbosity == 'all'
40
+ end
41
+ stack_output << ''
42
+ stack_cells << stack_output
43
+ end
44
+ stack_cells[1] ||= ['']
45
+ @output += tableize( stack_cells )
46
+ end
47
+ @output.join "\n"
48
+ end
49
+
50
+ private
51
+
52
+ def filter_for_verbosity info_hash
53
+ if @verbosity == 'stacks'
54
+ info_hash.each do |component, component_hash|
55
+ component_hash.each { |stack, stack_hash| stack_hash.delete :resources }
56
+ end
57
+ elsif @verbosity == 'instances'
58
+ info_hash.each do |component, component_hash|
59
+ component_hash.each do |stack, stack_hash|
60
+ if stack_hash[:resources]
61
+ stack_hash[:resources].select! do |resource_type, resources|
62
+ [:instances, :asg_instances].include? resource_type
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ info_hash
69
+ end
70
+
71
+ def colorized_stack_name stack_name, stack_hash
72
+ stack_color = case stack_name.split('').last
73
+ when 'B' then :cyan
74
+ when 'G' then :green
75
+ else :white
76
+ end
77
+ colorized_stack_name = " #{stack_name} ".color(stack_color).bright
78
+ stack_hash[:active] ? colorized_stack_name.inverse : colorized_stack_name
79
+ end
80
+
81
+ def stack_active_str active
82
+ case active
83
+ when true then 'Active'
84
+ when false then 'Inactive'
85
+ else ''
86
+ end
87
+ end
88
+
89
+ def asgs_status output, component_name, stack_name, asg_hash
90
+ return if asg_hash.empty?
91
+ output << ''
92
+ output << "#{PAD * 2}AutoScalingGroups:"
93
+ asg_hash.each do |asg_name, asg_instances|
94
+ asg_color = status_color @info[component_name][stack_name][:resources]['AWS::AutoScaling::AutoScalingGroup'][asg_name]
95
+ output << ''
96
+ output << "#{PAD * 3}#{ Rainbow(asg_name).color asg_color }"
97
+ instances_status output, component_name, stack_name, asg_instances, true
98
+ end
99
+ end
100
+
101
+ def instances_status output, component_name, stack_name, instances_hash, in_asg
102
+ pad = PAD * 2
103
+ output << ''
104
+ output << "#{pad}Instances:" if (instances_hash.any? && !in_asg)
105
+ instances_hash.each do |instance_id, instance|
106
+ instance_pad = in_asg ? pad + PAD : pad
107
+ instance_color = instance_status_color instance[:status]
108
+ instance_line_parts = [ Rainbow(instance_id).color(instance_color),
109
+ instance[:public_ip_address],
110
+ instance[:private_ip_address],
111
+ instance[:image_id],
112
+ instance[:key_pair]
113
+ ]
114
+ output << "#{PAD}#{instance_pad}" + instance_line_parts.join(PAD)
115
+ end
116
+ end
117
+
118
+ def resource_status output, resource_hash
119
+ resources_to_report = resource_hash.reject { |resource_type| VERBOSITY_3_SKIP.include? resource_type }
120
+ max_length = resources_to_report.map { |rtype, r| r.keys }.flatten.group_by(&:size).max.last.first.size
121
+ new_max = [ max_length, (UNPADDED_TABLE_CELL_WIDTH - 17 - (PAD.size * 4))].sort.first
122
+
123
+ resources_to_report.each do |resource_type, resources|
124
+ output << ''
125
+ output << "#{PAD * 2}#{resource_type.split('::').last}"
126
+ resources.each do |resource_id, resource_status|
127
+ truncated_id = middle_truncate_ljust(resource_id, new_max).color(status_color(resource_status))
128
+ output << "#{PAD * 3}#{truncated_id}#{PAD}#{resource_status}"
129
+ end
130
+ end
131
+ end
132
+
133
+ def instance_status_color status
134
+ CfDeployer::Driver::Instance::GOOD_STATUSES.include?(status) ? :green : :red
135
+ end
136
+
137
+ def status_color status
138
+ status = status.downcase.to_sym
139
+ if CfDeployer::Stack::READY_STATS.include? status
140
+ :green
141
+ elsif CfDeployer::Stack::FAILED_STATS.include? status
142
+ :red
143
+ else
144
+ :white
145
+ end
146
+ end
147
+
148
+ def tableize stack_cells
149
+ my_output = []
150
+
151
+ (col1, col2) = stack_cells
152
+ rows = stack_cells.map(&:size).max
153
+
154
+ rows.times do |i|
155
+ col1[i] ||= ''
156
+ col2[i] ||= ''
157
+
158
+ line = ''
159
+ line << col1[i].ljust(UNPADDED_TABLE_CELL_WIDTH + PAD.size + invisible_length(col1[i]))
160
+ line << '|'
161
+ line << col2[i]
162
+ my_output << line
163
+ end
164
+ my_output << table_seperator
165
+ my_output
166
+ end
167
+
168
+ def middle_truncate_ljust str, len
169
+ return str.ljust(len) if str.size <= len
170
+
171
+ replace_start = (len / 2).to_i - 4
172
+ replace_end = str.size - (len / 2).to_i
173
+ truncated = str[0..replace_start] + '...' + str[replace_end..str.size]
174
+ truncated.ljust len
175
+ end
176
+
177
+ def invisible_length str
178
+ str.size - visible_length(str)
179
+ end
180
+
181
+ def visible_length str
182
+ str.gsub(/\e\[[\d;]+m/,'').size
183
+ end
184
+
185
+ def table_seperator
186
+ "-" * (UNPADDED_TABLE_CELL_WIDTH + PAD.size) * 2
187
+ end
188
+
189
+ def centered the_string
190
+ width = (UNPADDED_TABLE_CELL_WIDTH + PAD.size) + (the_string.size / 2).to_i
191
+ the_string.rjust width
192
+ end
193
+
194
+ end
195
+ end
@@ -0,0 +1,3 @@
1
+ module CfDeployer
2
+ VERSION = "1.2.8"
3
+ end
@@ -0,0 +1,97 @@
1
+ require 'digest'
2
+ require 'set'
3
+ require 'time'
4
+ require 'json'
5
+ require 'timeout'
6
+ require 'aws-sdk'
7
+ require 'erb'
8
+ require 'fileutils'
9
+ require 'log4r'
10
+ require 'pp'
11
+ require 'forwardable'
12
+
13
+ require_relative 'cf_deployer/application_error'
14
+ require_relative 'cf_deployer/cli'
15
+ require_relative 'cf_deployer/application'
16
+ require_relative 'cf_deployer/aws_constants'
17
+ require_relative 'cf_deployer/component'
18
+ require_relative 'cf_deployer/config_loader'
19
+ require_relative 'cf_deployer/config_validation'
20
+ require_relative 'cf_deployer/stack'
21
+ require_relative 'cf_deployer/version'
22
+ require_relative 'cf_deployer/status_presenter'
23
+ require_relative 'cf_deployer/deployment_strategy/base'
24
+ require_relative 'cf_deployer/deployment_strategy/blue_green'
25
+ require_relative 'cf_deployer/deployment_strategy/auto_scaling_group_swap'
26
+ require_relative 'cf_deployer/deployment_strategy/cname_swap'
27
+ require_relative 'cf_deployer/deployment_strategy/create_or_update'
28
+ require_relative 'cf_deployer/driver/auto_scaling_group'
29
+ require_relative 'cf_deployer/driver/cloud_formation_driver'
30
+ require_relative 'cf_deployer/driver/dry_run'
31
+ require_relative 'cf_deployer/driver/elb_driver'
32
+ require_relative 'cf_deployer/driver/instance'
33
+ require_relative 'cf_deployer/driver/route53_driver'
34
+ require_relative 'cf_deployer/driver/verisign_driver'
35
+ require_relative 'cf_deployer/logger'
36
+ require_relative 'cf_deployer/hook'
37
+ require_relative 'cf_deployer/defaults'
38
+
39
+ module CfDeployer
40
+
41
+ AWS.config(:max_retries => 5)
42
+
43
+ def self.config opts
44
+ config = self.parseconfig opts, false
45
+ config[:components].each do |component, c_hash|
46
+ c_hash.delete :defined_parameters
47
+ end
48
+ puts config.select { |k,v| [:components, :environments, :environment, :application, :'config-file'].include? k.to_sym }.to_yaml
49
+ end
50
+
51
+ def self.deploy opts
52
+ config = self.parseconfig opts
53
+ # AWS.config(:logger => Logger.new($stdout))
54
+ Application.new(config).deploy
55
+ end
56
+
57
+ def self.destroy opts
58
+ config = self.parseconfig opts, false
59
+ # AWS.config(:logger => Logger.new($stdout))
60
+ Application.new(config).destroy
61
+ end
62
+
63
+ def self.json opts
64
+ config = self.parseconfig opts, false
65
+ Application.new(config).json
66
+ end
67
+
68
+ def self.status opts
69
+ config = self.parseconfig opts, false
70
+ status_info = Application.new(config).status opts[:component].first, opts[:verbosity]
71
+ presenter = CfDeployer::StatusPresenter.new status_info, opts[:verbosity]
72
+
73
+ puts opts[:'output-format'] == 'json' ? presenter.to_json : presenter.output
74
+ end
75
+
76
+ def self.switch opts
77
+ config = self.parseconfig opts, false
78
+ Application.new(config).switch
79
+ end
80
+
81
+ def self.kill_inactive opts
82
+ config = self.parseconfig opts, false
83
+ Application.new(config).kill_inactive
84
+ end
85
+
86
+ private
87
+
88
+ def self.parseconfig options, validate_inputs = true
89
+ AWS.config(:region => options[:region]) if options[:region]
90
+ split_settings = options.dup
91
+ split_settings.merge!({:cli_overrides => {:settings => split_settings.delete(:settings), :inputs => split_settings.delete(:inputs)} })
92
+ config = ConfigLoader.new.load split_settings
93
+ ConfigValidation.new.validate config, validate_inputs
94
+ config
95
+ end
96
+
97
+ end
@@ -0,0 +1,32 @@
1
+ module Fakes
2
+ class Instance
3
+
4
+ SIMPLE_ATTRIBS = [ :id, :status, :public_ip_address, :private_ip_address, :image_id ]
5
+
6
+ attr_reader *SIMPLE_ATTRIBS
7
+ attr_reader :key_pair
8
+
9
+ def initialize(options)
10
+ defaults = {
11
+ :id => 'i-abcd1234',
12
+ :status => :running,
13
+ :public_ip_address => '1.2.3.4',
14
+ :private_ip_address => '192.168.100.200',
15
+ :image_id => 'ami-abcd1234',
16
+ :key_pair => 'awesome_users'
17
+ }
18
+
19
+ SIMPLE_ATTRIBS.each do |attrib|
20
+ instance_variable_set "@#{attrib}", (options[attrib] || defaults[attrib])
21
+ end
22
+
23
+ @key_pair = ::AWS::EC2::KeyPair.new (options[:key_pair] || defaults[:key_pair] )
24
+ end
25
+
26
+ def inspect
27
+ "#{self.class}<#{@name}>"
28
+ end
29
+ alias_method :to_s, :inspect
30
+
31
+ end
32
+ end
@@ -0,0 +1,23 @@
1
+ module Fakes
2
+ class AWSRoute53
3
+ attr_reader :fail_counter, :hosted_zones, :client
4
+ def initialize(opts = {})
5
+ @client = AWSRoute53Client.new(opts)
6
+ @hosted_zones = opts[:hosted_zones]
7
+ @fail_counter = 0
8
+ end
9
+ end
10
+
11
+ class AWSRoute53Client
12
+ attr_reader :fail_counter
13
+ def initialize(opts = {})
14
+ @times_to_fail = opts[:times_to_fail]
15
+ @fail_counter = 0
16
+ end
17
+
18
+ def change_resource_record_sets(*args)
19
+ @fail_counter = @fail_counter + 1
20
+ raise 'Error' if @fail_counter <= @times_to_fail
21
+ end
22
+ end
23
+ end