Top Level Namespace

Defined Under Namespace

Modules: Clouds

Instance Method Summary collapse

Instance Method Details

#clone_stack(stack, new_stack, force = false, commit = false) ⇒ Object



238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/clouds.rb', line 238

def clone_stack(stack, new_stack, force=false, commit=false)
  if File.exist?(get_stack_directory(new_stack)) and force == false
    raise 'The stack already exists, use --force to override'
  elsif force == true
    FileUtils.rm_rf(get_stack_directory(new_stack))
  end
  FileUtils.cp_r(get_stack_directory(stack), get_stack_directory(new_stack))
  if commit
    puts 'Committing changes to AWS'
    update_stack(new_stack, true)
  else
    puts 'Only copied the stack template and parameters locally, use the --commit flag in order to do commit the changes to AWS'
  end
end

#configure(profile = nil) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/clouds.rb', line 9

def configure(profile=nil)
  if profile.nil? && ENV['AWS_ACCESS_KEY_ID'] && ENV['AWS_SECRET_ACCESS_KEY']
    @aws_access_key_id = ENV['AWS_ACCESS_KEY_ID']
    @aws_secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
    @aws_session_token = ENV['AWS_SECURITY_TOKEN']
    @region = ENV['AWS_DEFAULT_REGION'] || 'us-east-1'
  else
    profile = profile || ENV['AWS_DEFAULT_PROFILE']
    aws_credentials_file = File.join(File.expand_path('~'), '.aws', 'credentials')

    if profile.nil?
      profile = "default"
      puts "Using the default profile"
    else
      puts "Using the profile '#{profile}'"
    end
    aws_config = IniFile.load(aws_credentials_file)[profile]

    @aws_access_key_id = aws_config['aws_access_key_id']
    @aws_secret_access_key = aws_config['aws_secret_access_key']
    @region = aws_config['region']
  end

  proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']

  AWS.config({:access_key_id => @aws_access_key_id,
              :secret_access_key => @aws_secret_access_key,
              :session_token => @aws_session_token,
              :region => @region,
              :proxy_uri => proxy,
  })

  @cfn = AWS::CloudFormation.new
end

#create_stack_directory(stack_name) ⇒ Object



65
66
67
# File 'lib/clouds.rb', line 65

def create_stack_directory(stack_name)
  FileUtils.mkdir_p(get_stack_directory(stack_name))
end

#delete_stack(stack_name) ⇒ Object



253
254
255
256
257
258
259
260
261
# File 'lib/clouds.rb', line 253

def delete_stack(stack_name)
  configure()
  if @cfn.stacks[stack_name].exists?
    puts "Deleting stack #{stack_name}"
    @cfn.stacks[stack_name].delete
  else
    puts "Stack #{stack_name} does not exist, nothing to delete here..."
  end
end

#dump_all_stacks(force = false) ⇒ Object



131
132
133
134
135
136
137
138
139
140
# File 'lib/clouds.rb', line 131

def dump_all_stacks(force=false)
  configure()
  begin
    @cfn.stacks.each do |stack|
      dump_stacks([stack.name],force)
    end
  rescue => e
    puts e
  end
end

#dump_stacks(stack_list, force = false) ⇒ Object



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/clouds.rb', line 113

def dump_stacks(stack_list, force=false)
  stack_list.each do |stack_name|
    configure()
    begin
      stack = @cfn.stacks[stack_name]
      puts "Dumping stack #{stack.name}"

      template_content = stack.template
      parameters = stack.parameters
      create_stack_directory(stack_name)
      write_file(get_template_path(stack_name), template_content, force)
      write_file(get_parameters_path(stack_name), parameters.to_yaml, force)
    rescue => e
      puts "dump failed: #{e}"
    end
  end
end

#get_parameters_path(stack_name) ⇒ Object



74
75
76
# File 'lib/clouds.rb', line 74

def get_parameters_path(stack_name)
  File.join(get_stack_directory(stack_name), 'parameters.yaml')
end

#get_stack_directory(stack_name) ⇒ Object



61
62
63
# File 'lib/clouds.rb', line 61

def get_stack_directory(stack_name)
  File.join(File.expand_path('.'),'stacks', stack_name)
end

#get_template_path(stack_name) ⇒ Object



70
71
72
# File 'lib/clouds.rb', line 70

def get_template_path(stack_name)
  File.join(get_stack_directory(stack_name), 'template.json')
end

#list_local_stacksObject



103
104
105
106
107
108
109
110
111
# File 'lib/clouds.rb', line 103

def list_local_stacks()
  list = []
  return [] unless File.directory?('stacks')
  Dir.foreach('stacks') do |item|
    next if item == '.' or item == '..'
    list << item
  end
list
end

#list_stacksObject



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/clouds.rb', line 78

