genesis_framework 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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,5 @@
1
+ Facter.add(:test_fact) do
2
+ setcode do
3
+ "gabes_test_fact"
4
+ end
5
+ end
@@ -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: []