Class: Bosh::Director::DeploymentPlan::Job

Inherits:
Object
  • Object
show all
Includes:
Template::PropertyHelper
Defined in:
lib/bosh/director/deployment_plan/job.rb

Constant Summary collapse

VALID_LIFECYCLE_PROFILES =
%w(service errand)
DEFAULT_LIFECYCLE_PROFILE =
'service'
VALID_JOB_STATES =

started, stopped and detached are real states (persisting in DB and reflecting target instance state) recreate and restart are two virtual states (both set target instance state to “started” and set appropriate instance spec modifiers)

%w(started stopped detached recreate restart)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(logger) ⇒ Job

Returns a new instance of Job.



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/bosh/director/deployment_plan/job.rb', line 88

def initialize(logger)
  @logger = logger

  @release = nil
  @templates = []
  @all_properties = nil # All properties available to job
  @properties = nil # Actual job properties

  @instances = []
  @desired_instances = []
  @unneeded_instances = []
  @instance_states = {}
  @default_network = {}

  @packages = {}
  @link_paths = {}
  @resolved_links = {}
  @migrated_from = []
  @availability_zones = []

  @instance_plans = []
end

Instance Attribute Details

#all_propertiesObject

Returns the value of attribute all_properties.



73
74
75
# File 'lib/bosh/director/deployment_plan/job.rb', line 73

def all_properties
  @all_properties
end

#availability_zonesObject

Returns the value of attribute availability_zones.



71
72
73
# File 'lib/bosh/director/deployment_plan/job.rb', line 71

def availability_zones
  @availability_zones
end

#canonical_nameString

Returns Job canonical name (mostly for DNS).

Returns:

  • (String)

    Job canonical name (mostly for DNS)



26
27
28
# File 'lib/bosh/director/deployment_plan/job.rb', line 26

def canonical_name
  @canonical_name
end

#default_networkObject

Returns the value of attribute default_network.



43
44
45
# File 'lib/bosh/director/deployment_plan/job.rb', line 43

def default_network
  @default_network
end

#desired_instancesObject

Returns the value of attribute desired_instances.



79
80
81
# File 'lib/bosh/director/deployment_plan/job.rb', line 79

def desired_instances
  @desired_instances
end

#envDeploymentPlan::Env

Returns:



41
42
43
# File 'lib/bosh/director/deployment_plan/job.rb', line 41

def env
  @env
end

#instance_statesHash<Integer, String>

Returns Individual instance expected states.

Returns:

  • (Hash<Integer, String>)

    Individual instance expected states



69
70
71
# File 'lib/bosh/director/deployment_plan/job.rb', line 69

def instance_states
  @instance_states
end

#instancesObject

to preserve interface for UpdateStep – switch to instance_plans eventually



59
60
61
# File 'lib/bosh/director/deployment_plan/job.rb', line 59

def instances
  @instances
end

#lifecycleString

Returns Lifecycle profile.

Returns:

  • (String)

    Lifecycle profile



23
24
25
# File 'lib/bosh/director/deployment_plan/job.rb', line 23

def lifecycle
  @lifecycle
end

Returns the value of attribute link_paths.



81
82
83
# File 'lib/bosh/director/deployment_plan/job.rb', line 81

def link_paths
  @link_paths
end

#migrated_fromObject

Returns the value of attribute migrated_from.



77
78
79
# File 'lib/bosh/director/deployment_plan/job.rb', line 77

def migrated_from
  @migrated_from
end

#nameString

Returns Job name.

Returns:

  • (String)

    Job name



20
21
22
# File 'lib/bosh/director/deployment_plan/job.rb', line 20

def name
  @name
end

#networksObject

Returns the value of attribute networks.



75
76
77
# File 'lib/bosh/director/deployment_plan/job.rb', line 75

def networks
  @networks
end

#packagesHash<String, DeploymentPlan::Package] Packages included into this job

