Class: Sfn::Provider

Inherits:
Object
  • Object
show all
Includes:
Bogo::AnimalStrings
Defined in:
lib/sfn/provider.rb

Overview

Remote provider interface

Constant Summary collapse

STACK_EXPAND_INTERVAL =

Minimum number of seconds to wait before re-expanding in progress stack

45
STACK_LIST_INTERVAL =

Default interval for refreshing stack list in cache

120

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args = {}) ⇒ Provider

Create new instance

Parameters:

  • args (Hash) (defaults to: {})

Options Hash (args):

  • :miasma (Hash)

    miasma connection hash

  • :cache (Cache)
  • :async (TrueClass, FalseClass)

    fetch stacks async (defaults true)

  • :logger (Logger)

    use custom logger

  • :stack_expansion_interval (Numeric)

    interval to wait between stack data expands

  • :stack_list_interval (Numeric)

    interval to wait between stack list refresh



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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/sfn/provider.rb', line 41

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)
  @miamsa_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

Instance Attribute Details

#asyncTrueClass, FalseClass (readonly)

Returns async updates.

Returns:

  • (TrueClass, FalseClass)

    async updates



24
25
26
# File 'lib/sfn/provider.rb', line 24

def async
  @async
end

#cacheCache (readonly)

Returns:



20
21
22
# File 'lib/sfn/provider.rb', line 20

def cache
  @cache
end

#connectionMiasma::Models::Orchestration (readonly)

Returns:

  • (Miasma::Models::Orchestration)


18
19
20
# File 'lib/sfn/provider.rb', line 18

def connection
  @connection
end

#loggerLogger, NilClass (readonly)

Returns logger in use.

Returns:

  • (Logger, NilClass)

    logger in use



26
27
28
# File 'lib/sfn/provider.rb', line 26

def logger
  @logger
end

#stack_expansion_intervalNumeric (readonly)

Returns interval between stack expansions.

Returns:

  • (Numeric)

    interval between stack expansions



28
29
30
# File 'lib/sfn/provider.rb', line 28

def stack_expansion_interval
  @stack_expansion_interval
end

#stack_list_intervalNumeric (readonly)

Returns interval between stack list updates.

Returns:

  • (Numeric)

    interval between stack list updates



30
31
32
# File 'lib/sfn/provider.rb', line 30

def stack_list_interval
  @stack_list_interval
end

#updaterThread, NilClass

Returns stack list updater.

Returns:

  • (Thread, NilClass)

    stack list updater



22
23
24
# File 'lib/sfn/provider.rb', line 22

def updater
  @updater
end

Instance Method Details

#cached_stacksString

Returns json representation of cached stacks.

Returns:

  • (String)

    json representation of cached stacks



96
97
98
99
100
# File 'lib/sfn/provider.rb', line 96

def cached_stacks
  fetch_stacks unless @initial_fetch_complete
  value = cache[:stacks].value
  value ? MultiJson.dump(MultiJson.load(value).values) : '[]'
end

#expand_stack(stack) ⇒ Object

Expand all lazy loaded attributes within stack

Parameters:

  • stack (Miasma::Models::Orchestration::Stack)


140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/sfn/provider.rb', line 140

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

#fetch_stacksTrueClass

Request stack information and store in cache

Returns:

  • (TrueClass)


165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/sfn/provider.rb', line 165

def fetch_stacks
  cache.locked_action(:stacks_lock) do
    logger.info "Lock aquired for stack update. Requesting stacks from upstream. (#{Thread.current})"
    stacks = Hash[
      connection.stacks.reload.all.map do |stack|
        [stack.id, stack.attributes]
      end
    ]
    if(cache[:stacks].value)
      existing_stacks = MultiJson.load(cache[:stacks].value)
      # Force common types
      stacks = MultiJson.load(MultiJson.dump(stacks))
      # 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
    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(stack_id) ⇒ TrueClass, FalseClass

Remove stack from the cache

Parameters:

  • stack_id (String)

Returns:

  • (TrueClass, FalseClass)


126
127
128
129
130
131
132
133
134
135
# File 'lib/sfn/provider.rb', line 126

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

#save_expanded_stack(stack_id, stack_attributes) ⇒ TrueClass

Store stack attribute changes

Parameters:

  • stack_id (String)
  • stack_attributes (Hash)

Returns:

  • (TrueClass)


112
113
114
115
116
117
118
119
120
# File 'lib/sfn/provider.rb', line 112

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

#service_for(service) ⇒ Miasma::Model

Build API connection for service type

Parameters:

  • service (String, Symbol)

Returns:

  • (Miasma::Model)


216
217
218
# File 'lib/sfn/provider.rb', line 216

def service_for(service)
  connection.api_for(service)
end

#stack(stack_id) ⇒ Miasma::Orchestration::Stack, NilClass

Returns:

  • (Miasma::Orchestration::Stack, NilClass)


103
104
105
# File 'lib/sfn/provider.rb', line 103

def stack(stack_id)
  stacks.get(stack_id)
end

#stacksMiasma::Orchestration::Stacks

Returns:

  • (Miasma::Orchestration::Stacks)


91
92
93
# File 'lib/sfn/provider.rb', line 91

def stacks
  connection.stacks.from_json(cached_stacks)
end

#update_stack_list!TrueClass, FalseClass

Start async stack list update. Creates thread that loops every ‘self.stack_list_interval` seconds and refreshes stack list in cache

Returns:

  • (TrueClass, FalseClass)


194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/sfn/provider.rb', line 194

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