Class: Bosh::Deployer::InstanceManager

Inherits:
Object
  • Object
show all
Extended by:
Helpers
Includes:
Helpers
Defined in:
lib/bosh/deployer/instance_manager.rb,
lib/bosh/deployer/instance_manager/aws.rb,
lib/bosh/deployer/instance_manager/vcloud.rb,
lib/bosh/deployer/instance_manager/vsphere.rb,
lib/bosh/deployer/instance_manager/openstack.rb

Direct Known Subclasses

Aws, Openstack, Vcloud, Vsphere

Defined Under Namespace

Classes: Aws, Openstack, Vcloud, Vsphere

Constant Summary collapse

CONNECTION_EXCEPTIONS =
[
  Bosh::Agent::Error,
  Errno::ECONNREFUSED,
  Errno::ETIMEDOUT,
  DirectorGatewayError,
  HTTPClient::ConnectTimeoutError
]

Constants included from Helpers

Helpers::DEPLOYMENTS_FILE

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Helpers

close_ssh_sessions, cloud_plugin, dig_hash, is_tgz?, process_exists?, remote_tunnel, socket_readable?, strip_relative_path

Constructor Details

#initialize(config, config_sha1, ui_messager) ⇒ InstanceManager

Returns a new instance of InstanceManager.



40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/bosh/deployer/instance_manager.rb', line 40

def initialize(config, config_sha1, ui_messager)
  Config.configure(config)

  @state_yml = File.join(config['dir'], DEPLOYMENTS_FILE)
  load_state(config['name'])

  Config.uuid = state.uuid

  @config_sha1 = config_sha1
  @ui_messager = ui_messager
  @renderer = LoggerRenderer.new
end

Instance Attribute Details

#rendererObject

Returns the value of attribute renderer.



22
23
24
# File 'lib/bosh/deployer/instance_manager.rb', line 22

def renderer
  @renderer
end

#stateObject (readonly)

Returns the value of attribute state.



21
22
23
# File 'lib/bosh/deployer/instance_manager.rb', line 21

def state
  @state
end

Class Method Details

.create(config) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/bosh/deployer/instance_manager.rb', line 24

def self.create(config)
  plugin_name = cloud_plugin(config)

  begin
    require "bosh/deployer/instance_manager/#{plugin_name}"
  rescue LoadError
    err "Could not find Provider Plugin: #{plugin_name}"
  end

  config_sha1 = Bosh::Deployer::HashFingerprinter.new.sha1(config)
  ui_messager = Bosh::Deployer::UiMessager.for_deployer

  plugin_class = InstanceManager.const_get(plugin_name.capitalize)
  plugin_class.new(config, config_sha1, ui_messager)
end

Instance Method Details

#agentObject



57
58
59
# File 'lib/bosh/deployer/instance_manager.rb', line 57

def agent
  Config.agent
end

#apply(spec = nil) ⇒ Object



374
375
376
377
378
379
380
381
382
383
384
385
386
387
# File 'lib/bosh/deployer/instance_manager.rb', line 374

def apply(spec = nil)
  agent_stop

  spec ||= @apply_spec

  step 'Applying micro BOSH spec' do
    # first update spec with infrastructure specific stuff
    update_spec(spec)
    # then update spec with generic changes
    agent.run_task(:apply, spec.update(bosh_ip, service_ip))
  end

  agent_start
end

#attach_disk(disk_cid, is_create = false) ⇒ Object

it is up to the caller to save/update disk state info



310
311
312
313
314
315
# File 'lib/bosh/deployer/instance_manager.rb', line 310

def attach_disk(disk_cid, is_create = false)
  return unless disk_cid

  cloud.attach_disk(state.vm_cid, disk_cid)
  mount_disk(disk_cid)
end

#attach_missing_diskObject



328
329
330
331
332
# File 'lib/bosh/deployer/instance_manager.rb', line 328

def attach_missing_disk
  if state.disk_cid
    attach_disk(state.disk_cid, true)
  end
end

#check_dependenciesObject



