genesis_framework 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/genesis +19 -0
- data/lib/genesisframework.rb +6 -0
- data/lib/genesisframework/facter/test_fact.rb +5 -0
- data/lib/genesisframework/task.rb +175 -0
- data/lib/genesisframework/tasks.rb +136 -0
- data/lib/genesisframework/utils.rb +55 -0
- metadata +133 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ab603bd2b1113f7ec2bdc86187156625e40a333d
|
4
|
+
data.tar.gz: 75d4060aa531be4bef61a680cb7ee39ef51363c2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2f3723dff9601c50c2ac4bc8a4d3b48e9aa09dc7691d69776a781f287385490665ff03194ab2d9ddfd6b57a1a040096e3d820a99297b5595b3902b418b80bad1
|
7
|
+
data.tar.gz: ca2ff9803849bba62cbafe426f663fe803c1a53b34bd4a78fa8629a21429cac58948e004016e70e50f3282951f802dabd172ff0d2332f3f6fdd1d98f746c57bf
|
data/bin/genesis
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rake'
|
5
|
+
rescue LoadError
|
6
|
+
require 'rubygems'
|
7
|
+
require 'rake'
|
8
|
+
end
|
9
|
+
|
10
|
+
ENV['GENESIS_ROOT'] ||= '/var/run/genesis'
|
11
|
+
|
12
|
+
# rake does chdir also, so don't put this in a block to avoid the warning
|
13
|
+
Dir::chdir( File.join(ENV['GENESIS_ROOT'], '/tasks') )
|
14
|
+
|
15
|
+
# run the normal rake application, but change its name to genesis
|
16
|
+
rake = Rake.application
|
17
|
+
rake.init('genesis')
|
18
|
+
rake.load_rakefile
|
19
|
+
rake.top_level
|
@@ -0,0 +1,6 @@
|
|
1
|
+
# put the facter path in the LOAD_PATH so facter looks for new facts there
|
2
|
+
# this path needs to have a 'facter' dir as an immediate subdir
|
3
|
+
$LOAD_PATH << File.join(File.expand_path(File.dirname(__FILE__)),'genesisframework')
|
4
|
+
require 'genesisframework/task'
|
5
|
+
require 'genesisframework/tasks'
|
6
|
+
require 'genesisframework/utils'
|
@@ -0,0 +1,175 @@
|
|
1
|
+
require 'collins_client'
|
2
|
+
require 'syslog'
|
3
|
+
require 'retryingfetcher'
|
4
|
+
require 'promptcli'
|
5
|
+
require 'facter'
|
6
|
+
require 'open3'
|
7
|
+
|
8
|
+
module Genesis
|
9
|
+
module Framework
|
10
|
+
module Task
|
11
|
+
def self.included base
|
12
|
+
base.extend TaskDslMethods
|
13
|
+
end
|
14
|
+
|
15
|
+
module TaskDslMethods
|
16
|
+
attr_accessor :blocks, :options
|
17
|
+
|
18
|
+
def description desc
|
19
|
+
set_option :description, desc
|
20
|
+
end
|
21
|
+
|
22
|
+
def precondition description, &block
|
23
|
+
add_block :precondition, description, block
|
24
|
+
end
|
25
|
+
|
26
|
+
def init &block
|
27
|
+
set_block :init, block
|
28
|
+
end
|
29
|
+
|
30
|
+
def condition description, &block
|
31
|
+
add_block :condition, description, block
|
32
|
+
end
|
33
|
+
|
34
|
+
def run &block
|
35
|
+
set_block :run, block
|
36
|
+
end
|
37
|
+
|
38
|
+
def rollback &block
|
39
|
+
set_block :rollback, block
|
40
|
+
end
|
41
|
+
|
42
|
+
def success &block
|
43
|
+
set_block :success, block
|
44
|
+
end
|
45
|
+
|
46
|
+
def timeout secs
|
47
|
+
set_option :timeout, secs
|
48
|
+
end
|
49
|
+
|
50
|
+
def retries count
|
51
|
+
if count.is_a? Enumerator then
|
52
|
+
set_option :retries, count.to_a
|
53
|
+
else
|
54
|
+
set_option :retries, count.times.to_a
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def collins
|
59
|
+
Genesis::Framework::Utils.collins
|
60
|
+
end
|
61
|
+
|
62
|
+
def facter
|
63
|
+
# lets cache the facts on first use
|
64
|
+
# TODO symbolize these keys?
|
65
|
+
# TODO implement method_missing? on this hash for easy access
|
66
|
+
Genesis::Framework::Utils.facter
|
67
|
+
end
|
68
|
+
|
69
|
+
def run_cmd *cmd, stdin_data: '', return_both_streams: false, return_merged_streams: false
|
70
|
+
if return_both_streams && return_merged_streams
|
71
|
+
raise "Invalid run_cmd invocation, can's specify both and merged together"
|
72
|
+
end
|
73
|
+
|
74
|
+
if return_merged_streams
|
75
|
+
output, status = Open3.capture2e(*cmd, stdin_data: stdin_data)
|
76
|
+
else
|
77
|
+
stdout, stderr, status = Open3.capture3(*cmd, stdin_data: stdin_data)
|
78
|
+
if return_both_streams
|
79
|
+
output = [stdout, stderr]
|
80
|
+
else
|
81
|
+
output = stdout
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
if status.exitstatus != 0
|
86
|
+
log("Run Command failed for '%s' with exit status '%d' and output: %s" % [cmd.to_s, status.exitstatus, output.to_s])
|
87
|
+
raise 'run_cmd exited with status: ' + status.exitstatus.to_s
|
88
|
+
end
|
89
|
+
|
90
|
+
return output
|
91
|
+
end
|
92
|
+
|
93
|
+
def config
|
94
|
+
# We are intentionally causing a deep copy here so one task
|
95
|
+
# can't pollute another task's config setup
|
96
|
+
# TODO: consider possibly patching hash to not allow setting members?
|
97
|
+
@config ||= Marshal.load(Marshal.dump(Genesis::Framework::Utils.config_cache))
|
98
|
+
end
|
99
|
+
|
100
|
+
def log message
|
101
|
+
Genesis::Framework::Utils.log(self.class.name, message)
|
102
|
+
end
|
103
|
+
|
104
|
+
def prompt message, seconds=15, default=false
|
105
|
+
Genesis::PromptCLI.ask message, seconds, default
|
106
|
+
end
|
107
|
+
|
108
|
+
def install provider, *what
|
109
|
+
if provider == :rpm
|
110
|
+
Kernel.system("yum", "install", "-y", *what)
|
111
|
+
if $?.exitstatus != 0
|
112
|
+
raise 'yum install exited with status: ' + $?.exitstatus.to_s
|
113
|
+
end
|
114
|
+
elsif provider == :gem
|
115
|
+
# we give a decent try at detecting if the gem is installed before trying to reinstall again
|
116
|
+
# if it contains a - (aka you are specifying a specific version or a / (aka you are specifying a path to find it)
|
117
|
+
# then we punt on trying to determine if the gem is already installed and just pass it to install anyway
|
118
|
+
gems = what.select { |item| item.include?("-") || item.include?("/") || Gem::Dependency.new(item).matching_specs.count == 0 }
|
119
|
+
Kernel.system("gem", "install", "--no-ri", "--no-rdoc", *gems)
|
120
|
+
if $?.exitstatus != 0
|
121
|
+
raise 'gem install exited with status: ' + $?.exitstatus.to_s
|
122
|
+
end
|
123
|
+
|
124
|
+
# now need to clear out the Gem cache so we can load it
|
125
|
+
Gem.clear_paths
|
126
|
+
|
127
|
+
# Now we require all the gems you asked to be installed
|
128
|
+
what.all? { |gem| require gem }
|
129
|
+
else
|
130
|
+
raise "Unknown install provider: " + provider.to_s
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def fetch what, filename, base_url: ENV['GENESIS_URL']
|
135
|
+
filepath = tmp_path(filename)
|
136
|
+
Genesis::RetryingFetcher.get(what, base_url) {|data| File.open(filepath, "w", 0755) { |file| file.write data }}
|
137
|
+
end
|
138
|
+
|
139
|
+
def tmp_path filename
|
140
|
+
Genesis::Framework::Utils.tmp_path(filename, self.class.name)
|
141
|
+
end
|
142
|
+
|
143
|
+
#############################################################
|
144
|
+
# These methods are private and not part of the exposed DSL.
|
145
|
+
# Use at your own risk.
|
146
|
+
#############################################################
|
147
|
+
|
148
|
+
def set_block sym, block
|
149
|
+
self.init_defaults
|
150
|
+
self.blocks[sym] = block
|
151
|
+
end
|
152
|
+
|
153
|
+
def add_block sym, description, block
|
154
|
+
self.init_defaults
|
155
|
+
if self.blocks[sym].has_key?(description)
|
156
|
+
raise "Task defines multiple conditions with the same description"
|
157
|
+
end
|
158
|
+
self.blocks[sym][description] = block
|
159
|
+
end
|
160
|
+
|
161
|
+
def set_option sym, option
|
162
|
+
self.init_defaults
|
163
|
+
self.options[sym] = option
|
164
|
+
end
|
165
|
+
|
166
|
+
def init_defaults
|
167
|
+
self.blocks ||= { :precondition => {}, :init => nil, :condition => {}, :run => nil, :rollback => nil, :success => nil }
|
168
|
+
self.options ||= { :retries => 3.times.to_a, :timeout => 0, :description => nil }
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'promptcli'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Genesis
|
6
|
+
module Framework
|
7
|
+
module Tasks
|
8
|
+
def self.load_config file
|
9
|
+
begin
|
10
|
+
data = File.read(file)
|
11
|
+
|
12
|
+
## TODO: consider tokenizing the keys of the hash? needed???
|
13
|
+
Genesis::Framework::Utils.config_cache = YAML::load(data)
|
14
|
+
rescue => e
|
15
|
+
raise "Unable to parse config %s: %s" % [file, e.message]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.call_block blocks, sym, msg = nil
|
20
|
+
if blocks.has_key?(sym) && blocks[sym].respond_to?(:call)
|
21
|
+
puts msg if msg
|
22
|
+
blocks[sym].call
|
23
|
+
else
|
24
|
+
true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.load_tasks dir
|
29
|
+
# expand the LOAD_PATH to include modules, so facts are available
|
30
|
+
$:.unshift File.join(File.expand_path(dir),'modules')
|
31
|
+
puts "\nParsing tasks from directory: %s" % [dir]
|
32
|
+
|
33
|
+
Dir.glob(File.join(dir,'*.rb')) do |f|
|
34
|
+
begin
|
35
|
+
Genesis::Framework::Tasks.class_eval File.read(f)
|
36
|
+
rescue => e
|
37
|
+
raise "Error parsing task %s: %s" % [f, e.message]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
@tasks = Genesis::Framework::Tasks.constants.select do |c|
|
42
|
+
Genesis::Framework::Tasks.const_get(c).include?( Genesis::Framework::Task )
|
43
|
+
end
|
44
|
+
|
45
|
+
@tasks.sort!
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.execute task_name
|
49
|
+
puts "\n#{task_name}\n================================================="
|
50
|
+
|
51
|
+
return unless Genesis::PromptCLI.ask("Would you like to run this task?", 10, true) == true
|
52
|
+
|
53
|
+
task = Genesis::Framework::Tasks.const_get(task_name)
|
54
|
+
|
55
|
+
if task.blocks.nil?
|
56
|
+
puts "task is empty with nothing to do, skipping..."
|
57
|
+
return true
|
58
|
+
end
|
59
|
+
|
60
|
+
begin
|
61
|
+
puts "task is now testing if it needs to be initialized..."
|
62
|
+
if task.blocks.has_key?(:precondition)
|
63
|
+
task.blocks[:precondition].each do |description, block|
|
64
|
+
puts "Testing: %s" % description
|
65
|
+
unless self.call_block(task.blocks[:precondition], description)
|
66
|
+
puts "task is being skipped..."
|
67
|
+
return true
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
rescue => e
|
72
|
+
puts "%s task had error on testing if it needs initialization: %s" % [task_name, e.message]
|
73
|
+
return false
|
74
|
+
end
|
75
|
+
|
76
|
+
begin
|
77
|
+
puts "task is now initializing..."
|
78
|
+
self.call_block(task.blocks, :init);
|
79
|
+
puts "task is now initialized..."
|
80
|
+
rescue => e
|
81
|
+
puts "%s task threw error on initialization: %s" % [task_name, e.message]
|
82
|
+
return false
|
83
|
+
end
|
84
|
+
|
85
|
+
begin
|
86
|
+
puts "task is now testing if it can run..."
|
87
|
+
if task.blocks.has_key?(:condition)
|
88
|
+
task.blocks[:condition].each do |description, block|
|
89
|
+
puts "Checking: %s" % description
|
90
|
+
unless self.call_block(task.blocks[:condition], description)
|
91
|
+
puts "Conditional failed. Task is being skipped."
|
92
|
+
return true
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
rescue => e
|
97
|
+
puts "%s task had error on testing if it needs running: %s" % [task_name, e.message]
|
98
|
+
return false
|
99
|
+
end
|
100
|
+
|
101
|
+
success = nil
|
102
|
+
task.options[:retries].each_with_index do |sleep_interval, index|
|
103
|
+
attempt = index + 1
|
104
|
+
begin
|
105
|
+
puts "task is attempting run #%d..." % [attempt]
|
106
|
+
Timeout::timeout(task.options[:timeout]) do
|
107
|
+
success = self.call_block(task.blocks, :run)
|
108
|
+
end
|
109
|
+
# a run block should raise an error or be false for a failure
|
110
|
+
success = true if success.nil?
|
111
|
+
rescue => e
|
112
|
+
puts "%s task [run #%d] caused error: %s" % [task_name, attempt, e.message]
|
113
|
+
success = nil # cause a retry
|
114
|
+
end
|
115
|
+
break unless success.nil? # if we got an answer, we're done
|
116
|
+
puts "task is sleeping for %d seconds..." % [sleep_interval]
|
117
|
+
Kernel.sleep(sleep_interval)
|
118
|
+
end
|
119
|
+
success = false if success.nil? # must have used all the retries, fail
|
120
|
+
|
121
|
+
if success
|
122
|
+
success = self.call_block(task.blocks, :success)
|
123
|
+
puts "task is successful!"
|
124
|
+
else
|
125
|
+
puts 'task failed!!!'
|
126
|
+
success = self.call_block(task.blocks, :rollback, "rolling back!")
|
127
|
+
end
|
128
|
+
|
129
|
+
puts "\n\n"
|
130
|
+
return success
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'syslog'
|
2
|
+
require 'collins_client'
|
3
|
+
require 'facter'
|
4
|
+
|
5
|
+
module Genesis
|
6
|
+
module Framework
|
7
|
+
module Utils
|
8
|
+
def self.tmp_path filename, sandbox = ""
|
9
|
+
location = File.join(ENV['GENESIS_ROOT'], "tmp", sandbox)
|
10
|
+
Dir.mkdir(location, 0755) unless File.directory? location
|
11
|
+
File.join(location, filename)
|
12
|
+
end
|
13
|
+
|
14
|
+
@@config_cache = Hash.new
|
15
|
+
@@collins_conn = nil
|
16
|
+
@@facter = nil
|
17
|
+
|
18
|
+
# mimicking rail's cattr_accessor
|
19
|
+
def self.config_cache
|
20
|
+
@@config_cache
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.config_cache= (obj)
|
24
|
+
@@config_cache = obj
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.collins
|
28
|
+
if @@collins_conn.nil?
|
29
|
+
cfg = { :host => self.config_cache['collins']['host'], :username => self.config_cache['collins']['username'], :password => self.config_cache['collins']['password'] }
|
30
|
+
@@collins_conn = ::Collins::Client.new(cfg)
|
31
|
+
end
|
32
|
+
|
33
|
+
@@collins_conn
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.facter
|
37
|
+
if @@facter.nil?
|
38
|
+
@@facter = Facter.to_hash
|
39
|
+
end
|
40
|
+
|
41
|
+
@@facter
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.log subsystem, message
|
45
|
+
logline = subsystem.to_s + " :: " + message
|
46
|
+
puts logline
|
47
|
+
Syslog.open("genesis", Syslog::LOG_PID, Syslog::LOG_USER) unless Syslog.opened?
|
48
|
+
Syslog.log(Syslog::LOG_INFO, logline)
|
49
|
+
if self.facter['asset_tag']
|
50
|
+
self.collins.log! self.facter['asset_tag'], message
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
metadata
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: genesis_framework
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jeremy Johnstone
|
8
|
+
- Roy Marantz
|
9
|
+
- Gabe Conradi
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2014-12-29 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: genesis_promptcli
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0.2'
|
22
|
+
- - '>='
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.2.0
|
25
|
+
type: :runtime
|
26
|
+
prerelease: false
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ~>
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '0.2'
|
32
|
+
- - '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.2.0
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: genesis_retryingfetcher
|
37
|
+
requirement: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ~>
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0.4'
|
42
|
+
- - '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 0.4.0
|
45
|
+
type: :runtime
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ~>
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0.4'
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.4.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: collins_client
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.2'
|
62
|
+
- - '>='
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 0.2.0
|
65
|
+
type: :runtime
|
66
|
+
prerelease: false
|
67
|
+
version_requirements: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ~>
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0.2'
|
72
|
+
- - '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 0.2.0
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: facter
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ~>
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '2.0'
|
82
|
+
- - '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: 2.0.0
|
85
|
+
type: :runtime
|
86
|
+
prerelease: false
|
87
|
+
version_requirements: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ~>
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '2.0'
|
92
|
+
- - '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: 2.0.0
|
95
|
+
description: Genesis is a project used to manage provisioning of hardware. This is
|
96
|
+
the framework which runs the specified tasks.
|
97
|
+
email: opensourcesoftware@tumblr.com
|
98
|
+
executables:
|
99
|
+
- genesis
|
100
|
+
extensions: []
|
101
|
+
extra_rdoc_files: []
|
102
|
+
files:
|
103
|
+
- bin/genesis
|
104
|
+
- lib/genesisframework/facter/test_fact.rb
|
105
|
+
- lib/genesisframework/task.rb
|
106
|
+
- lib/genesisframework/tasks.rb
|
107
|
+
- lib/genesisframework/utils.rb
|
108
|
+
- lib/genesisframework.rb
|
109
|
+
homepage: https://github.ewr01.tumblr.net/Tumblr/genesis
|
110
|
+
licenses:
|
111
|
+
- Apache License, 2.0
|
112
|
+
metadata: {}
|
113
|
+
post_install_message:
|
114
|
+
rdoc_options: []
|
115
|
+
require_paths:
|
116
|
+
- lib
|
117
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - '>='
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - '>='
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
requirements: []
|
128
|
+
rubyforge_project:
|
129
|
+
rubygems_version: 2.0.14
|
130
|
+
signing_key:
|
131
|
+
specification_version: 4
|
132
|
+
summary: Generic server onboarding framework
|
133
|
+
test_files: []
|