Returns Hash<String, DeploymentPlan::Package] Packages included into this job.

Returns:

  • (Hash<String, DeploymentPlan::Package] Packages included into this job)

    Hash<String, DeploymentPlan::Package] Packages included into this job



53
54
55
# File 'lib/bosh/director/deployment_plan/job.rb', line 53

def packages
  @packages
end

#persistent_disk_typeDiskType

Returns Persistent disk type (or nil).

Returns:

  • (DiskType)

    Persistent disk type (or nil)



29
30
31
# File 'lib/bosh/director/deployment_plan/job.rb', line 29

def persistent_disk_type
  @persistent_disk_type
end

#propertiesHash

Returns Job properties.

Returns:

  • (Hash)

    Job properties



49
50
51
# File 'lib/bosh/director/deployment_plan/job.rb', line 49

def properties
  @properties
end

#releaseDeploymentPlan::ReleaseVersion

Returns Release this job belongs to.

Returns:



32
33
34
# File 'lib/bosh/director/deployment_plan/job.rb', line 32

def release
  @release
end

#stateString

Returns Expected job state.

Returns:

  • (String)

    Expected job state



66
67
68
# File 'lib/bosh/director/deployment_plan/job.rb', line 66

def state
  @state
end

#stemcellDeploymentPlan::Stemcell



35
36
37
# File 'lib/bosh/director/deployment_plan/job.rb', line 35

def stemcell
  @stemcell
end

#templatesArray<DeploymentPlan::Template] Templates included into the job

Returns Array<DeploymentPlan::Template] Templates included into the job.

Returns:



46
47
48
# File 'lib/bosh/director/deployment_plan/job.rb', line 46

def templates
  @templates
end

#unneeded_instancesArray<Models::Instance>

Returns List of excess instance models that are not needed for current deployment.

Returns:

  • (Array<Models::Instance>)

    List of excess instance models that are not needed for current deployment



63
64
65
# File 'lib/bosh/director/deployment_plan/job.rb', line 63

def unneeded_instances
  @unneeded_instances
end

#updateDeploymentPlan::UpdateConfig

Returns Job update settings.

Returns:



56
57
58
# File 'lib/bosh/director/deployment_plan/job.rb', line 56

def update
  @update
end

#vm_typeDeploymentPlan::VmType



38
39
40
# File 'lib/bosh/director/deployment_plan/job.rb', line 38

def vm_type
  @vm_type
end

Class Method Details

.convert_from_legacy_spec(job_spec) ⇒ Object

Takes in a job spec and returns a job spec in the new format, if it needs to be modified. The new format has “templates” key, which is an array with each template’s data. This is used for job collocation, specifically for the agent’s current job spec when compared to the director’s. We only convert their template to a single array entry because it should be impossible for the agent to have a job spec with multiple templates in legacy form.



131
132
133
134
135
136
137
138
139
140
# File 'lib/bosh/director/deployment_plan/job.rb', line 131

def self.convert_from_legacy_spec(job_spec)
  return job_spec if !self.is_legacy_spec?(job_spec)
  template = {
    "name" => job_spec["template"],
    "version" => job_spec["version"],
    "sha1" => job_spec["sha1"],
    "blobstore_id" => job_spec["blobstore_id"]
  }
  job_spec["templates"] = [template]
end

.is_legacy_spec?(job_spec) ⇒ Boolean

Returns:

  • (Boolean)


111
112
113
# File 'lib/bosh/director/deployment_plan/job.rb', line 111

def self.is_legacy_spec?(job_spec)
  !job_spec.has_key?("templates")
end

.parse(plan, job_spec, event_log, logger) ⇒ Object



83
84
85
86
# File 'lib/bosh/director/deployment_plan/job.rb', line 83

def self.parse(plan, job_spec, event_log, logger)
  parser = JobSpecParser.new(plan, event_log, logger)
  parser.parse(job_spec)
