Class: Kitchen::Driver::Cloudformation

Inherits:
Base
  • Object
show all
Defined in:
lib/kitchen/driver/cloudformation.rb

Overview

Amazon Cloudformation driver for Test Kitchen.

Instance Method Summary collapse

Instance Method Details

#cfObject



120
121
122
123
124
125
126
127
128
# File 'lib/kitchen/driver/cloudformation.rb', line 120

def cf
  @cf ||= Aws::CfClient.new(
    '',
    config[:shared_credentials_profile],
    config[:ssl_cert_file],
    { access_key_id: nil, secret_access_key: nil },
    nil
  )
end

#copy_deprecated_configs(state) ⇒ Object

This copies transport config from the current config object into the state. This relies on logic in the transport that merges the transport config with the current state object, so its a bad coupling. But we can get rid of this when we get rid of these deprecated configs!



138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/kitchen/driver/cloudformation.rb', line 138

def copy_deprecated_configs(state)
  state[:connection_timeout] = config[:ssh_timeout] if config[:ssh_timeout]
  state[:connection_retries] = config[:ssh_retries] if config[:ssh_retries]
  state[:username] = config[:username] if config[:username]
  # elsif instance.transport[:username] == instance.transport.class.defaults[:username]
  # If the transport has the default username, copy it from amis.json
  # This duplicated old behavior but I hate amis.json
  # ami_username = amis["usernames"][instance.platform.name]
  # state[:username] = ami_username if ami_username
  # end
  state[:ssh_key] = config[:ssh_key] if config[:ssh_key]
end

#create(state) ⇒ Object



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
90
91
92
93
94
# File 'lib/kitchen/driver/cloudformation.rb', line 57

def create(state)
  copy_deprecated_configs(state)
  return if state[:stack_name]

  info(Kitchen::Util.outdent!(<<-END))
    Creating CloudFormation Stack <#{config[:stack_name]}>...
    If you are not using an account that qualifies under the AWS
    free-tier, you may be charged to run these suites. The charge
    should be minimal, but neither Test Kitchen nor its maintainers
    are responsible for your incurred costs.
  END
  begin
    stack = create_stack
  rescue # Exception => e
    error("CloudFormation #{$ERROR_INFO}.") # e.message
    return
  end
  state[:stack_name] = stack.stack_name
  state[:hostname] = config[:hostname]
  info("Stack <#{state[:stack_name]}> requested.")
  # tag_stack(stack)

  s = cf.get_stack(state[:stack_name])
  while s.stack_status == 'CREATE_IN_PROGRESS'
    debug_stack_events(state[:stack_name])
    info("CloudFormation waiting for stack <#{state[:stack_name]}> to be created.....")
    sleep(30)
    s = cf.get_stack(state[:stack_name])
  end
  if s.stack_status == 'CREATE_COMPLETE'
    display_stack_events(state[:stack_name])
    info("CloudFormation stack <#{state[:stack_name]}> created.")
  else
    display_stack_events(state[:stack_name])
    error("CloudFormation stack <#{stack.stack_name}> failed to create....attempting to delete")
    destroy(state)
  end
end

#create_stackObject



151
152
153
154
155
# File 'lib/kitchen/driver/cloudformation.rb', line 151

def create_stack
  stack_data = stack_generator.cf_stack_data
  info("Creating CloudFormation Stack #{stack_data[:stack_name]}")
  cf.create_stack(stack_data)
end

#debug_stack_events(stack_name) ⇒ Object



157
158
159
160
161
162
163
# File 'lib/kitchen/driver/cloudformation.rb', line 157

def debug_stack_events(stack_name)
  return unless logger.debug?
  response = cf.get_stack_events(stack_name)
  response[:stack_events].each do |r|
    debug("#{r[:timestamp]} #{r[:resource_type]} #{r[:logical_resource_id]} #{r[:resource_status]} #{r[:resource_status_reason]}")
  end
end

#destroy(state) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/kitchen/driver/cloudformation.rb', line 96

def destroy(state)
  stack = cf.get_stack(state[:stack_name])
  if stack.nil?
    state.delete(:stack_name)
  else
    cf.delete_stack(state[:stack_name])
    begin
      stack = cf.get_stack(state[:stack_name])
      while stack.stack_status == 'DELETE_IN_PROGRESS'
        debug_stack_events(state[:stack_name])
        info("CloudFormation waiting for stack <#{state[:stack_name]}> to be deleted.....")
        sleep(30)
        stack = cf.get_stack(state[:stack_name])
      end
    rescue # Exception => e
      info("CloudFormation stack <#{state[:stack_name]}> deleted.")
      state.delete(:stack_name)
      return
    end
    display_stack_events(state[:stack_name])
    error("CloudFormation stack <#{stack.stack_name}> failed to deleted.")
  end
end

#display_stack_events(stack_name) ⇒ Object



165
166
167
168
169
170
# File 'lib/kitchen/driver/cloudformation.rb', line 165

def display_stack_events(stack_name)
  response = cf.get_stack_events(stack_name)
  response[:stack_events].each do |r|
    info("#{r[:timestamp]} #{r[:resource_type]} #{r[:logical_resource_id]} #{r[:resource_status]} #{r[:resource_status_reason]}")
  end
end

#stack_generatorObject



130
131
132
# File 'lib/kitchen/driver/cloudformation.rb', line 130

def stack_generator
  @stack_generator ||= Aws::StackGenerator.new(config, cf)
end