Class: Bosh::Deployer::InstanceManager

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
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

Defined Under Namespace

Classes: Aws, Openstack, Vcloud, Vsphere

Constant Summary collapse

CONNECTION_EXCEPTIONS =
[
  Bosh::Agent::Error,
  Errno::ECONNREFUSED,
  Errno::ETIMEDOUT,
  DirectorGatewayError,
  HTTPClient::ConnectTimeoutError
]
DEPLOYMENTS_FILE =
'bosh-deployments.yml'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config, config_sha1, ui_messager, plugin_name) ⇒ InstanceManager

Returns a new instance of InstanceManager.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/bosh/deployer/instance_manager.rb', line 46

def initialize(config, config_sha1, ui_messager, plugin_name)
  @config = config

  plugin_class = InstanceManager.const_get(plugin_name.capitalize)
  @infrastructure = plugin_class.new(self, config, logger)

  @deployments_state = DeploymentsState.load_from_dir(config.base_dir, logger)
  deployments_state.load_deployment(config.name)

  config.uuid = state.uuid

  @config_sha1 = config_sha1
  @ui_messager = ui_messager
  @renderer = LoggerRenderer.new(logger)
end

Instance Attribute Details

#deployments_stateObject (readonly)

Returns the value of attribute deployments_state.



26
27
28
# File 'lib/bosh/deployer/instance_manager.rb', line 26

def deployments_state
  @deployments_state
end

#infrastructureObject (readonly)

Returns the value of attribute infrastructure.



26
27
28
# File 'lib/bosh/deployer/instance_manager.rb', line 26

def infrastructure
  @infrastructure
end

#rendererObject

Returns the value of attribute renderer.



25
26
27
# File 'lib/bosh/deployer/instance_manager.rb', line 25

def renderer
  @renderer
end

Class Method Details

.create(config) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/bosh/deployer/instance_manager.rb', line 28

def self.create(config)
  err 'No cloud properties defined' if config['cloud'].nil?
  err 'No cloud plugin defined' if config['cloud']['plugin'].nil?

  plugin_name = config['cloud']['plugin']

  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

  new(Config.configure(config), config_sha1, ui_messager, plugin_name)
end

Instance Method Details

#agentObject



402
403
404
405
406
407
408
409
410
411
412
# File 'lib/bosh/deployer/instance_manager.rb', line 402

def agent
  uri = URI.parse(config.agent_url)
  user, password = uri.userinfo.split(':', 2)
  uri.userinfo = nil
  uri.host = client_services_ip
  Bosh::Agent::HTTPClient.new(uri.to_s, {
    'user' => user,
    'password' => password,
    'reply_to' => config.uuid,
  })
end

#apply(spec = nil) ⇒ Object



377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
# File 'lib/bosh/deployer/instance_manager.rb', line 377

def apply(spec = nil)
  agent_stop

  spec ||= @apply_spec

  step 'Applying micro BOSH spec' do
    # first update spec with infrastructure specific stuff
    infrastructure.update_spec(spec)

    # then update spec with generic changes
    spec = spec.update(agent_services_ip, internal_services_ip)

    microbosh_instance = MicroboshJobInstance.new(client_services_ip, config.agent_url, logger)
    spec = microbosh_instance.render_templates(spec)

    agent.run_task(:apply, spec)
  end

  agent_start
end

#attach_disk(disk_cid, is_create = false) ⇒ Object

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



313
314
315
316
317
318
# File 'lib/bosh/deployer/instance_manager.rb', line 313

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



331
332
333
334
335
# File 'lib/bosh/deployer/instance_manager.rb', line 331

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

#check_persistent_diskObject



337
338
339
340
341
342
343
344
345
# File 'lib/bosh/deployer/instance_manager.rb', line 337

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

#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
# 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 })
  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, config)
    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



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

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

#create_stemcell(stemcell_tgz) ⇒ Object

rubocop:disable MethodLength



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

def create_stemcell(stemcell_tgz)
  unless File.extname(stemcell_tgz) == '.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, config)

    # 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 File.extname(stemcell_tgz) == '.tgz' && state.stemcell_cid
  raise e
end

#create_vm(stemcell_cid) ⇒ Object

rubocop:enable MethodLength



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

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, [], 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



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

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



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

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



320
321
322
323
324
325
326
327
328
329
# File 'lib/bosh/deployer/instance_manager.rb', line 320

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

#disk_infoObject



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

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

#migrate_disk(src_disk_cid, dst_disk_cid) ⇒ Object



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

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



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

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

#persistent_disk_cloud_propertiesObject



287
288
289
# File 'lib/bosh/deployer/instance_manager.rb', line 287

def persistent_disk_cloud_properties
  config.resources.fetch('persistent_disk_cloud_properties', {})
end

#save_stateObject



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

def save_state
  deployments_state.save(infrastructure)
end

#step(task) ⇒ Object



83
84
85
86
87
88
# File 'lib/bosh/deployer/instance_manager.rb', line 83

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

#unmount_disk(disk_cid) ⇒ Object



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

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



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

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



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

def update_persistent_disk
  attach_missing_disk
  check_persistent_disk

  if state.disk_cid.nil?
    create_disk
    attach_disk(state.disk_cid, true)
  elsif infrastructure.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, persistent_disk_cloud_properties, 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



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

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
  infrastructure.start
  yield
ensure
  infrastructure.stop
end