end

Instance Method Details

#add_instance_plans(instance_plans) ⇒ Object



115
116
117
# File 'lib/bosh/director/deployment_plan/job.rb', line 115

def add_instance_plans(instance_plans)
  @instance_plans = instance_plans
end


307
308
309
310
# File 'lib/bosh/director/deployment_plan/job.rb', line 307

def add_link_path(template_name, link_name, link_path)
  @link_paths[template_name] ||= {}
  @link_paths[template_name][link_name] = link_path
end


295
296
297
# File 'lib/bosh/director/deployment_plan/job.rb', line 295

def add_resolved_link(link_name, link_spec)
  @resolved_links[link_name] = link_spec
end

#bind_instance_networks(ip_provider) ⇒ Object

TODO: Job should not be responsible for reserving IPs. Consider moving this somewhere else? Maybe in the consumer?



265
266
267
268
269
270
271
272
273
274
# File 'lib/bosh/director/deployment_plan/job.rb', line 265

def bind_instance_networks(ip_provider)
  needed_instance_plans
    .flat_map(&:network_plans)
    .reject(&:obsolete?)
    .reject(&:existing?)
    .each do |network_plan|
    reservation = network_plan.reservation
    ip_provider.reserve(reservation)
  end
end

#bind_instances(ip_provider) ⇒ Object



259
260
261
262
# File 'lib/bosh/director/deployment_plan/job.rb', line 259

def bind_instances(ip_provider)
  instances.each(&:ensure_model_bound)
  bind_instance_networks(ip_provider)
end

#bind_propertiesObject

Extracts only the properties needed by this job. This is decoupled from parsing properties because templates need to be bound to their models before ‘bind_properties’ is being called (as we persist job template property definitions in DB).



229
230
231
# File 'lib/bosh/director/deployment_plan/job.rb', line 229

def bind_properties
  @properties = filter_properties(@all_properties)
end

#can_run_as_errand?Boolean

Returns:

  • (Boolean)


280
281
282
# File 'lib/bosh/director/deployment_plan/job.rb', line 280

def can_run_as_errand?
  @lifecycle == 'errand'
end

#compilation?Boolean

Returns:

  • (Boolean)


312
313
314
# File 'lib/bosh/director/deployment_plan/job.rb', line 312

def compilation?
  false
end

#instance(index) ⇒ Object



206
207
208
# File 'lib/bosh/director/deployment_plan/job.rb', line 206

def instance(index)
  @instances[index]
end

#instance_plans_with_missing_vmsObject



289
290
291
292
293
# File 'lib/bosh/director/deployment_plan/job.rb', line 289

def instance_plans_with_missing_vms
  needed_instance_plans.reject do |instance_plan|
    instance_plan.instance.vm_created? || instance_plan.instance.state == 'detached'
  end
end


303
304
305
# File 'lib/bosh/director/deployment_plan/job.rb', line 303

def link_path(template_name, link_name)
  @link_paths.fetch(template_name, {})[link_name]
end


299
300
301
# File 'lib/bosh/director/deployment_plan/job.rb', line 299

def link_spec
  @resolved_links
end

#needed_instance_plansObject



151
152
153
# File 'lib/bosh/director/deployment_plan/job.rb', line 151

def needed_instance_plans
  sorted_instance_plans
end

#obsolete_instance_plansObject



143
144
145
# File 'lib/bosh/director/deployment_plan/job.rb', line 143

def obsolete_instance_plans
  @instance_plans.select(&:obsolete?)
end

#package_specHash<String, Hash>

Returns package specs for all packages in the job indexed by package name. To be used by all instances of the job to populate agent state.

Returns:

  • (Hash<String, Hash>)

    All package specs indexed by package name



197
198
199
200
201
202
203
204
# File 'lib/bosh/director/deployment_plan/job.rb', line 197

