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

def initialize(config, config_sha1, ui_messager, plugin_name)
  Config.configure(config)
  @config = Config

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

  @deployments_state = DeploymentsState.load_from_dir(config['dir'], logger)
  load_state(config['name'])

  Config.uuid = state.uuid

  @config_sha1 = config_sha1
  @ui_messager = ui_messager
  @renderer = LoggerRenderer.new
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, config_sha1, ui_messager, plugin_name)
end

Instance Method Details

#apply(spec = nil) ⇒ Object



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 378

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(bosh_ip, infrastructure.service_ip)

    microbosh_job_instance = MicroboshJobInstance.new(bosh_ip, Config.agent_url, logger)
    spec = microbosh_job_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



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

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



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

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

#check_dependenciesObject



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

def check_dependencies
  infrastructure.check_dependencies
end

#check_persistent_diskObject



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

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



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
161
162
163
164
# File 'lib/bosh/deployer/instance_manager.rb', line 114

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 })
    infrastructure.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



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

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

#create_diskObject



284
285
286
287
288
289
290
# File 'lib/bosh/deployer/instance_manager.rb', line 284

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



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

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)

    # 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



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

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



109
110
111
# File 'lib/bosh/deployer/instance_manager.rb', line 109

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



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

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



167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/bosh/deployer/instance_manager.rb', line 167

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



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

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



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

def discover_bosh_ip
  infrastructure.discover_bosh_ip
end

#disk_infoObject



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

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

#migrate_disk(src_disk_cid, dst_disk_cid) ⇒ Object



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

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



257
258
259
260
261
# File 'lib/bosh/deployer/instance_manager.rb', line 257

def mount_disk(disk_cid)
  step 'Mount disk' do
    agent.run_task(:mount_disk, disk_cid.to_s)
  end
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



87
88
89
90
91
92
# File 'lib/bosh/deployer/instance_manager.rb', line 87

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

#unmount_disk(disk_cid) ⇒ Object



263
264
265
266
267
268
269
270
271
# File 'lib/bosh/deployer/instance_manager.rb', line 263

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



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

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



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

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

#update_persistent_diskObject



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

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



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

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

#with_lifecycleObject



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

def with_lifecycle
  infrastructure.start
  yield
ensure
  infrastructure.stop
end