def list_stacks()
  configure()
  max_stack_length = 0
  stacks = Hash.new()

  local_stacks = list_local_stacks()
  local_stacks.each do |s|
    max_stack_length = s.length if s.length > max_stack_length
    stacks[s] = 'LOCAL-ONLY'
  end

  begin
    @cfn.stacks.each do |stack|
      stacks[stack.name] = stack.status
      max_stack_length = stack.name.length if stack.name.length > max_stack_length
    end
  rescue => e
    puts e
  end
  puts 'Stack list and stack status:'
  stacks.keys.sort.each do |key|
    printf("%#{max_stack_length}s %s\n",key, stacks[key])
  end
end


215
216
217
218
219
220
221
222
# File 'lib/clouds.rb', line 215

def print_stack_outputs(stack)
  puts '# ---'
  puts "# Outputs from #{stack.name}"
  stack.outputs.each do |o|
    puts "#{o.key}: #{o.value}"
  end
  puts '# ---'
end

#read_file(file_name) ⇒ Object



53
54
55
56
57
58
59
# File 'lib/clouds.rb', line 53

def read_file(file_name)
  begin
    File.open(file_name,'rb').read
  rescue => e
    puts e
  end
end

#update_all_stacks(create_if_missing = false, synchronous = false, outputs = false) ⇒ Object



233
234
235
236
# File 'lib/clouds.rb', line 233

def update_all_stacks(create_if_missing=false, synchronous=false, outputs=false)
  stacks = Dir.glob('stacks/*').map { |d| File.basename d }
  update_stacks(stacks, create_if_missing, synchronous, outputs)
end

#update_stack(stack_name, create_if_missing = false, synchronous = false, outputs = false) ⇒ Object



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/clouds.rb', line 142

def update_stack(stack_name, create_if_missing=false, synchronous=false, outputs=false)
  configure
  stack = nil

  template_content = read_file(get_template_path(stack_name))
  parameters_content = read_file(get_parameters_path(stack_name))

  parameters_hash = {}

  begin
    yaml_hash = YAML.load(parameters_content)
  rescue => e
    puts e
    raise e
  end

  yaml_hash.each do |k, v|
    v = v.join(",") if v.is_a?(Array)
    parameters_hash[k] = v
  end

  p parameters_hash

  raise 'Empty stack template' if template_content.nil? || template_content.empty?

  template_validation = @cfn.validate_template(template_content)
  raise template_validation[:message] unless template_validation[:message].nil?

  begin
    if @cfn.stacks[stack_name].exists?
      puts "# Updating stack #{stack_name}"
      stack = @cfn.stacks[stack_name]
      stack_capabilities = stack.capabilities
      stack.update(:template => template_content,
                   :parameters => parameters_hash,
                   :capabilities => stack_capabilities)
      status = wait_until_status(stack, %w( CREATE_IN_PROGRESS UPDATE_IN_PROGRESS )) if synchronous
    elsif create_if_missing
      puts "# Creating stack #{stack_name}"
      stack = @cfn.stacks.create(stack_name,
                                 template_content,
                                 { :parameters => parameters_hash,
                                   :capabilities => ['CAPABILITY_IAM']})
      status = wait_until_status(stack, %w( CREATE_IN_PROGRESS UPDATE_IN_PROGRESS )) if synchronous
    else
      puts "Skipping stack #{stack_name} since it's not defined in this AWS account, if the stack exists locally you might use the -c flag"
    end
    if ! stack.nil? && %w( UPDATE_COMPLETE CREATE_COMPLETE ).include?(status)
      estimated_cost = stack.estimate_template_cost
      puts "# Estimated costs for the stack #{stack_name} is #{estimated_cost}"

      print_stack_outputs(stack) if outputs
    end
  rescue => e
    puts e
  end

  %w( UPDATE_COMPLETE CREATE_COMPLETE ).include? status
end

#update_stacks(stack_list, create_if_missing = false, synchronous = false, outputs = false) ⇒ Object



224
225
226
227
228
229
230
231
# File 'lib/clouds.rb', line 224

def update_stacks(stack_list, create_if_missing=false, synchronous=false, outputs=false)
  stack_list.each do |stack|
    res = update_stack(stack, create_if_missing, synchronous, outputs)
    return res unless res
  end

  true
end

#wait_until_status(stack, status_arr) ⇒ Object



202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/clouds.rb', line 202

def wait_until_status(stack, status_arr)
  while true
    begin
      status = stack.status
      printf("# %s : %s\n", Time.now.strftime('%Y-%m-%d %H:%M:%S'), status)
      return status unless status_arr.include? status
      sleep 5
    rescue => e
      raise "Cannot retrieve stack status: #{e}"
    end
  end
end

#write_file(file_name, content, force) ⇒ Object



44
45
46
47
48
49
50
51
# File 'lib/clouds.rb', line 44

def write_file(file_name, content, force)
  puts "Creating #{file_name}"
  if File.exist?(file_name) and force == false
    raise 'The file already exists, use --force to override'
  end
  File.open(file_name, 'w')  {|f| f.write(content)}
  puts "Created #{file_name}"
end