Class: OpsworksShip::Deploy

Inherits:
Object
  • Object
show all
Defined in:
lib/opsworks_ship/deploy.rb

Instance Method Summary collapse

Constructor Details

#initialize(stack_name, revision, app_type, app_layer_name_regex, hipchat_auth_token = nil, hipchat_room_id = nil) ⇒ Deploy

Returns a new instance of Deploy.



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/opsworks_ship/deploy.rb', line 5

def initialize(stack_name, revision, app_type, app_layer_name_regex, hipchat_auth_token = nil, hipchat_room_id = nil)
  @stack_name = stack_name
  raise "Invalid stack name, valid stacks are: #{all_stack_names}" unless all_stack_names.any?{|available_name| available_name == stack_name}

  @revision = revision

  @app_type = app_type
  raise "Invalid app type #{@app_type}" unless opsworks_app_types.include?(@app_type)

  @app_layer_name_regex = app_layer_name_regex

  @hipchat_auth_token = hipchat_auth_token
  @hipchat_room_id = hipchat_room_id
  raise "Must supply both or neither hipchat params" if [@hipchat_auth_token, @hipchat_room_id].compact.size == 1
end

Instance Method Details

#all_stack_namesObject



52
53
54
# File 'lib/opsworks_ship/deploy.rb', line 52

def all_stack_names
  @all_stack_names ||= stack_data.map{|s| s['Name']}.sort
end

#app_id(stack_id) ⇒ Object



78
79
80
# File 'lib/opsworks_ship/deploy.rb', line 78

def app_id(stack_id)
  JSON.parse(`aws opsworks describe-apps --stack-id=#{stack_id}`)['Apps'].select{|a| a['Type'] == @app_type}.first['AppId']
end

#deployObject



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/opsworks_ship/deploy.rb', line 27

def deploy
  start_time = Time.now

  puts "\n-------------------------------------"
  puts "Deployment started"
  puts "Revision: #{@revision}"
  puts "Stack: #{@stack_name}"
  puts "-------------------------------------\n\n"

  set_revision_in_opsworks_app

  deployment_id = start_deployment
  final_status = monitor_deployment(deployment_id)

  if final_status.downcase =~ /successful/
    msg = "#{@app_type} deployment successful! Layers #{@app_layer_name_regex} now on #{@revision} deployed to #{@stack_name} by #{deployed_by}"
    post_deployment_to_hipchat(msg)
  else
    raise "Deployment failed, status: #{final_status}"
  end

  run_time_seconds = Time.now.to_i - start_time.to_i
  timestamped_puts "Deployment time #{run_time_seconds / 60}:%02i" % (run_time_seconds % 60)
end

#deploy_commandObject



96
97
98
# File 'lib/opsworks_ship/deploy.rb', line 96

def deploy_command
  { :Name => "deploy" }.to_json.gsub('"', "\\\"")
end

#deploy_commentObject



92
93
94
# File 'lib/opsworks_ship/deploy.rb', line 92

def deploy_comment
  "--comment \"rev. #{@revision}, deployed by #{deployed_by}\" "
end

#deployed_byObject



86
87
88
89
90
# File 'lib/opsworks_ship/deploy.rb', line 86

def deployed_by
  git_user = `git config --global user.name`.chomp
  git_email = `git config --global user.email`.chomp
  "#{git_user} (#{git_email})"
end

#describe_deployments(deployment_id) ⇒ Object



117
118
119
120
# File 'lib/opsworks_ship/deploy.rb', line 117

def describe_deployments(deployment_id)
  cmd = "aws opsworks describe-deployments --deployment-ids #{deployment_id}"
  JSON.parse(`#{cmd}`)
end

#layer_instance_ids(layer_ids) ⇒ Object



161
162
163
164
165
# File 'lib/opsworks_ship/deploy.rb', line 161

def layer_instance_ids(layer_ids)
  layer_ids.map do |layer_id|
    JSON.parse(`aws opsworks describe-instances --layer-id=#{layer_id}`)['Instances'].map{|i| i['InstanceId']}
  end.flatten
end

#monitor_deployment(deployment_id) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/opsworks_ship/deploy.rb', line 100

def monitor_deployment(deployment_id)
  deployment_finished = false
  status = ""
  while !deployment_finished
    response = describe_deployments(deployment_id)
    response["Deployments"].each do |deployment|
      next if deployment["DeploymentId"] != deployment_id
      status = deployment["Status"]
      timestamped_puts "Status: #{status}"
      deployment_finished = true if deployment["Status"].downcase != "running"
    end
    sleep(15) unless deployment_finished
  end
  timestamped_puts "Deployment #{status}"
  status
