Class: Qops::Instance

Inherits:
Thor
  • Object
show all
Includes:
DeployHelpers
Defined in:
lib/qops/deployment/instances.rb

Overview

rubocop:disable Metrics/ClassLength

Instance Method Summary collapse

Instance Method Details

#cleanObject



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
# File 'lib/qops/deployment/instances.rb', line 144

def clean
  initialize_run

  fail "Cannot clean instances in a #{config.deploy_type} environment" if config.deploy_type == 'production'

  terminated_instances = []

  # Find all instances to be destroyed
  retrieve_instances.each do |instance|
    next if instance.hostname == "#{config.hostname_prefix}master"

    ec2instances = config.ec2.describe_instances(instance_ids: [instance.ec2_instance_id])
    next if ec2instances.reservations.empty?

    # Get various tag values
    ec2instance = ec2instances.reservations.first.instances.first
    environment = ec2instance.tags.find { |t| t.key == 'environment' }
    cleanable = ec2instance.tags.find { |t| t.key == 'cleanable' }
    branch = ec2instance.tags.find { |t| t.key == 'branch' }

    next if !cleanable || cleanable.value != 'true'
    next if !environment || environment.value != 'staging'
    next if !branch || branch.value == 'master'

    # Find the latest command since the instance was deployed
    latest_command = Time.parse(instance.created_at)
    config.opsworks.describe_commands(instance_id: instance.instance_id).commands.each do |command|
      next if config.clean_commands_to_ignore.include?(command.type)
      completed_at = Time.parse(command.completed_at || command.acknowledged_at || command.created_at)
      latest_command = completed_at if completed_at > latest_command
    end

    # If the latest deployment is greater than the maximum alive time allowed remove the instance.
    if Time.now.to_i - latest_command.to_i > config.max_instance_duration
      terminate_instance(instance.instance_id)
      terminated_instances << instance
    end
  end

  if terminated_instances.any?
    puts "Terminated instances: #{terminated_instances.map(&:hostname).join("\n")}"
  else
    puts 'No unused instances old enough to terminate.'
  end
end

#downObject



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/qops/deployment/instances.rb', line 115

def down
  initialize_run

  # Get the instance to shutdown
  if config.deploy_type == 'staging'
    instance = retrieve_instance
  elsif config.deploy_type == 'production'
    instance = retrieve_instances.first
  end

  if instance.nil?
    puts 'No instance available to shutdown'
    exit(0)
  else
    instance_id = instance.instance_id
  end

  terminate_instance(instance_id)

  puts 'Success'
end

#rebuildObject



138
139
140
141
# File 'lib/qops/deployment/instances.rb', line 138

def rebuild
  down
  up
end

#run_commandObject



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/qops/deployment/instances.rb', line 191

def run_command
  initialize_run
  instances = retrieve_instances

  puts "Preparing to run command to all servers (#{instances.map(&:hostname).join(', ')})"

  command = ask('Which command you want to execute?', limited_to: %w[setup configure install_dependencies update_dependencies])

  option = ask('Which command you want to execute?', limited_to: %w[all_in_once one_by_one])

  base_deployment_params = {
    stack_id: config.stack_id,
    command: { name: command.to_s }
  }

  manifest = { environment: config.deploy_type }

  case option
  when 'all_in_once'
    print "Run command #{command} on all instances at once ..."
    deployment_params = base_deployment_params.deep_dup
    run_opsworks_command(deployment_params)
    ping_slack(
      'Quandl::Slack::Release',
      "Run command: `#{command}` on all instances",
      'success',
      manifest.merge(
        app_name: config.app_name,
        command: 'deploy',
        migrate: false,
        completed: Time.now,
        hostname: instances.map(&:hostname),
        instance_id: instances.map(&:instance_id)
      )
    )
  else
    instances.each do |instance|
      print "Run command #{command} on instance #{instance.ec2_instance_id}"

      run_opsworks_command(base_deployment_params, [instance.instance_id])

      ping_slack('Quandl::Slack::InstanceDown', "Run command: `#{command}` on existing instance", 'success',
                 manifest.merge(
                   completed: Time.now,
                   hostname: instance.hostname,
                   instance_id: instance.instance_id,
                   private_ip: instance.private_ip,
                   public_ip: instance.public_ip
                 ))
      puts 'Success'
      break if instance.instance_id == instances.last.instance_id
      delay = config.wait_deploy
      puts "wait for #{delay / 60.0} mintues"
      sleep delay
    end
  end
