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.
-
#working_directory ⇒ String
Working directory.
-
#workspace(payload) ⇒ String
Fresh working directory.
Methods included from StackCommon
#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
176 177 178 |
# File 'lib/jackal-stacks/builder.rb', line 176 def allowed?(payload) !!determine_namespace(payload) end |
#build_stack_args(payload, directory) ⇒ Smash
Build configuration arguments for Sfn::Command execution
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/jackal-stacks/builder.rb', line 86 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
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/jackal-stacks/builder.rb', line 51 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, :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
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/jackal-stacks/builder.rb', line 184 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
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/jackal-stacks/builder.rb', line 121 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
146 147 148 149 150 151 152 153 |
# File 'lib/jackal-stacks/builder.rb', line 146 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
238 239 240 241 |
# File 'lib/jackal-stacks/builder.rb', line 238 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
161 162 163 164 165 166 167 168 169 |
# File 'lib/jackal-stacks/builder.rb', line 161 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! true end |
#setup(*_) ⇒ Object
Setup callback
11 12 13 14 15 16 17 18 19 20 |
# File 'lib/jackal-stacks/builder.rb', line 11 def setup(*_) require 'sfn' require 'bogo-ui' require 'stringio' require 'openssl' require 'fileutils' require 'batali' # Ensure we can build the API at startup stacks_api end |
#store_stable_asset(payload, directory) ⇒ Object
Store stable asset in object store
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/jackal-stacks/builder.rb', line 205 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
26 27 28 29 30 31 32 33 |
# File 'lib/jackal-stacks/builder.rb', line 26 def valid?() super do |payload| payload.get(:data, :stacks, :builder) && payload.get(:data, :stacks, :template) && payload.get(:data, :stacks, :asset) && allowed?(payload) end end |
#working_directory ⇒ String
Returns working directory.
36 37 38 39 40 41 |
# File 'lib/jackal-stacks/builder.rb', line 36 def working_directory memoize(:working_directory, :direct) do FileUtils.mkdir_p(dir = config.fetch(:working_directory, '/tmp/jackal-stack-builder')) dir end end |
#workspace(payload) ⇒ String
Returns fresh working directory.
44 45 46 |
# File 'lib/jackal-stacks/builder.rb', line 44 def workspace(payload) File.join(working_directory, payload[:id]) end |