end

#opsworks_app_typesObject



179
180
181
182
183
184
185
186
187
188
189
# File 'lib/opsworks_ship/deploy.rb', line 179

def opsworks_app_types
  [
    'aws-flow-ruby',
    'java',
    'rails',
    'php',
    'nodejs',
    'static',
    'other',
  ]
end

#post_deployment_to_hipchat(msg) ⇒ Object



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

def post_deployment_to_hipchat(msg)
  room_id = (@hipchat_room_id || ENV['HIPCHAT_ROOM_ID']).to_i
  auth_token = @hipchat_auth_token || ENV['HIPCHAT_AUTH_TOKEN']

  return unless room_id > 0 && auth_token

  post_data = {
    :name => "Deployments",
    :privacy => 'private',
    :is_archived => false,
    :is_guest_accessible => true,
    :topic => 'curl',
    :message => msg,
    :color => 'green',
    :owner => { :id => 5 }
  }

  url = "https://api.hipchat.com/v2/room/#{room_id}/notification"
  cmd = "curl --header \"content-type: application/json\" --header \"Authorization: Bearer #{auth_token}\" -X POST -d \"#{post_data.to_json.gsub('"', '\\"')}\" #{url}"
  puts cmd
  `#{cmd}`
end

#relevant_app_layer_ids(stack_id) ⇒ Object



171
172
173
# File 'lib/opsworks_ship/deploy.rb', line 171

def relevant_app_layer_ids(stack_id)
  stack_layers(stack_id).select{|l| l['Name'] =~ /#{@app_layer_name_regex}/i}.map{|layer_data| layer_data['LayerId']}
end

#relevant_instance_ids(stack_id) ⇒ Object



167
168
169
# File 'lib/opsworks_ship/deploy.rb', line 167

def relevant_instance_ids(stack_id)
  layer_instance_ids(relevant_app_layer_ids(stack_id))
end

#set_revision_in_opsworks_appObject



71
72
73
74
75
76
# File 'lib/opsworks_ship/deploy.rb', line 71

def set_revision_in_opsworks_app
  timestamped_puts "Setting revision #{@revision}"
  cmd = "aws opsworks update-app --app-id #{app_id(stack_id)} --app-source Revision=#{@revision}"
  timestamped_puts "#{cmd}"
  `#{cmd}`
end

#stack_dataObject



56
57
58
59
60
# File 'lib/opsworks_ship/deploy.rb', line 56

def stack_data
  @stack_data ||= begin
    JSON.parse(`aws opsworks describe-stacks`)['Stacks']
  end
end

#stack_idObject



62
63
64
65
66
67
68
69
# File 'lib/opsworks_ship/deploy.rb', line 62

def stack_id
  stack = stack_data.select{|s| s['Name'].downcase == @stack_name.downcase}.first
  if stack
    stack['StackId']
  else
    raise "Stack not found.  Available opsworks stacks: #{all_stack_names}"
  end
end

#stack_layers(stack_id) ⇒ Object



175
176
177
# File 'lib/opsworks_ship/deploy.rb', line 175

def stack_layers(stack_id)
  JSON.parse(`aws opsworks describe-layers --stack-id=#{stack_id}`)['Layers']
end

#start_deploymentObject



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/opsworks_ship/deploy.rb', line 122

def start_deployment
  app_id = app_id(stack_id)

  cmd = "aws opsworks create-deployment --app-id #{app_id} " +
      "--stack-id #{stack_id} " +
      "--command \"#{deploy_command}\" " +
      "--instance-ids #{relevant_instance_ids(stack_id).join(' ')} " +
      deploy_comment

  timestamped_puts "Starting deployment..."
  timestamped_puts cmd

  response = JSON.parse(`#{cmd}`)
  response["DeploymentId"]
end

#syntaxObject



21
22
23
24
25
# File 'lib/opsworks_ship/deploy.rb', line 21

def syntax
  puts "Arguments: #{method(:initialize).parameters.map{|p| "#{p.last} (#{p.first})"}.join(' ')}"
  puts "\n"
  puts "Valid stacks: #{all_stack_names}}"
end

#timestamped_puts(str) ⇒ Object



82
83
84
# File 'lib/opsworks_ship/deploy.rb', line 82

def timestamped_puts(str)
  puts "#{Time.now}  #{str}"
end