def package_spec
  result = {}
  @packages.each do |name, package|
    result[name] = package.spec
  end

  result.select { |name, _| run_time_dependencies.include? name }
end

#persistent_disk=(disk_size) ⇒ Object

reverse compatibility: translate disk size into a disk pool



285
286
287
# File 'lib/bosh/director/deployment_plan/job.rb', line 285

def persistent_disk=(disk_size)
  @persistent_disk_type = DiskType.new(SecureRandom.uuid, disk_size, {})
end

#sorted_instance_plansObject



119
120
121
122
# File 'lib/bosh/director/deployment_plan/job.rb', line 119

def sorted_instance_plans
  @sorted_instance_plans ||= InstancePlanSorter.new(@logger)
                             .sort(@instance_plans.reject(&:obsolete?))
end

#specHash

Returns job spec as a Hash. To be used by all instances of the job to populate agent state.

Returns:

  • (Hash)

    Hash representation



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
189
190
191
192
# File 'lib/bosh/director/deployment_plan/job.rb', line 162

def spec
  first_template = @templates[0]
  result = {
    "name" => @name,
    "templates" => [],
    # --- Legacy ---
    "template" => first_template.name,
    "version" => first_template.version,
    "sha1" => first_template.sha1,
    "blobstore_id" => first_template.blobstore_id
  }
  if first_template.logs
    result["logs"] = first_template.logs
  end
  # --- /Legacy ---

  @templates.each do |template|
    template_entry = {
      "name" => template.name,
      "version" => template.version,
      "sha1" => template.sha1,
      "blobstore_id" => template.blobstore_id
    }
    if template.logs
      template_entry["logs"] = template.logs
    end
    result["templates"] << template_entry
  end

  result
end

#starts_on_deploy?Boolean

Returns:

  • (Boolean)


276
277
278
# File 'lib/bosh/director/deployment_plan/job.rb', line 276

def starts_on_deploy?
  @lifecycle == 'service'
end

#state_for_instance(instance_model) ⇒ String?

Returns the state state of job instance by its index

Parameters:

  • index (Integer)

    Instance index

Returns:

  • (String, nil)

    Instance state (nil if not specified)



213
214
215
# File 'lib/bosh/director/deployment_plan/job.rb', line 213

def state_for_instance(instance_model)
  @instance_states[instance_model.uuid] || @instance_states[instance_model.index.to_s] || @state
end

#use_compiled_package(compiled_package_model) ⇒ void

This method returns an undefined value.

Registers compiled package with this job.

Parameters:



220
221
222
223
# File 'lib/bosh/director/deployment_plan/job.rb', line 220

def use_compiled_package(compiled_package_model)
  compiled_package = CompiledPackage.new(compiled_package_model)
  @packages[compiled_package.name] = compiled_package
end

#validate_package_names_do_not_collide!Object



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/bosh/director/deployment_plan/job.rb', line 233

def validate_package_names_do_not_collide!
  releases_by_package_names = templates
                                .reduce([]) { |memo, t| memo + t.model.package_names.product([t.release]) }
                                .reduce({}) { |memo, package_name_and_release_version|
    package_name = package_name_and_release_version.first
    release_version = package_name_and_release_version.last
    memo[package_name] ||= Set.new
    memo[package_name] << release_version
    memo
  }

  releases_by_package_names.each do |package_name, releases|
    if releases.size > 1
      release1, release2 = releases.to_a[0..1]
      offending_template1 = templates.find { |t| t.release == release1 }
      offending_template2 = templates.find { |t| t.release == release2 }

      raise JobPackageCollision,
        "Package name collision detected in job `#{@name}': "\
            "template `#{release1.name}/#{offending_template1.name}' depends on package `#{release1.name}/#{package_name}', "\
            "template `#{release2.name}/#{offending_template2.name}' depends on `#{release2.name}/#{package_name}'. " +
          'BOSH cannot currently collocate two packages with identical names from separate releases.'
    end
  end
end