397
398
399
# File 'lib/bosh/deployer/instance_manager.rb', line 397

def check_dependencies
  # nothing to check, move on...
end

#check_persistent_diskObject



334
335
336
337
338
339
340
341
342
# File 'lib/bosh/deployer/instance_manager.rb', line 334

def check_persistent_disk
  return if state.disk_cid.nil?
  agent_disk_cid = disk_info.first
  if agent_disk_cid != state.disk_cid
    err "instance #{state.vm_cid} has invalid disk: " +
          "Agent reports #{agent_disk_cid} while " +
          "deployer's record shows #{state.disk_cid}"
  end
end

#cloudObject



53
54
55
# File 'lib/bosh/deployer/instance_manager.rb', line 53

def cloud
  Config.cloud
end

#create(stemcell_tgz, stemcell_archive) ⇒ Object

rubocop:disable MethodLength



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/bosh/deployer/instance_manager.rb', line 110

def create(stemcell_tgz, stemcell_archive)
  err "VM #{state.vm_cid} already exists" if state.vm_cid
  if state.stemcell_cid && state.stemcell_cid != state.stemcell_name
    err "stemcell #{state.stemcell_cid} already exists"
  end

  renderer.enter_stage('Deploy Micro BOSH', 11)

  state.stemcell_cid = create_stemcell(stemcell_tgz)
  state.stemcell_name = File.basename(stemcell_tgz, '.tgz')
  save_state

  step "Creating VM from #{state.stemcell_cid}" do
    state.vm_cid = create_vm(state.stemcell_cid)
    (state.vm_cid, { 'Name' => state.name })
    discover_bosh_ip
  end
  save_state

  step 'Waiting for the agent' do
    begin
      wait_until_agent_ready
    rescue *CONNECTION_EXCEPTIONS
      err 'Unable to connect to Bosh agent. Check logs for more details.'
    end
  end

  step 'Updating persistent disk' do
    update_persistent_disk
  end

  unless @apply_spec
    step 'Fetching apply spec' do
      @apply_spec = Specification.new(agent.release_apply_spec)
    end
  end

  apply

  step 'Waiting for the director' do
    begin
      wait_until_director_ready
    rescue *CONNECTION_EXCEPTIONS
      err 'Unable to connect to Bosh Director. Retry manually or check logs for more details.'
    end
  end

  # Capture stemcell and config sha1 here (end of the deployment)
  # to avoid locking deployer out if this deployment does not succeed
  save_fingerprints(stemcell_tgz, stemcell_archive)
end

#create_deployment(stemcell_tgz, stemcell_archive) ⇒ Object



97
98
99
# File 'lib/bosh/deployer/instance_manager.rb', line 97

def create_deployment(stemcell_tgz, stemcell_archive)
  with_lifecycle { create(stemcell_tgz, stemcell_archive) }
end

#create_diskObject



280
281
282
283
284
285
286
# File 'lib/bosh/deployer/instance_manager.rb', line 280

def create_disk
  step 'Create disk' do
    size = Config.resources['persistent_disk']
    state.disk_cid = cloud.create_disk(size, state.vm_cid)
    save_state
  end
end

#create_stemcell(stemcell_tgz) ⇒ Object

rubocop:disable MethodLength



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
# File 'lib/bosh/deployer/instance_manager.rb', line 206

def create_stemcell(stemcell_tgz)
  unless is_tgz?(stemcell_tgz)
    step 'Using existing stemcell' do
    end

    return stemcell_tgz
  end

  Dir.mktmpdir('sc-') do |stemcell|
    step 'Unpacking stemcell' do
      run_command("tar -zxf #{stemcell_tgz} -C #{stemcell}")
    end

    @apply_spec = Specification.load_from_stemcell(stemcell)

    # load properties from stemcell manifest
    properties = load_stemcell_manifest(stemcell)

    # override with values from the deployment manifest
    override = Config.cloud_options['properties']['stemcell']
    properties['cloud_properties'].merge!(override) if override

    step 'Uploading stemcell' do
      cloud.create_stemcell("#{stemcell}/image", properties['cloud_properties'])
    end
  end
