2011-07-30 08:12:53 +04:00
|
|
|
#!/usr/bin/env ruby
|
|
|
|
require 'rake'
|
|
|
|
require 'yaml'
|
|
|
|
require 'set'
|
|
|
|
require 'pp'
|
|
|
|
|
|
|
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
|
|
|
|
|
|
class JobManager
|
|
|
|
ALL = "all"
|
2011-08-15 20:56:58 +04:00
|
|
|
NATS = "nats_server"
|
2011-07-30 08:12:53 +04:00
|
|
|
ROUTER = "router"
|
|
|
|
CC = "cloud_controller"
|
2011-08-15 20:56:58 +04:00
|
|
|
CCDB = "ccdb"
|
2011-07-30 08:12:53 +04:00
|
|
|
CF = "cloudfoundry"
|
|
|
|
HM = "health_manager"
|
|
|
|
DEA = "dea"
|
2012-01-31 12:55:44 +04:00
|
|
|
UAA = "uaa"
|
|
|
|
UAADB = "uaadb"
|
|
|
|
ACM = "acm"
|
|
|
|
ACMDB = "acmdb"
|
2011-07-30 08:12:53 +04:00
|
|
|
|
2012-05-10 21:43:53 +04:00
|
|
|
SERVICES = ["redis", "mysql", "mongodb", "neo4j", "rabbitmq", "memcached"]
|
2011-09-09 23:13:45 +04:00
|
|
|
SERVICES_NODE = SERVICES.map do |service|
|
|
|
|
"#{service}_node"
|
|
|
|
end
|
2011-08-15 20:56:58 +04:00
|
|
|
SERVICES_GATEWAY = SERVICES.map do |service|
|
|
|
|
"#{service}_gateway"
|
|
|
|
end
|
2011-09-09 23:13:45 +04:00
|
|
|
SERVICES_NODE.each do |node|
|
|
|
|
# Service name constant e.g. REDIS_NODE -> "redis_node"
|
|
|
|
const_set(node.upcase, node)
|
2011-08-15 20:56:58 +04:00
|
|
|
end
|
2011-07-30 08:12:53 +04:00
|
|
|
|
2011-08-15 20:56:58 +04:00
|
|
|
# All supported jobs
|
2012-04-11 00:03:15 +04:00
|
|
|
JOBS = [ALL, NATS, ROUTER, CF, CC, HM, DEA, CCDB, UAA, UAADB] + SERVICES_NODE + SERVICES_GATEWAY
|
2011-07-30 08:12:53 +04:00
|
|
|
SYSTEM_JOB = [CF]
|
|
|
|
|
2011-08-15 20:56:58 +04:00
|
|
|
# List of the required properties for jobs
|
|
|
|
INSTALLED_JOB_PROPERTIES = {NATS => ["host"], CC => ["service_api_uri", "builtin_services"],
|
|
|
|
CCDB => ["host"]}
|
2012-05-10 21:43:53 +04:00
|
|
|
INSTALL_JOB_PROPERTIES = {CC => ["builtin_services"], MYSQL_NODE => ["index"], MONGODB_NODE => ["index"], REDIS_NODE => ["index"], NEO4J_NODE => ["index"], RABBITMQ_NODE => ["index"], MEMCACHED_NODE => ["index"]}
|
2011-08-15 20:56:58 +04:00
|
|
|
|
|
|
|
# Dependency between JOBS and components that are consumed by "vcap_dev" when cf is started or
|
|
|
|
# stopped
|
2011-09-09 23:13:45 +04:00
|
|
|
SERVICE_NODE_RUN_COMPONENTS = Hash.new
|
|
|
|
SERVICES_NODE.each do |node|
|
|
|
|
SERVICE_NODE_RUN_COMPONENTS[node] = node
|
2011-08-15 20:56:58 +04:00
|
|
|
end
|
2011-07-30 08:12:53 +04:00
|
|
|
|
2011-08-15 20:56:58 +04:00
|
|
|
SERVICE_GATEWAY_RUN_COMPONENTS = Hash.new
|
|
|
|
SERVICES_GATEWAY.each do |gateway|
|
|
|
|
SERVICE_GATEWAY_RUN_COMPONENTS[gateway] = gateway
|
|
|
|
end
|
2011-07-30 08:12:53 +04:00
|
|
|
|
2012-04-11 00:03:15 +04:00
|
|
|
RUN_COMPONENTS = {ROUTER => ROUTER, CC => CC, HM => HM, DEA => DEA, UAA => UAA}.update(SERVICE_NODE_RUN_COMPONENTS).update(SERVICE_GATEWAY_RUN_COMPONENTS)
|
2011-07-30 08:12:53 +04:00
|
|
|
|
|
|
|
class << self
|
|
|
|
if defined?(Rake::DSL)
|
|
|
|
include Rake::DSL
|
|
|
|
end
|
|
|
|
# Take user input and create a Hash that has the job name as the key and its
|
|
|
|
# properties as value.
|
|
|
|
#
|
|
|
|
# Allows the user to specify jobs as either
|
|
|
|
# jobs:
|
|
|
|
# install:
|
|
|
|
# - redis
|
|
|
|
# - mysql
|
|
|
|
# OR
|
|
|
|
# jobs:
|
|
|
|
# install:
|
|
|
|
# redis:
|
|
|
|
# mysql:
|
|
|
|
#
|
|
|
|
def sanitize_jobs(type)
|
|
|
|
return nil if @config["jobs"][type].nil?
|
|
|
|
|
|
|
|
jobs = {}
|
|
|
|
config_jobs = @config["jobs"][type]
|
|
|
|
config_jobs.each do |element|
|
|
|
|
case element
|
|
|
|
when String
|
|
|
|
jobs[element] = nil
|
|
|
|
when Hash
|
|
|
|
if element.length > 1
|
|
|
|
puts "Bad input, #{element.pretty_inspect} should have only one key, please fix your yaml file."
|
|
|
|
exit 1
|
|
|
|
end
|
|
|
|
element.each do |job, properties|
|
|
|
|
jobs[job] = properties.nil? ? nil : properties.dup
|
|
|
|
end
|
|
|
|
else
|
|
|
|
puts "Unsupported type for Installed or Install job #{element}"
|
|
|
|
exit 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# validate jobs
|
|
|
|
given_jobs = Set.new(jobs.keys)
|
|
|
|
if (intersect = @valid_jobs.intersection(given_jobs)) != given_jobs
|
|
|
|
puts "Input Error: Please provide valid #{type} jobs, following jobs are not recognized\n#{(given_jobs - intersect).pretty_inspect}"
|
|
|
|
exit 1
|
|
|
|
end
|
|
|
|
|
|
|
|
jobs
|
|
|
|
end
|
|
|
|
|
|
|
|
def detect_duplicate_jobs
|
|
|
|
if !@config["jobs"]["install"].nil? && !@config["jobs"]["installed"].nil?
|
|
|
|
install_jobs = Set.new(@config["jobs"]["install"].keys)
|
|
|
|
installed_jobs = Set.new(@config["jobs"]["installed"].keys)
|
|
|
|
common = install_jobs.intersection(installed_jobs)
|
|
|
|
unless common.empty?
|
|
|
|
puts "Input error, The following jobs are specified in both the install and installed list.\n#{common.pretty_inspect}"
|
|
|
|
exit 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-08-15 20:56:58 +04:00
|
|
|
def validate_properties(jobs, required_properties)
|
2011-07-30 08:12:53 +04:00
|
|
|
return if jobs.nil?
|
|
|
|
|
|
|
|
missing_keys = {}
|
|
|
|
jobs.each do |job, properties|
|
|
|
|
# Check if this job needs properties
|
2011-08-15 20:56:58 +04:00
|
|
|
next if required_properties[job].nil?
|
2011-07-30 08:12:53 +04:00
|
|
|
|
2011-08-15 20:56:58 +04:00
|
|
|
expected = Set.new(required_properties[job])
|
2011-07-30 08:12:53 +04:00
|
|
|
given = properties.nil? ? Set.new : Set.new(properties.keys)
|
|
|
|
|
|
|
|
# Check if all the required properties are given
|
|
|
|
if !expected.subset?(given)
|
|
|
|
missing_keys[job] ||= []
|
|
|
|
missing_keys[job] << (expected - given).to_a
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-08-15 20:56:58 +04:00
|
|
|
if !missing_keys.empty?
|
|
|
|
puts "Input Error: The following mandatory job properties are missing #{missing_keys.pretty_inspect}"
|
2011-07-30 08:12:53 +04:00
|
|
|
exit 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Gets called by rake tasks for each job.
|
|
|
|
# The possibilities for each job are as follows.
|
|
|
|
# 1. It is already installed
|
|
|
|
# 2. It is in the install list
|
|
|
|
# 3. It is not on the install list or the installed list
|
|
|
|
#
|
|
|
|
# Case 1, propogate the properties of the installed job to the spec
|
|
|
|
# so dependent jobs can use these properties.
|
|
|
|
# Case 2, add the required chef role to the chef run list, add default/given
|
|
|
|
# properties to the spec
|
|
|
|
# Case 3, is a dependecy failure.
|
|
|
|
def install(job)
|
|
|
|
unless @all_install
|
2011-08-15 20:56:58 +04:00
|
|
|
if !@config["jobs"]["installed"].nil? && !@config["jobs"]["installed"][job].nil?
|
|
|
|
@spec[job] = @config["jobs"]["installed"][job].dup
|
2011-07-30 08:12:53 +04:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2011-08-15 20:56:58 +04:00
|
|
|
unless @config["jobs"]["install"].has_key?(job) || SYSTEM_JOB.include?(job)
|
2011-07-30 08:12:53 +04:00
|
|
|
puts "Dependecy check error: job #{job} is needed by one of the jobs in the install list, please add job #{job} to the install or installed list"
|
|
|
|
exit 1
|
|
|
|
end
|
|
|
|
|
2011-08-15 20:56:58 +04:00
|
|
|
if !@config["jobs"]["install"][job].nil?
|
|
|
|
@spec[job] = @config["jobs"]["install"][job].dup
|
2011-07-30 08:12:53 +04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-08-15 20:56:58 +04:00
|
|
|
# Prepare the run list for this job
|
|
|
|
if RUN_COMPONENTS.has_key?(job)
|
|
|
|
case RUN_COMPONENTS[job]
|
|
|
|
when String
|
|
|
|
@run_list << RUN_COMPONENTS[job]
|
|
|
|
when Array
|
|
|
|
RUN_COMPONENTS[job].each do |component|
|
|
|
|
@run_list << component
|
|
|
|
end
|
|
|
|
end
|
2011-07-30 08:12:53 +04:00
|
|
|
end
|
2011-08-15 20:56:58 +04:00
|
|
|
|
2011-07-30 08:12:53 +04:00
|
|
|
@roles << job
|
|
|
|
end
|
|
|
|
|
2011-08-15 20:56:58 +04:00
|
|
|
def process_jobs
|
2011-07-30 08:12:53 +04:00
|
|
|
# Default to all jobs
|
|
|
|
if @config["jobs"].nil?
|
|
|
|
# Install all jobs
|
|
|
|
@all_install = true
|
|
|
|
Rake.application[ALL].invoke
|
2011-08-15 20:56:58 +04:00
|
|
|
return
|
2011-07-30 08:12:53 +04:00
|
|
|
end
|
|
|
|
|
|
|
|
# Make sure that the "install" and "installed" jobs specified are valid
|
|
|
|
@config["jobs"]["install"] = sanitize_jobs("install")
|
|
|
|
@config["jobs"]["installed"] = sanitize_jobs("installed")
|
|
|
|
|
|
|
|
if @config["jobs"]["install"].nil?
|
|
|
|
puts "You have not selected any jobs to install."
|
|
|
|
exit 0
|
|
|
|
end
|
|
|
|
|
|
|
|
# Make sure that the "install" and "installed" jobs do not intersect
|
|
|
|
detect_duplicate_jobs
|
|
|
|
|
|
|
|
if @config["jobs"]["install"].include?("all")
|
|
|
|
# Install all jobs
|
|
|
|
if !@config["jobs"]["installed"].nil?
|
|
|
|
puts "Please correct your config file. You are trying to install all jobs, but you have also specified an 'installed' section"
|
|
|
|
exit 1
|
|
|
|
end
|
|
|
|
|
|
|
|
if @config["jobs"]["install"].length != 1
|
|
|
|
puts "Please correct your config file. You are trying to install all jobs, remove all other jobs from the 'install' list"
|
|
|
|
exit 1
|
|
|
|
end
|
|
|
|
|
|
|
|
@all_install = true
|
|
|
|
Rake.application[ALL].invoke
|
2011-08-15 20:56:58 +04:00
|
|
|
return
|
2011-07-30 08:12:53 +04:00
|
|
|
end
|
|
|
|
|
|
|
|
# Sanity check the given properties
|
2011-08-15 20:56:58 +04:00
|
|
|
validate_properties(@config["jobs"]["installed"], INSTALLED_JOB_PROPERTIES)
|
|
|
|
validate_properties(@config["jobs"]["install"], INSTALL_JOB_PROPERTIES)
|
2011-07-30 08:12:53 +04:00
|
|
|
|
|
|
|
# Let the install rake task do the dependency management
|
|
|
|
@config["jobs"]["install"].keys.each do |job|
|
|
|
|
Rake.application[job].invoke
|
|
|
|
end
|
2011-08-15 20:56:58 +04:00
|
|
|
end
|
|
|
|
|
|
|
|
def go(config)
|
|
|
|
@spec = {}
|
|
|
|
@roles = []
|
|
|
|
@run_list = Set.new
|
|
|
|
@valid_jobs = Set.new(JOBS)
|
|
|
|
@config = config.dup
|
|
|
|
@all_install = false
|
|
|
|
|
|
|
|
# Load the job dependecies
|
|
|
|
Rake.application.rake_require("job_dependency")
|
|
|
|
|
|
|
|
process_jobs
|
2011-07-30 08:12:53 +04:00
|
|
|
|
2011-08-15 20:56:58 +04:00
|
|
|
# All dependencies are resolved, return the job property spec, chef
|
|
|
|
# roles and the vcap run list
|
|
|
|
return @spec, @roles, @run_list.to_a
|
2011-07-30 08:12:53 +04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|