elevage 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +21 -0
- data/.rubocop.yml +8 -0
- data/.ruby-version +1 -0
- data/.simplecov +8 -0
- data/.travis.yml +3 -0
- data/.yardoc/checksums +12 -0
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/.yardoc/proxy_types +0 -0
- data/Gemfile +4 -0
- data/Guardfile +10 -0
- data/LICENSE.txt +203 -0
- data/README.md +112 -0
- data/Rakefile +20 -0
- data/bin/elevage +4 -0
- data/coverage/.resultset.json.lock +0 -0
- data/doc/Elevage/Build.html +435 -0
- data/doc/Elevage/CLI.html +282 -0
- data/doc/Elevage/Environment.html +950 -0
- data/doc/Elevage/Generate.html +346 -0
- data/doc/Elevage/Health.html +359 -0
- data/doc/Elevage/New.html +411 -0
- data/doc/Elevage/Platform.html +1119 -0
- data/doc/Elevage/Provisioner.html +804 -0
- data/doc/Elevage/ProvisionerRunQueue.html +765 -0
- data/doc/Elevage/Runner.html +319 -0
- data/doc/Elevage.html +501 -0
- data/doc/_index.html +239 -0
- data/doc/class_list.html +58 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +57 -0
- data/doc/css/style.css +339 -0
- data/doc/file.README.html +187 -0
- data/doc/file_list.html +60 -0
- data/doc/frames.html +26 -0
- data/doc/index.html +187 -0
- data/doc/js/app.js +219 -0
- data/doc/js/full_list.js +181 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +369 -0
- data/doc/top-level-namespace.html +112 -0
- data/elevage.gemspec +39 -0
- data/features/archive +314 -0
- data/features/build.feature +237 -0
- data/features/elevage.feature +24 -0
- data/features/generate.feature +235 -0
- data/features/health_env_failure.feature +292 -0
- data/features/health_failure.feature +291 -0
- data/features/health_success.feature +279 -0
- data/features/list.feature +315 -0
- data/features/new.feature +68 -0
- data/features/step_definitions/elevage_steps.rb +27 -0
- data/features/support/env.rb +9 -0
- data/lib/elevage/build.rb +109 -0
- data/lib/elevage/constants.rb +113 -0
- data/lib/elevage/environment.rb +223 -0
- data/lib/elevage/generate.rb +48 -0
- data/lib/elevage/health.rb +27 -0
- data/lib/elevage/new.rb +30 -0
- data/lib/elevage/platform.rb +105 -0
- data/lib/elevage/provisioner.rb +169 -0
- data/lib/elevage/provisionerrunqueue.rb +114 -0
- data/lib/elevage/runner.rb +39 -0
- data/lib/elevage/templates/compute.yml.tt +18 -0
- data/lib/elevage/templates/environment.yml.tt +20 -0
- data/lib/elevage/templates/network.yml.tt +16 -0
- data/lib/elevage/templates/platform.yml.tt +110 -0
- data/lib/elevage/templates/vcenter.yml.tt +77 -0
- data/lib/elevage/version.rb +4 -0
- data/lib/elevage.rb +45 -0
- data/spec/spec_helper.rb +4 -0
- metadata +357 -0
@@ -0,0 +1,114 @@
|
|
1
|
+
require_relative 'constants'
|
2
|
+
|
3
|
+
module Elevage
|
4
|
+
# ProvisionerRunQueue class
|
5
|
+
class ProvisionerRunQueue
|
6
|
+
attr_reader :running_tasks
|
7
|
+
attr_accessor :provisioners
|
8
|
+
attr_accessor :max_concurrent
|
9
|
+
attr_accessor :busy_wait_timeout
|
10
|
+
attr_accessor :build_status_interval
|
11
|
+
|
12
|
+
# Public: Initialize the object
|
13
|
+
def initialize
|
14
|
+
@running_tasks = 0 # We start out with nothing running
|
15
|
+
@max_concurrent = BUILD_CONCURRENT_DEFAULT
|
16
|
+
@busy_wait_timeout = BUILD_CHILD_WAIT_TIMEOUT
|
17
|
+
@build_status_interval = BUILD_STATUS_INTERVAL
|
18
|
+
@provisioners = []
|
19
|
+
@children = {}
|
20
|
+
end
|
21
|
+
|
22
|
+
# Public: run() - Process the queue
|
23
|
+
# rubocop:disable MethodLength
|
24
|
+
def run
|
25
|
+
puts "#{Time.now} [#{$$}]: Provisioning started."
|
26
|
+
@provisioners.each do |provisioner|
|
27
|
+
# Make sure we're not running more jobs than we're allowed
|
28
|
+
wait_for_tasks
|
29
|
+
child_pid = fork do
|
30
|
+
provision_task task: provisioner
|
31
|
+
end
|
32
|
+
@children[child_pid] = provisioner.name
|
33
|
+
@running_tasks += 1
|
34
|
+
end
|
35
|
+
# Hang around until we collect all the rest of the children
|
36
|
+
wait_for_tasks state: :collect
|
37
|
+
puts "#{Time.now} [#{$$}]: Provisioning completed."
|
38
|
+
end
|
39
|
+
# rubocop:enable MethodLength
|
40
|
+
|
41
|
+
# Public: Display a string representation
|
42
|
+
# rubocop:disable MethodLength
|
43
|
+
def to_s
|
44
|
+
puts "Running Tasks: #{@running_tasks}"
|
45
|
+
puts "Max Concurrency: #{@max_concurrent}"
|
46
|
+
puts "Wait status interval: #{@build_status_interval}"
|
47
|
+
puts 'Current Child processes:'
|
48
|
+
@children.each do |pid, name|
|
49
|
+
puts " - [#{pid}]: #{name}"
|
50
|
+
end
|
51
|
+
puts 'Queued Provisioners:'
|
52
|
+
@provisioners.each do |provisioner|
|
53
|
+
puts " - #{provisioner.name}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
# rubocop:enable MethodLength
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# rubocop:disable LineLength, GlobalVars
|
61
|
+
# Private: provision_task is the method that should execute in the child
|
62
|
+
# process, and contain all the logic for the child process.
|
63
|
+
def provision_task(task: nil)
|
64
|
+
start_time = Time.now
|
65
|
+
print "#{Time.now} [#{$$}]: #{task.name} Provisioning...\n"
|
66
|
+
status = task.build ? 'succeeded' : 'FAILED'
|
67
|
+
run_time = Time.now - start_time
|
68
|
+
print "#{Time.now} [#{$$}]: #{task.name} #{status} in #{run_time.round(2)} seconds.\n"
|
69
|
+
end
|
70
|
+
# rubocop:enable LineLength, GlobalVars
|
71
|
+
|
72
|
+
# rubocop:disable MethodLength, LineLength, CyclomaticComplexity, GlobalVars
|
73
|
+
# Private: Wait for child tasks to return
|
74
|
+
# Since our trap for SIGCHLD will clean up the @running_tasks count and
|
75
|
+
# the children hash, here we can just keep checking until @running_tasks
|
76
|
+
# is 0.
|
77
|
+
# If we've been waiting at least a minute, print out a notice of what
|
78
|
+
# we're still waiting for.
|
79
|
+
def wait_for_tasks(state: :running)
|
80
|
+
i = interval = @build_status_interval / @busy_wait_timeout
|
81
|
+
|
82
|
+
# We are either "running", and waiting for a child to return so we can
|
83
|
+
# dispatch a new child, or we are "collecting", in which case we have
|
84
|
+
# no more children waiting to be dispatched, and are waiting for them
|
85
|
+
# all to finish.
|
86
|
+
while @running_tasks >= @max_concurrent && state.eql?(:running) || @running_tasks > 0 && state.eql?(:collect)
|
87
|
+
|
88
|
+
# Always having to clean up after our children...
|
89
|
+
@children.keys.each do |pid|
|
90
|
+
childpid = Process.wait(pid, Process::WNOHANG | Process::WUNTRACED)
|
91
|
+
unless childpid.nil?
|
92
|
+
@children.delete(childpid)
|
93
|
+
@running_tasks -= 1
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Is it time for a status update yet?
|
98
|
+
if i <= 0
|
99
|
+
print "#{Time.now} [#{$$}]: Waiting for #{@children.size} jobs:\n"
|
100
|
+
@children.each do |pid, name|
|
101
|
+
print " - #{pid}: #{name}\n"
|
102
|
+
end
|
103
|
+
# reset the status counter
|
104
|
+
i = interval
|
105
|
+
end
|
106
|
+
|
107
|
+
# tick the status counter
|
108
|
+
i -= 1
|
109
|
+
sleep @busy_wait_timeout
|
110
|
+
end
|
111
|
+
end
|
112
|
+
# rubocop:enable MethodLength, LineLength, CyclomaticComplexity, GlobalVars
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'elevage'
|
2
|
+
# rubocop:disable all
|
3
|
+
module Elevage
|
4
|
+
# wrapper to assist aruba in single process execution
|
5
|
+
class Runner
|
6
|
+
# Allow everything fun to be injected from the outside while defaulting to normal implementations.
|
7
|
+
def initialize(argv, stdin = STDIN, stdout = STDOUT, stderr = STDERR, kernel = Kernel)
|
8
|
+
@argv, @stdin, @stdout, @stderr, @kernel = argv, stdin, stdout, stderr, kernel
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute!
|
12
|
+
exit_code = begin
|
13
|
+
# Thor accesses these streams directly rather than letting them be injected, so we replace them...
|
14
|
+
$stderr = @stderr
|
15
|
+
$stdin = @stdin
|
16
|
+
$stdout = @stdout
|
17
|
+
|
18
|
+
Elevage::CLI.start(@argv)
|
19
|
+
|
20
|
+
# Thor::Base#start does not have a return value, assume success if no exception is raised.
|
21
|
+
0
|
22
|
+
rescue StandardError => e
|
23
|
+
# The ruby interpreter would pipe this to STDERR and exit 1 in the case of an unhandled exception
|
24
|
+
b = e.backtrace
|
25
|
+
b.unshift("#{b.shift}: #{e.message} (#{e.class})")
|
26
|
+
@stderr.puts(b.map { |s| "\tfrom #{s}" }.join("\n"))
|
27
|
+
1
|
28
|
+
ensure
|
29
|
+
# put them back.
|
30
|
+
$stderr = STDERR
|
31
|
+
$stdin = STDIN
|
32
|
+
$stdout = STDOUT
|
33
|
+
end
|
34
|
+
# Proxy exit code back to the injected kernel.
|
35
|
+
@kernel.exit(exit_code)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
# rubocop:enable all
|
@@ -0,0 +1,18 @@
|
|
1
|
+
? "%YAML 1.2"
|
2
|
+
compute:
|
3
|
+
# The compute name is used to specify the compute resources used in the provsion of the component node.
|
4
|
+
# Currently available options are cpu and ram. Ram is an integer value representing Gb.
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
#
|
8
|
+
# dev:
|
9
|
+
# cpu: 2
|
10
|
+
# ram: 1
|
11
|
+
#
|
12
|
+
# prod:
|
13
|
+
# cpu: 2
|
14
|
+
# ram: 12
|
15
|
+
#
|
16
|
+
default: &default
|
17
|
+
cpu:
|
18
|
+
ram:
|
@@ -0,0 +1,20 @@
|
|
1
|
+
? "%YAML 1.2"
|
2
|
+
environment:
|
3
|
+
vcenter:
|
4
|
+
# Environment files are generated from the platform.yml defintion.
|
5
|
+
#
|
6
|
+
# You can override any of the default values based on the requirements
|
7
|
+
# of this particular environment. Though typically you will only
|
8
|
+
# want to override things like:
|
9
|
+
#
|
10
|
+
# count The number of components in the load balance pool
|
11
|
+
# compute Size of compute resource to assign, defined in the compute.yml file
|
12
|
+
# network vlan the component nodes are attached
|
13
|
+
#
|
14
|
+
# Additionally, you must specify IP addresses for each node. The generate
|
15
|
+
# command will create array placeholders based on the default Count
|
16
|
+
#
|
17
|
+
pools:
|
18
|
+
<%= @env_pools %>
|
19
|
+
components:
|
20
|
+
<%= @env_components %>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
? "%YAML 1.2"
|
2
|
+
network:
|
3
|
+
# Any number of networks may be specified. Networks are assigned to components as part
|
4
|
+
# of the component definition. The vcenter vlan id is used to specify the network.
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
#
|
8
|
+
# devweb:
|
9
|
+
# vlanid: DEV_WEB_NET
|
10
|
+
# gateway: 10.10.128.1
|
11
|
+
# netmask: 19
|
12
|
+
#
|
13
|
+
networkname:
|
14
|
+
vlanid:
|
15
|
+
gateway:
|
16
|
+
netmask:
|
@@ -0,0 +1,110 @@
|
|
1
|
+
? "%YAML 1.2"
|
2
|
+
platform:
|
3
|
+
name: <%= platform.downcase %>
|
4
|
+
description: platform desired state definition files
|
5
|
+
# Environments are listed as individual yml array elements such as
|
6
|
+
#
|
7
|
+
# Example
|
8
|
+
#
|
9
|
+
# environments:
|
10
|
+
# - INT
|
11
|
+
# - QA
|
12
|
+
# - STAGE
|
13
|
+
# - PROD
|
14
|
+
#
|
15
|
+
environments:
|
16
|
+
-
|
17
|
+
# Components are grouped into tiers. You may define a single tier or multiple.
|
18
|
+
# Tier names may be appended to vcenter resource folders. A typical platform may
|
19
|
+
# be tiered as follows:
|
20
|
+
#
|
21
|
+
# Example
|
22
|
+
#
|
23
|
+
# tiers:
|
24
|
+
# - web
|
25
|
+
# - app
|
26
|
+
# - db
|
27
|
+
#
|
28
|
+
tiers:
|
29
|
+
-
|
30
|
+
# Individual node name are constructed at provision time based on the pattern
|
31
|
+
# you define here. Available options from the platform or environment yml keys are
|
32
|
+
# environment
|
33
|
+
# component
|
34
|
+
# instance single leading zero 0..9, up to count of component pool
|
35
|
+
# geo first letter of geo key value
|
36
|
+
# 'string' any single quoted string, escape char not evaluated
|
37
|
+
#
|
38
|
+
# Example
|
39
|
+
#
|
40
|
+
# nodenameconvention:
|
41
|
+
# - environment
|
42
|
+
# - '-'
|
43
|
+
# - component
|
44
|
+
# - instance
|
45
|
+
#
|
46
|
+
# => "dev-api01"
|
47
|
+
#
|
48
|
+
nodenameconvention:
|
49
|
+
- 'node'
|
50
|
+
- instance
|
51
|
+
# Components are defined as part of a pool. The pool is where you assign a component
|
52
|
+
# the following required items:
|
53
|
+
#
|
54
|
+
# count The number of components in the load balance pool
|
55
|
+
# tier Which platform tier the component is built on
|
56
|
+
# image The vmware image to clone for the nodes in this pool
|
57
|
+
# compute Size of compute resource to assign, defined in the compute.yml file
|
58
|
+
# port service comm port
|
59
|
+
# runlist Chef runlist(s) for the node
|
60
|
+
# componentrole optional custom role created by substituting the component name for # in the supplied string
|
61
|
+
#
|
62
|
+
# you may define any number of pools which may inherit based on yml default indicators.
|
63
|
+
# Each component may share a pool definition or have a unique one.
|
64
|
+
#
|
65
|
+
# Example
|
66
|
+
#
|
67
|
+
# pools:
|
68
|
+
# vmdefaults: &vmdefaults
|
69
|
+
# count: 4
|
70
|
+
# tier: Web
|
71
|
+
# image: 'centos-6.5-x86_64-20140714'
|
72
|
+
# compute: dev
|
73
|
+
# port: 80
|
74
|
+
# runlist:
|
75
|
+
# - 'role[base]'
|
76
|
+
# componentrole: 'role[myapp-#]'
|
77
|
+
#
|
78
|
+
pools:
|
79
|
+
vmdefaults: &vmdefaults
|
80
|
+
count:
|
81
|
+
tier:
|
82
|
+
image:
|
83
|
+
compute:
|
84
|
+
port:
|
85
|
+
runlist:
|
86
|
+
-
|
87
|
+
componentrole: false
|
88
|
+
# Components inherit all the key values from the assigned pool, which may be overridden
|
89
|
+
# in addition you can define a service port number for individual service pools
|
90
|
+
#
|
91
|
+
# Example
|
92
|
+
#
|
93
|
+
# components:
|
94
|
+
# api:
|
95
|
+
# <<: *vmdefaults
|
96
|
+
# port: 8080
|
97
|
+
#
|
98
|
+
# cui:
|
99
|
+
# <<: *vmdefaults
|
100
|
+
# port: 8082
|
101
|
+
#
|
102
|
+
# terracotta:
|
103
|
+
# <<: *vmdefaults
|
104
|
+
# compute: prodtc
|
105
|
+
# image: 'centos32g-6.5-x86_64-20140714'
|
106
|
+
#
|
107
|
+
components:
|
108
|
+
|
109
|
+
component:
|
110
|
+
<<: *vmdefaults
|
@@ -0,0 +1,77 @@
|
|
1
|
+
? "%YAML 1.2"
|
2
|
+
vcenter:
|
3
|
+
# you can add as many vcenter definitions as required. The default file configuratioon
|
4
|
+
# includes two in the form of a production and non production datacenter.
|
5
|
+
# Key elements are as follows:
|
6
|
+
#
|
7
|
+
# geo geographic location of a physical data center. Primarily for use in DR defintions
|
8
|
+
# timezone node timezone setting
|
9
|
+
# host hostname of vcenter management server
|
10
|
+
# datacenter vcenter 'datacenter' folder
|
11
|
+
# imagefolder vcenter resource folder where the clone image is located
|
12
|
+
# destfolder vcenter resource folder destination for provisioned vms
|
13
|
+
# resourcepool vcenter resource pool for the provisioned vms
|
14
|
+
# appendenv if true the destfolder path will be appended with the environment name
|
15
|
+
# appenddomain if true the domain will be pre-pended with environment name
|
16
|
+
# datastores array list of available datastores. Elevage will attempt to evenly distribute
|
17
|
+
# domain domain for fqdn of host
|
18
|
+
# dnsips ips of dns servers
|
19
|
+
#
|
20
|
+
# Example
|
21
|
+
#
|
22
|
+
# locations:
|
23
|
+
# nonprod: &vcenter
|
24
|
+
# geo: west
|
25
|
+
# timezone: 085
|
26
|
+
#
|
27
|
+
# host: 'vcwest.corp.local'
|
28
|
+
# datacenter: 'WCDC NonProd'
|
29
|
+
# imagefolder: 'Corporate/Platform Services/Templates'
|
30
|
+
# destfolder: 'Corporate/Platform Services/app'
|
31
|
+
# resourcepool: 'App-Web Linux/Corporate'
|
32
|
+
# appendenv: true
|
33
|
+
# appenddomain: true
|
34
|
+
# datastores:
|
35
|
+
# - NonProd_Cor_25
|
36
|
+
# - NonProd_Cor_26
|
37
|
+
# - NonProd_Cor_38
|
38
|
+
# - NonProd_Cor_39
|
39
|
+
#
|
40
|
+
# domain: dev.corp.local
|
41
|
+
# dnsips:
|
42
|
+
# - 10.10.10.5
|
43
|
+
# - 10.10.10.6
|
44
|
+
#
|
45
|
+
# prod:
|
46
|
+
# <<: *vcenter
|
47
|
+
#
|
48
|
+
# datacenter: 'WCDC Prod'
|
49
|
+
# datastores:
|
50
|
+
# - Prod_Cor_03
|
51
|
+
# - Prod_Cor_04
|
52
|
+
#
|
53
|
+
# domain: corp.local
|
54
|
+
# dnsips:
|
55
|
+
# - 10.20.100.5
|
56
|
+
# - 10.20.100.6
|
57
|
+
#
|
58
|
+
nonprod: &default
|
59
|
+
geo:
|
60
|
+
timezone:
|
61
|
+
|
62
|
+
host:
|
63
|
+
datacenter:
|
64
|
+
imagefolder:
|
65
|
+
destfolder:
|
66
|
+
resourcepool:
|
67
|
+
appendenv: false
|
68
|
+
appenddomain: false
|
69
|
+
datastores:
|
70
|
+
-
|
71
|
+
|
72
|
+
domain:
|
73
|
+
dnsips:
|
74
|
+
-
|
75
|
+
|
76
|
+
prod:
|
77
|
+
<<: *default
|
data/lib/elevage.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'elevage/version'
|
3
|
+
require 'elevage/constants'
|
4
|
+
require 'elevage/new'
|
5
|
+
require 'elevage/platform'
|
6
|
+
require 'elevage/environment'
|
7
|
+
require 'elevage/health'
|
8
|
+
require 'elevage/generate'
|
9
|
+
require 'elevage/build'
|
10
|
+
|
11
|
+
# Refer to README.md for use instructions
|
12
|
+
module Elevage
|
13
|
+
# Start of main CLI
|
14
|
+
class CLI < Thor
|
15
|
+
package_name 'elevage'
|
16
|
+
map '--version' => :version
|
17
|
+
map '-v' => :version
|
18
|
+
|
19
|
+
desc 'version', DESC_VERSION
|
20
|
+
def version
|
21
|
+
puts VERSION
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'list ITEM', DESC_LIST
|
25
|
+
method_option :nodes, aliases: '-n', desc: DESC_LIST_NODES
|
26
|
+
# rubocop:disable LineLength
|
27
|
+
def list(item)
|
28
|
+
# errors handled in class methods
|
29
|
+
if LIST_CMDS.include?(item)
|
30
|
+
puts Elevage::Platform.new.send(item).to_yaml
|
31
|
+
else
|
32
|
+
fail(IOError, ERR[:not_list_cmd]) unless File.file?(ENV_FOLDER + item + '.yml')
|
33
|
+
environment = Elevage::Environment.new(item)
|
34
|
+
puts options[:nodes] ? environment.list_nodes : environment
|
35
|
+
end
|
36
|
+
end
|
37
|
+
# rubocop:enable LineLength
|
38
|
+
|
39
|
+
# subcommand in Thor called as registered class
|
40
|
+
register(Elevage::New, 'new', 'new PLATFORM', DESC_NEW)
|
41
|
+
register(Elevage::Health, 'health', 'health', DESC_HEALTH)
|
42
|
+
register(Elevage::Generate, 'generate', 'generate ENV', DESC_GENERATE)
|
43
|
+
register(Elevage::Build, 'build', 'build TARGET', DESC_BUILD)
|
44
|
+
end
|
45
|
+
end
|
data/spec/spec_helper.rb
ADDED