Class: KnifeCloudformation::Provider

Inherits:
Object
  • Object
show all
Includes:
Utils::AnimalStrings
Defined in:
lib/knife-cloudformation/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

Methods included from Utils::AnimalStrings

#camel, #snake

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



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
89
# File 'lib/knife-cloudformation/provider.rb', line 43

def initialize(args={})
  unless(args[: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



26
27
28
# File 'lib/knife-cloudformation/provider.rb', line 26

def async
  @async
end

#cacheCache (readonly)

Returns:



22
23
24
# File 'lib/knife-cloudformation/provider.rb', line 22

def cache
  @cache
end

#connectionMiasma::Models::Orchestration (readonly)

Returns:

  • (Miasma::Models::Orchestration)


20
21
22
# File 'lib/knife-cloudformation/provider.rb', line 20

def connection
  @connection
end

#loggerLogger, NilClass (readonly)

Returns logger in use.

Returns:

  • (Logger, NilClass)

    logger in use



28
29
30
# File 'lib/knife-cloudformation/provider.rb', line 28

def logger
  @logger
end

#stack_expansion_intervalNumeric (readonly)

Returns interval between stack expansions.

Returns:

  • (Numeric)

    interval between stack expansions



30
31
32
# File 'lib/knife-cloudformation/provider.rb', line 30

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



32
33
34
# File 'lib/knife-cloudformation/provider.rb', line 32

def stack_list_interval
  @stack_list_interval
end

#updaterThread, NilClass

Returns stack list updater.

Returns:

  • (Thread, NilClass)

    stack list updater



24
25
26
# File 'lib/knife-cloudformation/provider.rb', line 24

def updater
  @updater
end

Instance Method Details

#cached_stacksString

Returns json representation of cached stacks.

Returns:

  • (String)

    json representation of cached stacks



97
98
99
100
101
# File 'lib/knife-cloudformation/provider.rb', line 97

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)


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

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)


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

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 = Chef::Mixin::DeepMerge.merge(existing_stacks, 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)


127
128
129
130
131
132
133
134
135
136
# File 'lib/knife-cloudformation/provider.rb', line 127

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)


113
114
115
116
117
118
119
120
121
# File 'lib/knife-cloudformation/provider.rb', line 113

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)


217
218
219
# File 'lib/knife-cloudformation/provider.rb', line 217

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

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

Returns:

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


104
105
106
# File 'lib/knife-cloudformation/provider.rb', line 104

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

#stacksMiasma::Orchestration::Stacks

Returns:

  • (Miasma::Orchestration::Stacks)


92
93
94
# File 'lib/knife-cloudformation/provider.rb', line 92

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)


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

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