Class: Jackal::Stacks::Builder
- Inherits:
-
Callback
- Object
- Callback
- Jackal::Stacks::Builder
- Includes:
- StackCommon
- Defined in:
- lib/jackal-stacks/builder.rb
Overview
Stack builder
Instance Method Summary collapse
-
#allowed?(payload) ⇒ TrueClass, FalseClass
Check if this payload is allowed to be processed based on defined restrictions within the configuration.
-
#build_stack_args(payload, directory) ⇒ Smash
Build configuration arguments for Sfn::Command execution.
-
#execute(message) ⇒ Object
Build or update stacks.
-
#init_provider(payload) ⇒ Object
Initialize provider if instructed via config.
-
#load_stack_parameters(payload, directory) ⇒ Object
Extract any custom parameters from asset store if available, and merge any parameters provided via payload, and finally merge any parameters provided via configuration.
-
#load_stacks_file(payload, directory) ⇒ Smash
Parse the ‘.stacks` file if available.
-
#name_for(payload, asset_name) ⇒ String
Provide prefixed key name for asset.
-
#run_stack(payload, directory, action) ⇒ TrueClass
Perform stack action.
-
#setup(*_) ⇒ Object
Setup callback.
-
#store_stable_asset(payload, directory) ⇒ Object
Store stable asset in object store.
-
#valid?(message) ⇒ Truthy, Falsey
Determine validity of message.
-
#wait_for_complete(stack) ⇒ TrueClass
Wait for stack to reach a completion state.
Methods included from StackCommon
#api_config, #determine_namespace, #stack_name, #stacks_api
Instance Method Details
#allowed?(payload) ⇒ TrueClass, FalseClass
Check if this payload is allowed to be processed based on defined restrictions within the configuration
177 178 179 |
# File 'lib/jackal-stacks/builder.rb', line 177 def allowed?(payload) !!determine_namespace(payload) end |
#build_stack_args(payload, directory) ⇒ Smash
Build configuration arguments for Sfn::Command execution
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/jackal-stacks/builder.rb', line 74 def build_stack_args(payload, directory) Smash.new( :base_directory => File.join(directory, 'cloudformation'), :parameters => load_stack_parameters(payload, directory), :ui => Bogo::Ui.new( :app_name => 'JackalStacks', :defaults => true, :output_to => StringIO.new('') ), :interactive_parameters => false, :nesting_bucket => config.get(:orchestration, :bucket_name), :apply_nesting => true, :processing => true, :options => { :disable_rollback => true, :capabilities => ['CAPABILITY_IAM'] }, :credentials => config.get(:orchestration, :api, :credentials), :file => payload.fetch(:data, :stacks, :template, config.get(:default_template_path)), :file_path_prompt => false, :poll => false ) end |
#execute(message) ⇒ Object
Build or update stacks
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/jackal-stacks/builder.rb', line 36 def execute() failure_wrap() do |payload| directory = asset_store.unpack(asset_store.get(payload.get(:data, :stacks, :asset)), workspace(payload)) begin unless(payload.get(:data, :stacks, :template)) payload.set(:data, :stacks, :template, 'infrastructure') end unless(payload.get(:data, :stacks, :name)) payload.set(:data, :stacks, :name, stack_name(payload)) end store_stable_asset(payload, directory) begin stack = stacks_api.stacks.get(payload.get(:data, :stacks, :name)) rescue stack = nil end if(stack) info "Stack currently exists. Applying update [#{stack}]" run_stack(payload, directory, :update) payload.set(:data, :stacks, :updated, true) else info "Stack does not currently exist. Building new stack [#{payload.get(:data, :stacks, :name)}]" init_provider(payload) run_stack(payload, directory, :create) payload.set(:data, :stacks, :created, true) end ensure FileUtils.rm_rf(directory) end job_completed(:stacks, payload, ) end end |
#init_provider(payload) ⇒ Object
this currently init’s chef related items
Initialize provider if instructed via config
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/jackal-stacks/builder.rb', line 185 def init_provider(payload) if(config.get(:init, :chef, :validator) || config.get(:init, :chef, :encrypted_secret)) bucket = stacks_api.api_for(:storage).buckets.get(config.get(:orchestration, :bucket_name)) validator_name = name_for(payload, 'validator.pem') if(config.get(:init, :chef, :validator) && bucket.files.get(validator_name).nil?) file = bucket.files.build(:name => validator_name) file.body = OpenSSL::PKey::RSA.new(2048).export file.save end secret_name = name_for(payload, 'encrypted_data_bag_secret') if(config.get(:init, :chef, :encrypted_secret) && bucket.files.get(secret_name).nil?) file = bucket.files.build(:name => secret_name) file.body = SecureRandom.base64(2048) file.save end end end |
#load_stack_parameters(payload, directory) ⇒ Object
parameter precedence:
-
Hook URL parameters
-
Payload parameters
-
Stacks file parameters
-
Service configuration parameters
Extract any custom parameters from asset store if available, and merge any parameters provided via payload, and finally merge any parameters provided via configuration
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/jackal-stacks/builder.rb', line 109 def load_stack_parameters(payload, directory) params = Smash.new stacks_file = load_stacks_file(payload, directory) s_namespace = determine_namespace(payload) template = payload.get(:data, :stacks, :template) params.deep_merge!(payload.fetch(:data, :webhook, :query, :stacks, :parameters, Smash.new)) params.deep_merge!(payload.fetch(:data, :stacks, :parameters, Smash.new)) params.deep_merge!( stacks_file.fetch(s_namespace, template, :parameters, stacks_file.fetch(:default, template, :parameters, Smash.new) ) ) params.deep_merge!( config.fetch(:parameter_overrides, s_namespace, template, config.fetch(:parameter_overrides, :default, template, Smash.new) ) ) params end |
#load_stacks_file(payload, directory) ⇒ Smash
Parse the ‘.stacks` file if available
134 135 136 137 138 139 140 141 |
# File 'lib/jackal-stacks/builder.rb', line 134 def load_stacks_file(payload, directory) stacks_path = File.join(directory, '.stacks') if(File.exists?(stacks_path)) Bogo::Config.new(file_path).data else Smash.new end end |
#name_for(payload, asset_name) ⇒ String
this is currently a no-op and thus are shared across stacks. currently is stubbed for completion of template and interaction logic
Provide prefixed key name for asset
239 240 241 242 |
# File 'lib/jackal-stacks/builder.rb', line 239 def name_for(payload, asset_name) File.join(determine_namespace(payload), asset_name) asset_name end |
#run_stack(payload, directory, action) ⇒ TrueClass
Perform stack action
149 150 151 152 153 154 155 156 157 158 |
# File 'lib/jackal-stacks/builder.rb', line 149 def run_stack(payload, directory, action) unless([:create, :update].include?(action.to_sym)) abort ArgumentError.new("Invalid action argument `#{action}`. Expecting `create` or `update`!") end args = build_stack_args(payload, directory) stack_name = payload.get(:data, :stacks, :name) Sfn::Command.const_get(action.to_s.capitalize).new(args, [stack_name]).execute! wait_for_complete(stacks_api.stacks.get(stack_name)) true end |
#setup(*_) ⇒ Object
Setup callback
11 12 13 14 15 16 17 18 |
# File 'lib/jackal-stacks/builder.rb', line 11 def setup(*_) require 'sfn' require 'bogo-ui' require 'stringio' require 'openssl' require 'fileutils' require 'batali' end |
#store_stable_asset(payload, directory) ⇒ Object
Store stable asset in object store
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/jackal-stacks/builder.rb', line 206 def store_stable_asset(payload, directory) if(config.get(:init, :stable)) ['.batali', 'Gemfile', 'Gemfile.lock'].each do |file| file_path = File.join(directory, file) if(File.exists?(file_path)) debug "Removing file from infra directory: #{file}" FileUtils.rm(file_path) end end if(File.exists?(File.join(directory, 'batali.manifest'))) debug 'Installing cookbooks from Batali manifest' Dir.chdir(directory) do Batali::Command::Install.new({}, []).execute! end end debug "Starting stable asset upload for #{payload[:id]}" bucket = stacks_api.api_for(:storage).buckets.get(config.get(:orchestration, :bucket_name)) stable_name = name_for(payload, 'stable.zip') file = bucket.files.get(stable_name) || bucket.files.build(:name => stable_name) file.body = asset_store.pack(directory) file.save debug "Completed stable asset upload for #{payload[:id]}" end end |
#valid?(message) ⇒ Truthy, Falsey
Determine validity of message
24 25 26 27 28 29 30 31 |
# File 'lib/jackal-stacks/builder.rb', line 24 def valid?() super do |payload| (!block_given? || yield(payload)) && payload.get(:data, :stacks, :builder) && payload.get(:data, :stacks, :asset) && allowed?(payload) end end |
#wait_for_complete(stack) ⇒ TrueClass
Wait for stack to reach a completion state
164 165 166 167 168 169 170 |
# File 'lib/jackal-stacks/builder.rb', line 164 def wait_for_complete(stack) until(stack.state.to_s.donwcase.end_with?('complete') || stack.state.to_s.donwcase.end_with?('failed')) sleep(10) stack.reload end true end |