class Bosh::Director::DeploymentPlan::InstanceGroup

Constants

DEFAULT_LIFECYCLE_PROFILE
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)

VALID_LIFECYCLE_PROFILES

Attributes

all_properties[RW]
availability_zones[RW]
canonical_name[RW]

@return [String] Job canonical name (mostly for DNS)

default_network[RW]
desired_instances[RW]
did_change[RW]
env[RW]

@return [DeploymentPlan::Env]

instance_states[RW]

@return [Hash<Integer, String>] Individual instance expected states

instances[RW]

@return [Array<DeploymentPlan::Instance>] All job instances

lifecycle[RW]

@return [String] Lifecycle profile

migrated_from[RW]
name[RW]

@return [String] Job name

networks[RW]
packages[RW]

@return [Hash<String, DeploymentPlan::Package] Packages included into

this job
persistent_disk_type[RW]

@return [DiskType] Persistent disk type (or nil)

properties[RW]

@return [Hash] Job properties

release[RW]

@return [DeploymentPlan::ReleaseVersion] Release this job belongs to

state[RW]

@return [String] Expected job state

stemcell[RW]

@return [DeploymentPlan::Stemcell]

templates[RW]

@return [Array<DeploymentPlan::Template] Templates included into the job

unneeded_instances[RW]

@return [Array<Models::Instance>] List of excess instance models that

are not needed for current deployment
update[RW]

@return [DeploymentPlan::UpdateConfig] Job update settings

vm_extensions[RW]

@return [DeploymentPlan::VmExtension]

vm_type[RW]

@return [DeploymentPlan::VmType]

Public Class Methods

convert_from_legacy_spec(job_spec) click to toggle source

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.

# File lib/bosh/director/deployment_plan/instance_group.rb, line 138
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"]
  }

  # Supporting 'template_scoped_properties' for legacy spec is going to be messy.
  # So we will support this feature if a user want to use legacy spec. If they
  # want to use properties per template, let them use the regular way of defining
  # templates, i.e. by using the 'templates' key
  job_spec['templates'] = [template]
end
is_legacy_spec?(job_spec) click to toggle source
# File lib/bosh/director/deployment_plan/instance_group.rb, line 118
def self.is_legacy_spec?(job_spec)
  !job_spec.has_key?("templates")
end
new(logger) click to toggle source
# File lib/bosh/director/deployment_plan/instance_group.rb, line 93
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 = []

  @did_change = false
end
parse(plan, job_spec, event_log, logger, parse_options = {}) click to toggle source
# File lib/bosh/director/deployment_plan/instance_group.rb, line 88
def self.parse(plan, job_spec, event_log, logger, parse_options = {})
  parser = InstanceGroupSpecParser.new(plan, event_log, logger)
  parser.parse(job_spec, parse_options)
end

Public Instance Methods

add_instance_plans(instance_plans) click to toggle source
# File lib/bosh/director/deployment_plan/instance_group.rb, line 122
def add_instance_plans(instance_plans)
  @instance_plans = instance_plans
end
bind_instance_networks(ip_provider) click to toggle source

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

# File lib/bosh/director/deployment_plan/instance_group.rb, line 284
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) click to toggle source
# File lib/bosh/director/deployment_plan/instance_group.rb, line 278
def bind_instances(ip_provider)
  instances.each(&:ensure_model_bound)
  bind_instance_networks(ip_provider)
end
bind_properties() click to toggle source

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

# File lib/bosh/director/deployment_plan/instance_group.rb, line 248
def bind_properties
  @properties = filter_properties(@all_properties)
end
compilation?() click to toggle source
# File lib/bosh/director/deployment_plan/instance_group.rb, line 337
def compilation?
  false
end
has_network?(network_name) click to toggle source
# File lib/bosh/director/deployment_plan/instance_group.rb, line 295
def has_network?(network_name)
  networks.any? do |network|
    network.name == network_name
  end
end
instance(index) click to toggle source
# File lib/bosh/director/deployment_plan/instance_group.rb, line 225
def instance(index)
  @instances[index]
end
instance_plans_with_missing_vms() click to toggle source
# File lib/bosh/director/deployment_plan/instance_group.rb, line 314
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
is_errand?() click to toggle source
# File lib/bosh/director/deployment_plan/instance_group.rb, line 305
def is_errand?
  @lifecycle == 'errand'
