genesis_framework 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|