class Sfn::Provider
Remote provider interface
Constants
- STACK_EXPAND_INTERVAL
Minimum number of seconds to wait before re-expanding in progress stack
- STACK_LIST_INTERVAL
Default interval for refreshing stack list in cache
Attributes
@return [TrueClass, FalseClass] async updates
@return [Cache]
@return [Miasma::Models::Orchestration]
@return [Logger, NilClass] logger in use
@return [Numeric] interval between stack expansions
@return [Numeric] interval between stack list updates
@return [Thread, NilClass] stack list updater
Public Class Methods
Create new instance
@param args [Hash] @option args [Hash] :miasma miasma connection hash @option args [Cache] :cache @option args [TrueClass, FalseClass] :async fetch stacks async (defaults true) @option args [Logger] :logger use custom logger @option args [Numeric] :stack_expansion_interval interval to wait between stack data expands @option args [Numeric] :stack_list_interval interval to wait between stack list refresh
# File lib/sfn/provider.rb, line 40 def initialize(args = {}) args = args.to_smash unless args.get(:miasma, :provider) best_guess = (args[:miasma] || {}).keys.group_by do |key| key.to_s.split("_").first end.sort do |x, y| y.size <=> x.size end.first if best_guess provider = best_guess.first.to_sym else raise ArgumentError.new "Cannot auto determine :provider value for credentials" end else provider = args[:miasma].delete(:provider).to_sym end if provider == :aws if args[:miasma][:region] args[:miasma][:aws_region] = args[:miasma].delete(:region) end end if ENV["DEBUG"].to_s.downcase == "true" log_to = STDOUT else if Gem.win_platform? log_to = "NUL" else log_to = "/dev/null" end end @logger = args.fetch(:logger, Logger.new(log_to)) @stack_expansion_interval = args.fetch(:stack_expansion_interval, STACK_EXPAND_INTERVAL) @stack_list_interval = args.fetch(:stack_list_interval, STACK_LIST_INTERVAL) @connection = Miasma.api( :provider => provider, :type => :orchestration, :credentials => args[:miasma], ) @cache = args.fetch(:cache, Cache.new(:local)) @async = args.fetch(:async, true) @miasma_args = args[:miasma].dup cache.init(:stacks_lock, :lock, :timeout => 0.1) cache.init(:stacks, :stamped) cache.init(:stack_expansion_lock, :lock, :timeout => 0.1) if args.fetch(:fetch, false) async ? update_stack_list! : fetch_stacks end end
Public Instance Methods
@return [String] json representation of cached stacks
# File lib/sfn/provider.rb, line 95 def cached_stacks(stack_id = nil) if !@initial_fetch_complete || stack_id recache = true if stack_id && @initial_fetch_complete recache = !!stacks.get(stack_id) end fetch_stacks(stack_id) if recache end value = cache[:stacks].value if value value = MultiJson.load(value) if value.respond_to?(:values) value = value.values end MultiJson.dump(value) else "[]" end end
Expand all lazy loaded attributes within stack
@param stack [Miasma::Models::Orchestration::Stack]
# File lib/sfn/provider.rb, line 153 def expand_stack(stack) logger.info "Stack expansion requested (#{stack.id})" if ((stack.in_progress? && Time.now.to_i - stack.attributes["Cached"].to_i > stack_expansion_interval) || !stack.attributes["Cached"]) begin expanded = false cache.locked_action(:stack_expansion_lock) do expanded = true stack.reload stack.data["Cached"] = Time.now.to_i end if expanded save_expanded_stack(stack.id, stack.to_json) end rescue => e logger.error "Stack expansion failed (#{stack.id}) - #{e.class}: #{e}" end else logger.info "Stack has been cached within expand interval. Expansion prevented. (#{stack.id})" end end
Request stack information and store in cache
@return [TrueClass]
# File lib/sfn/provider.rb, line 178 def fetch_stacks(stack_id = nil) cache.locked_action(:stacks_lock) do logger.info "Lock aquired for stack update. Requesting stacks from upstream. (#{Thread.current})" if stack_id single_stack = connection.stacks.get(stack_id) stacks = single_stack ? {single_stack.id => single_stack} : {} else stacks = Hash[ connection.stacks.reload.all.map do |stack| [stack.id, stack.attributes] end ] end if cache[:stacks].value existing_stacks = MultiJson.load(cache[:stacks].value) # Force common types stacks = MultiJson.load(MultiJson.dump(stacks)) if stack_id stacks = existing_stacks.to_smash.deep_merge(stacks) else # Remove stacks that have been deleted stale_ids = existing_stacks.keys - stacks.keys stacks = existing_stacks.to_smash.deep_merge(stacks) stale_ids.each do |stale_id| stacks.delete(stale_id) end end end cache[:stacks].value = stacks.to_json logger.info "Stack list has been updated from upstream and cached locally" end @initial_fetch_complete = true end
Remove stack from the cache
@param stack_id [String] @return [TrueClass, FalseClass]
# File lib/sfn/provider.rb, line 139 def remove_stack(stack_id) current_stacks = MultiJson.load(cached_stacks) logger.info "Attempting to remove stack from internal cache (#{stack_id})" cache.locked_action(:stacks_lock) do val = current_stacks.delete(stack_id) logger.info "Successfully removed stack from internal cache (#{stack_id})" cache[:stacks].value = MultiJson.dump(current_stacks) !!val end end
Store stack attribute changes
@param stack_id [String] @param stack_attributes [Hash] @return [TrueClass]
# File lib/sfn/provider.rb, line 125 def save_expanded_stack(stack_id, stack_attributes) current_stacks = MultiJson.load(cached_stacks) cache.locked_action(:stacks_lock) do logger.info "Saving expanded stack attributes in cache (#{stack_id})" current_stacks[stack_id] = stack_attributes.merge("Cached" => Time.now.to_i) cache[:stacks].value = MultiJson.dump(current_stacks) end true end
Build API connection for service type
@param service [String, Symbol] @return [Miasma::Model]
# File lib/sfn/provider.rb, line 238 def service_for(service) connection.api_for(service) end
@return [Miasma::Orchestration::Stack, NilClass]
# File lib/sfn/provider.rb, line 116 def stack(stack_id) stacks(stack_id).get(stack_id) end
@return [Miasma::Orchestration::Stacks]
# File lib/sfn/provider.rb, line 90 def stacks(stack_id = nil) connection.stacks.from_json(cached_stacks(stack_id)) end
Start async stack list update. Creates thread that loops every `self.stack_list_interval` seconds and refreshes stack list in cache
@return [TrueClass, FalseClass]
# File lib/sfn/provider.rb, line 216 def update_stack_list! if updater.nil? || !updater.alive? self.updater = Thread.new { loop do begin fetch_stacks sleep(stack_list_interval) rescue => e logger.error "Failure encountered on stack fetch: #{e.class} - #{e}" end end } true else false end end