end
is_service?() click to toggle source
# File lib/bosh/director/deployment_plan/instance_group.rb, line 301
def is_service?
  @lifecycle == 'service'
end
needed_instance_plans() click to toggle source
# File lib/bosh/director/deployment_plan/instance_group.rb, line 163
def needed_instance_plans
  sorted_instance_plans
end
obsolete_instance_plans() click to toggle source
# File lib/bosh/director/deployment_plan/instance_group.rb, line 155
def obsolete_instance_plans
  @instance_plans.select(&:obsolete?)
end
package_spec() click to toggle source

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. @return [Hash<String, Hash>] All package specs indexed by package name

# File lib/bosh/director/deployment_plan/instance_group.rb, line 216
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) click to toggle source

reverse compatibility: translate disk size into a disk pool

# File lib/bosh/director/deployment_plan/instance_group.rb, line 310
def persistent_disk=(disk_size)
  @persistent_disk_type = DiskType.new(SecureRandom.uuid, disk_size, {})
end
sorted_instance_plans() click to toggle source
# File lib/bosh/director/deployment_plan/instance_group.rb, line 126
def sorted_instance_plans
  @sorted_instance_plans ||= InstancePlanSorter.new(@logger)
                             .sort(@instance_plans.reject(&:obsolete?))
end
spec() click to toggle source

Returns job spec as a Hash. To be used by all instances of the job to populate agent state. @return [Hash] Hash representation

# File lib/bosh/director/deployment_plan/instance_group.rb, line 174
def spec
  if @templates.size >= 1
    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
end
state_for_instance(instance_model) click to toggle source

Returns the state state of job instance by its index @param [Integer] index Instance index @return [String, nil] Instance state (nil if not specified)

# File lib/bosh/director/deployment_plan/instance_group.rb, line 232
def state_for_instance(instance_model)
  @instance_states[instance_model.uuid] || @instance_states[instance_model.index.to_s] || @state
end
update_spec() click to toggle source
# File lib/bosh/director/deployment_plan/instance_group.rb, line 209
def update_spec
  update.to_hash
end
use_compiled_package(compiled_package_model) click to toggle source

Registers compiled package with this job. @param [Models::CompiledPackage] compiled_package_model Compiled package @return [void]

# File lib/bosh/director/deployment_plan/instance_group.rb, line 239
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!() click to toggle source
# File lib/bosh/director/deployment_plan/instance_group.rb, line 252
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 instance group '#{@name}': "\
            "job '#{release1.name}/#{offending_template1.name}' depends on package '#{release1.name}/#{package_name}', "\
            "job '#{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

Private Instance Methods

extract_template_properties(collection) click to toggle source
# File lib/bosh/director/deployment_plan/instance_group.rb, line 364
def extract_template_properties(collection)
  result = {}

  @templates.each do |template|
    # If a template has properties that were defined in the deployment manifest
    # for that template only, then we need to bind only these properties, and not
    # make them available to other templates in the same deployment job. That can
    # be done by checking @template_scoped_properties variable of each
    # template
    result[template.name] ||= {}
    if template.has_template_scoped_properties(@name)
      template.bind_template_scoped_properties(@name)
      result[template.name] = template.template_scoped_properties[@name]
    else
      template.properties.each_pair do |name, definition|
        copy_property(result[template.name], collection, name, definition["default"])
      end
    end
  end

  result
end
filter_properties(collection) click to toggle source

@param [Hash] collection All properties collection @return [Hash] Properties required by templates included in this job

# File lib/bosh/director/deployment_plan/instance_group.rb, line 345
def filter_properties(collection)
  if @templates.none? { |template| template.properties}
    result = {}
    @templates.each do |template|
      result[template.name] = collection
    end
    return result
  end

  if @templates.all? { |template| template.properties }
    return extract_template_properties(collection)
  end

  raise JobIncompatibleSpecs,
        "Instance group '#{name}' has specs with conflicting property definition styles between" +
            " its job spec templates.  This may occur if colocating jobs, one of which has a spec file including" +
            " 'properties' and one which doesn't."
end
run_time_dependencies() click to toggle source
# File lib/bosh/director/deployment_plan/instance_group.rb, line 387
def run_time_dependencies
  templates.flat_map { |template| template.package_models }.uniq.map(&:name)
end