Class: Jets::Commands::Deploy
- Inherits:
-
Object
- Object
- Jets::Commands::Deploy
- Extended by:
- Memoist
- Includes:
- StackInfo
- Defined in:
- lib/jets/commands/deploy.rb
Instance Method Summary collapse
-
#aws_config_update! ⇒ Object
Override the AWS retry settings during a deploy.
- #build_code ⇒ Object
- #check_dev_mode ⇒ Object
- #create_s3_event_buckets ⇒ Object
- #delete_minimal_stack ⇒ Object
-
#exit_unless_updateable! ⇒ Object
All CloudFormation states listed here: docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-describing-stacks.html.
- #find_stack(stack_name) ⇒ Object
-
#initialize(options) ⇒ Deploy
constructor
A new instance of Deploy.
-
#minimal_rollback_complete? ⇒ Boolean
Checks for a few things before deciding to delete the parent stack.
- #run ⇒ Object
- #ship(stack_options) ⇒ Object
- #stack_name ⇒ Object
- #status ⇒ Object
- #validate_routes! ⇒ Object
Methods included from StackInfo
#first_run?, #parent_stack_name, #s3_bucket, #stack_type
Methods included from AwsServices
#apigateway, #aws_lambda, #cfn, #dynamodb, #logs, #s3, #s3_resource, #sns, #sqs, #sts
Methods included from AwsServices::StackStatus
#lookup, #stack_exists?, #stack_in_progress?
Methods included from AwsServices::GlobalMemoist
Constructor Details
#initialize(options) ⇒ Deploy
Returns a new instance of Deploy.
7 8 9 |
# File 'lib/jets/commands/deploy.rb', line 7 def initialize() @options = end |
Instance Method Details
#aws_config_update! ⇒ Object
Override the AWS retry settings during a deploy.
The aws-sdk-core has expondential backup with this formula:
2 ** c.retries * c.config.retry_base_delay
So the max delay will be 2 ** 7 * 0.6 = 76.8s
Only scoping this to deploy because dont want to affect people’s application that use the aws sdk.
There is also additional rate backoff logic elsewhere, since this is only scoped to deploys.
Useful links:
https://github.com/aws/aws-sdk-ruby/blob/master/gems/aws-sdk-core/lib/aws-sdk-core/plugins/retry_errors.rb
https://docs.aws.amazon.com/apigateway/latest/developerguide/limits.html
56 57 58 59 60 61 |
# File 'lib/jets/commands/deploy.rb', line 56 def aws_config_update! Aws.config.update( retry_limit: 7, # default: 3 retry_base_delay: 0.6, # default: 0.3 ) end |
#build_code ⇒ Object
84 85 86 |
# File 'lib/jets/commands/deploy.rb', line 84 def build_code Jets::Commands::Build.new(@options).build_code end |
#check_dev_mode ⇒ Object
77 78 79 80 81 82 |
# File 'lib/jets/commands/deploy.rb', line 77 def check_dev_mode if File.exist?("#{Jets.root}/dev.mode") puts "The dev.mode file exists. Please removed it and run bundle update before you deploy.".color(:red) exit 1 end end |
#create_s3_event_buckets ⇒ Object
63 64 65 66 67 68 |
# File 'lib/jets/commands/deploy.rb', line 63 def create_s3_event_buckets buckets = Jets::Job::Base.s3_events.keys buckets.each do |bucket| Jets::AwsServices::S3Bucket.ensure_exists(bucket) end end |
#delete_minimal_stack ⇒ Object
70 71 72 73 74 75 |
# File 'lib/jets/commands/deploy.rb', line 70 def delete_minimal_stack puts "Existing stack is in ROLLBACK_COMPLETE state from a previous failed minimal deploy. Deleting stack and continuing." cfn.delete_stack(stack_name: stack_name) status.wait status.reset end |
#exit_unless_updateable! ⇒ Object
All CloudFormation states listed here: docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-describing-stacks.html
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/jets/commands/deploy.rb', line 154 def exit_unless_updateable! return if ENV['JETS_FORCE_UPDATEABLE'] # useful for debugging if stack stack updating stack_name = Jets::Naming.parent_stack_name exists = stack_exists?(stack_name) return unless exists # continue because stack could be updating stack = cfn.describe_stacks(stack_name: stack_name).stacks.first status = stack["stack_status"] if status =~ /^ROLLBACK_/ || status =~ /_IN_PROGRESS$/ region = `aws configure get region`.strip rescue "us-east-1" url = "https://console.aws.amazon.com/cloudformation/home?region=#{region}#/stacks" puts "The parent stack of the #{Jets.config.project_name.color(:green)} project is not in an updateable state." puts "Stack name #{stack_name.color(:yellow)} status #{stack["stack_status"].color(:yellow)}" puts "Here's the CloudFormation url to check for more details #{url}" exit 1 end end |
#find_stack(stack_name) ⇒ Object
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/jets/commands/deploy.rb', line 128 def find_stack(stack_name) retries = 0 resp = cfn.describe_stacks(stack_name: stack_name) resp.stacks.first rescue Aws::CloudFormation::Errors::ValidationError => e # example: Stack with id demo-dev does not exist if e. =~ /Stack with/ && e. =~ /does not exist/ nil else raise end rescue Aws::CloudFormation::Errors::Throttling => e retries += 1 seconds = 2 ** retries puts "WARN: find_stack #{e.class} #{e.}".color(:yellow) puts "Backing off and will retry in #{seconds} seconds." sleep(seconds) if seconds > 90 # 2 ** 6 is 64 so will give up after 6 retries puts "Giving up after #{retries} retries" else retry end end |
#minimal_rollback_complete? ⇒ Boolean
Checks for a few things before deciding to delete the parent stack
* Parent stack status status is ROLLBACK_COMPLETE
* Parent resources are in the DELETE_COMPLETE state
116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/jets/commands/deploy.rb', line 116 def minimal_rollback_complete? stack = find_stack(stack_name) return false unless stack return false unless stack.stack_status == 'ROLLBACK_COMPLETE' # Finally check if all the minimal resources in the parent template have been deleted resp = cfn.describe_stack_resources(stack_name: stack_name) resource_statuses = resp.stack_resources.map(&:resource_status).uniq resource_statuses == ['DELETE_COMPLETE'] end |
#run ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/jets/commands/deploy.rb', line 11 def run aws_config_update! deployment_env = Jets.config.project_namespace.color(:green) puts "Deploying to Lambda #{deployment_env} environment..." return if @options[:noop] check_dev_mode validate_routes! # deploy full nested stack when stack already exists # Delete existing rollback stack from previous bad minimal deploy delete_minimal_stack if minimal_rollback_complete? exit_unless_updateable! # Stack could be in a weird rollback state or in progress state if first_run? ship(stack_type: :minimal) Jets.application.reload_configs! end # Build code after the minimal stack because need s3 bucket for assets # on_aws? and s3_base_url logic # TODO: possible deploy hook point: before_build build_code # TODO: possible deploy hook point: before_ship create_s3_event_buckets ship(stack_type: :full, s3_bucket: s3_bucket) end |
#ship(stack_options) ⇒ Object
96 97 98 99 100 |
# File 'lib/jets/commands/deploy.rb', line 96 def ship() = @options.merge() # includes stack_type and s3_bucket Jets::Commands::Build.new().build_templates Jets::Cfn::Ship.new().run end |
#stack_name ⇒ Object
107 108 109 |
# File 'lib/jets/commands/deploy.rb', line 107 def stack_name Jets::Naming.parent_stack_name end |
#status ⇒ Object
102 103 104 |
# File 'lib/jets/commands/deploy.rb', line 102 def status Jets::Cfn::Status.new(stack_name) end |
#validate_routes! ⇒ Object
88 89 90 91 92 93 94 |
# File 'lib/jets/commands/deploy.rb', line 88 def validate_routes! valid = Jets::Router.validate_routes! unless valid puts "Deploy fail: The jets application contain invalid routes.".color(:red) exit 1 end end |