rescue => e
  logger.err("create stemcell failed: #{e.message}:\n#{e.backtrace.join("\n")}")
  # make sure we clean up the stemcell if something goes wrong
  delete_stemcell if is_tgz?(stemcell_tgz) && state.stemcell_cid
  raise e
end

#create_vm(stemcell_cid) ⇒ Object

rubocop:enable MethodLength



240
241
242
243
244
245
# File 'lib/bosh/deployer/instance_manager.rb', line 240

def create_vm(stemcell_cid)
  resources = Config.resources['cloud_properties']
  networks = Config.networks
  env = Config.env
  cloud.create_vm(state.uuid, stemcell_cid, resources, networks, nil, env)
end

#delete_deploymentObject



105
106
107
# File 'lib/bosh/deployer/instance_manager.rb', line 105

def delete_deployment
  with_lifecycle { destroy }
end

#delete_disk(disk_cid, vm_cid) ⇒ Object

it is up to the caller to save/update disk state info



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/bosh/deployer/instance_manager.rb', line 289

def delete_disk(disk_cid, vm_cid)
  unmount_disk(disk_cid)

  begin
    step 'Detach disk' do
      cloud.detach_disk(vm_cid, disk_cid) if vm_cid
    end
  rescue Bosh::Clouds::DiskNotAttached => e
    logger.info(e.inspect)
  end

  begin
    step 'Delete disk' do
      cloud.delete_disk(disk_cid)
    end
  rescue Bosh::Clouds::DiskNotFound => e
    logger.info(e.inspect)
  end
end

#destroyObject

rubocop:enable MethodLength



163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/bosh/deployer/instance_manager.rb', line 163

def destroy
  renderer.enter_stage('Delete micro BOSH', 7)
  agent_stop
  if state.disk_cid
    step "Deleting persistent disk `#{state.disk_cid}'" do
      delete_disk(state.disk_cid, state.vm_cid)
      state.disk_cid = nil
      save_state
    end
  end
  delete_vm
  delete_stemcell
end

#detach_disk(disk_cid) ⇒ Object



317
318
319
320
321
322
323
324
325
326
# File 'lib/bosh/deployer/instance_manager.rb', line 317

def detach_disk(disk_cid)
  unless disk_cid
    err 'Error: nil value given for persistent disk id'
  end

  unmount_disk(disk_cid)
  step 'Detach disk' do
    cloud.detach_disk(state.vm_cid, disk_cid)
  end
end

#discover_bosh_ipObject



389
390
391
# File 'lib/bosh/deployer/instance_manager.rb', line 389

def discover_bosh_ip
  bosh_ip
end

#disk_infoObject



275
276
277
278
# File 'lib/bosh/deployer/instance_manager.rb', line 275

def disk_info
  return @disk_list if @disk_list
  @disk_list = agent.list_disk
end

#disk_modelObject



65
66
67
# File 'lib/bosh/deployer/instance_manager.rb', line 65

def disk_model
  nil
end

#exists?Boolean

Returns:

  • (Boolean)


73
74
75
# File 'lib/bosh/deployer/instance_manager.rb', line 73

def exists?
  [state.vm_cid, state.stemcell_cid, state.disk_cid].any?
end

#instance_modelObject



69
70
71
# File 'lib/bosh/deployer/instance_manager.rb', line 69

def instance_model
  Models::Instance
end

#loggerObject



61
62
63
# File 'lib/bosh/deployer/instance_manager.rb', line 61

def logger
  Config.logger
end

#migrate_disk(src_disk_cid, dst_disk_cid) ⇒ Object



269
270
271
272
273
# File 'lib/bosh/deployer/instance_manager.rb', line 269

def migrate_disk(src_disk_cid, dst_disk_cid)
  step 'Migrate disk' do
    agent.run_task(:migrate_disk, src_disk_cid.to_s, dst_disk_cid.to_s)
  end
end

