cloudfoundry-community/bosh-cloudstack-cpi

View on GitHub
bat/lib/bat/deployment_helper.rb

Summary

Maintainability
A
2 hrs
Test Coverage
require 'tmpdir'
require 'logger'

module Bat
  module DeploymentHelper
    def deployment
      load_deployment_spec
      @deployment ||= Bat::Deployment.new(@spec)
    end

    def reload_deployment_spec
      @spec = nil
      load_deployment_spec
    end

    def load_deployment_spec
      @spec ||= Psych.load_file(@env.deployment_spec_path)
      # Always set the batlight.missing to something, or deployments will fail.
      # It is used for negative testing.
      @spec['properties']['batlight'] ||= {}
      @spec['properties']['batlight']['missing'] = 'nope'
      @spec['properties']['dns'] = [@env.dns_host]
    end

    # if with_deployment() is called without a block, it is up to the caller to
    # remove the generated deployment file
    # @return [Bat::Deployment]
    def with_deployment(spec = {}, &block)
      deployed = false # move into Deployment ?
      deployment = Bat::Deployment.new(@spec.merge(spec))

      if !block_given?
        return deployment
      elsif block.arity == 0
        @bosh_runner.bosh("deployment #{deployment.to_path}").should succeed
        @bosh_runner.bosh('deploy').should succeed
        deployed = true
        yield
      elsif block.arity == 1
        yield deployment
      elsif block.arity == 2
        @bosh_runner.bosh("deployment #{deployment.to_path}").should succeed
        result = @bosh_runner.bosh('deploy')
        result.should succeed
        deployed = true
        yield deployment, result
      else
        raise "unknown arity: #{block.arity}"
      end
    ensure
      if block_given?
        deployment.delete if deployment
        if deployed
          @bosh_runner.bosh("delete deployment #{deployment.name}").should succeed
        end
      end
    end

    def with_tmpdir
      dir = nil
      back = Dir.pwd
      Dir.mktmpdir do |tmpdir|
        dir = tmpdir
        Dir.chdir(dir)
        yield dir
      end
    ensure
      Dir.chdir(back)
      FileUtils.rm_rf(dir) if dir
    end

    def use_job(job)
      @spec['properties']['job'] = job
    end

    def use_templates(templates)
      @spec['properties']['template'] = templates.map { |item| "\n      - #{item}" }.join
    end

    def use_job_instances(count)
      @spec['properties']['instances'] = count
    end

    def use_deployment_name(name)
      @spec['properties']['name'] = name
    end

    def use_additional_dns_server(dns_server)
      # Make sure working dns is the first entry in resolv.conf
      @spec['properties']['dns'] = [@env.dns_host, dns_server]
    end

    def deployment_name
      @spec.fetch('properties', {}).fetch('name', 'bat')
    end

    def use_vip
      @spec['properties']['use_vip'] = true
    end

    def no_vip
      @spec['properties']['use_vip'] = false
    end

    def public_ip
      # For AWS and OpenStack, the elastic IP is the public IP
      # For vSphere and vCloud, the static_ip is the public IP
      @spec['properties']['vip'] || @spec['properties']['deployment_static_ip']
    end

    def use_static_ip
      @spec['properties']['use_static_ip'] = true
      @spec['properties']['deployment_static_ip'] = static_ip
      @spec['properties']['mbus'] = mbus_url(static_ip)
    end

    def no_static_ip
      @spec['properties']['use_static_ip'] = false
    end

    def static_ip
      @spec['properties']['static_ip']
    end

    def use_second_static_ip
      @spec['properties']['use_static_ip'] = true
      @spec['properties']['deployment_static_ip'] = second_static_ip
      @spec['properties']['mbus'] = mbus_url(second_static_ip)
    end

    def second_static_ip
      @spec['properties']['second_static_ip']
    end

    def use_persistent_disk(size)
      @spec['properties']['persistent_disk'] = size
    end

    def use_canaries(count)
      @spec['properties']['canaries'] = count
    end

    def use_pool_size(size)
      @spec['properties']['pool_size'] = size
    end

    def use_password(passwd)
      @spec['properties']['password'] = passwd
    end

    def use_failing_job
      @spec['properties']['batlight']['fail'] = 'control'
    end

    def dynamic_network?
      network_type == 'dynamic'
    end

    def network_type
      @spec['properties'].fetch('network', {}).fetch('type', nil)
    end

    def get_task_id(output, state = 'done')
      task_regex = /Task (\d+) #{state}/
      expect(output).to match(task_regex)
      match = output.match(task_regex)
      match[1]
    end

    def events(task_id)
      result = @bosh_runner.bosh("task #{task_id} --raw")
      result.should succeed_with /Task \d+ \w+/

      event_list = []
      result.output.split("\n").each do |line|
        event = parse_json_safely(line)
        event_list << event if event
      end
      event_list
    end

    def start_and_finish_times_for_job_updates(task_id)
      jobs = {}
      events(task_id).select do |e|
        e['stage'] == 'Updating job' && %w(started finished).include?(e['state'])
      end.each do |e|
        jobs[e['task']] ||= {}
        jobs[e['task']][e['state']] = e['time']
      end
      jobs
    end

    private

    def mbus_url(ip)
      "nats://nats:0b450ada9f830085e2cdeff6@#{ip}:4222"
    end

    def spec
      @spec ||= {}
    end

    def parse_json_safely(line)
      JSON.parse(line)
    rescue JSON::ParserError => e
      @logger.info("Failed to parse '#{line}': #{e.inspect}")
      nil
    end
  end
end