cf_deployer 1.2.8

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