end

#upObject



7
8
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
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/qops/deployment/instances.rb', line 7

def up
  initialize_run

  # Get the instance(s) to work with if they exist. In production we always create a new instacne
  instance = retrieve_instance if config.deploy_type == 'staging'

  # Create the instance if necessary
  if instance
    instance_id = instance.instance_id
    puts "Existing instance #{requested_hostname}"
  else
    params = {
      stack_id: config.stack_id,
      layer_ids: [config.layer_id],
      instance_type: config.instance_type,
      os: config.opsworks_os,
      hostname: requested_hostname,
      subnet_id: config.subnet,
      auto_scaling_type: config.autoscale_type,
      architecture: 'x86_64',
      root_device_type: 'ebs',
      block_device_mappings: [
        {
          device_name: 'ROOT_DEVICE',
          ebs: {
            volume_size: config.root_volume_size,
            volume_type: 'gp2',
            delete_on_termination: true
          }
        }
      ],
      ebs_optimized: config.ebs_optimize
    }
    puts 'Creating instance with params: ' + params.inspect
    instance_id = config.opsworks.create_instance(params).data.instance_id
    creating_instance = true
  end

  instance_results = config.opsworks.describe_instances(instance_ids: [instance_id])
  instance = instance_results.data.instances.first

  # Set up the automatic boot scheduler
  if config.autoscale_type == 'timer'
    print 'Setting up weekly schedule ...'
    config.opsworks.set_time_based_auto_scaling(instance_id: instance_id, auto_scaling_schedule: config.schedule)
    print "done\n"
  end

  # Record the initial instance before doing anything.
  initial_instance_state = instance

  # Start the instance if necessary
  print 'Booting instance ...'
  config.opsworks.start_instance(instance_id: instance_id) unless %w[online booting].include?(instance.status)

  manifest = {
    environment: config.deploy_type,
    app_name: config.app_name,
    command: 'add instance'
  }

  # Boot the instance
  iterator(manifest) do |i|
    instance_results = config.opsworks.describe_instances(instance_ids: [instance_id])
    instance = instance_results.data.instances.first

    if %w[booting requested pending].include?(instance.status)
      print '.'
      print " #{instance.status} :" if been_a_minute?(i)
    else
      puts ' ' + instance.status
      true
    end
  end

  puts "Public IP: #{instance.public_ip}"
  puts "Private IP: #{instance.private_ip}"

  tag_instance(instance)
  setup_instance(instance, initial_instance_state, manifest)

  if creating_instance
    ping_slack(
      'Quandl::Slack::InstanceUp',
      'Created another instance',
      'success',
      manifest.merge(
        completed: Time.now,
        hostname: instance.hostname,
        instance_id: instance.instance_id,
        private_ip: instance.private_ip,
        public_ip: instance.public_ip.blank? ? 'N/A' : instance.public_ip
      )
    )
  end

  # For Elasticsearch cluster, register with public elb
  if config.option?(:public_search_elb)
    print "Register instance #{instance.ec2_instance_id} to elb #{config.public_search_elb}"
    config.elb.register_instances_with_load_balancer(load_balancer_name: config.public_search_elb.to_s,
                                                     instances: [{ instance_id: instance.ec2_instance_id.to_s }])
  end

  # Deploy the latest code to instance
  Qops::Deploy.new([], options).app
end