#mount_disk(disk_cid) ⇒ Object



253
254
255
256
257
# File 'lib/bosh/deployer/instance_manager.rb', line 253

def mount_disk(disk_cid)
  step 'Mount disk' do
    agent.run_task(:mount_disk, disk_cid.to_s)
  end
end

#service_ipObject



393
394
395
# File 'lib/bosh/deployer/instance_manager.rb', line 393

def service_ip
  bosh_ip
end

#startObject



84
85
# File 'lib/bosh/deployer/instance_manager.rb', line 84

def start
end

#step(task) ⇒ Object



77
78
79
80
81
82
# File 'lib/bosh/deployer/instance_manager.rb', line 77

def step(task)
  renderer.update(:started, task)
  result = yield
  renderer.update(:finished, task)
  result
end

#stopObject



87
88
# File 'lib/bosh/deployer/instance_manager.rb', line 87

def stop
end

#unmount_disk(disk_cid) ⇒ Object



259
260
261
262
263
264
265
266
267
# File 'lib/bosh/deployer/instance_manager.rb', line 259

def unmount_disk(disk_cid)
  step 'Unmount disk' do
    if disk_info.include?(disk_cid)
      agent.run_task(:unmount_disk, disk_cid.to_s)
    else
      logger.error("not unmounting #{disk_cid} as it doesn't belong to me: #{disk_info}")
    end
  end
end

#update(stemcell_tgz, stemcell_archive) ⇒ Object



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/bosh/deployer/instance_manager.rb', line 177

def update(stemcell_tgz, stemcell_archive)
  result, message = has_pending_changes?(state, stemcell_tgz, stemcell_archive)
  @ui_messager.info(message)
  return unless result

  renderer.enter_stage('Prepare for update', 5)

  # Reset stemcell and config sha1 before deploying
  # to make sure that if any step in current deploy fails
  # subsequent redeploys will not be skipped because sha1s matched
  reset_saved_fingerprints

  if state.vm_cid
    agent_stop
    detach_disk(state.disk_cid)
    delete_vm
  end

  # Do we always want to delete the stemcell?
  # What if we are redeploying to the same stemcell version just so
  # we can upgrade to a bigger persistent disk.
  if state.stemcell_cid
    delete_stemcell
  end

  create(stemcell_tgz, stemcell_archive)
end

#update_deployment(stemcell_tgz, stemcell_archive) ⇒ Object



101
102
103
# File 'lib/bosh/deployer/instance_manager.rb', line 101

def update_deployment(stemcell_tgz, stemcell_archive)
  with_lifecycle { update(stemcell_tgz, stemcell_archive) }
end

#update_persistent_diskObject



344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/bosh/deployer/instance_manager.rb', line 344

def update_persistent_disk
  attach_missing_disk
  check_persistent_disk

  if state.disk_cid.nil?
    create_disk
    attach_disk(state.disk_cid, true)
  elsif persistent_disk_changed?
    size = Config.resources['persistent_disk']

    # save a reference to the old disk
    old_disk_cid = state.disk_cid

    # create a new disk and attach it
    new_disk_cid = cloud.create_disk(size, state.vm_cid)
    attach_disk(new_disk_cid, true)

    # migrate data (which mounts the disks)
    migrate_disk(old_disk_cid, new_disk_cid)

    # replace the old with the new in the state file
    state.disk_cid = new_disk_cid

    # delete the old disk
    delete_disk(old_disk_cid, state.vm_cid)
  end
ensure
  save_state
end

#update_vm_metadata(vm, metadata) ⇒ Object



247
248
249
250
251
# File 'lib/bosh/deployer/instance_manager.rb', line 247

def (vm, )
  cloud.(vm, ) if cloud.respond_to?(:set_vm_metadata)
rescue Bosh::Clouds::NotImplemented => e
  logger.error(e)
end

#with_lifecycleObject



90
91
92
93
94
95
# File 'lib/bosh/deployer/instance_manager.rb', line 90

def with_lifecycle
  start
  yield
ensure
  stop
end