hookit 0.7.11

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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +22 -0
  5. data/README.md +3 -0
  6. data/Rakefile +10 -0
  7. data/bin/hookit +41 -0
  8. data/hookit.gemspec +30 -0
  9. data/lib/hookit/converginator.rb +141 -0
  10. data/lib/hookit/db.rb +37 -0
  11. data/lib/hookit/error.rb +8 -0
  12. data/lib/hookit/exit.rb +23 -0
  13. data/lib/hookit/helper/cron.rb +42 -0
  14. data/lib/hookit/helper/nfs.rb +113 -0
  15. data/lib/hookit/helper/shell.rb +35 -0
  16. data/lib/hookit/helper/xml.rb +25 -0
  17. data/lib/hookit/helper.rb +9 -0
  18. data/lib/hookit/hook.rb +102 -0
  19. data/lib/hookit/logger.rb +58 -0
  20. data/lib/hookit/logvac.rb +34 -0
  21. data/lib/hookit/platform/base.rb +11 -0
  22. data/lib/hookit/platform/smartos.rb +19 -0
  23. data/lib/hookit/platform/ubuntu.rb +19 -0
  24. data/lib/hookit/platform.rb +9 -0
  25. data/lib/hookit/platforms.rb +2 -0
  26. data/lib/hookit/registry.rb +53 -0
  27. data/lib/hookit/resource/base.rb +105 -0
  28. data/lib/hookit/resource/cron.rb +26 -0
  29. data/lib/hookit/resource/directory.rb +67 -0
  30. data/lib/hookit/resource/execute.rb +148 -0
  31. data/lib/hookit/resource/file.rb +71 -0
  32. data/lib/hookit/resource/hook_file.rb +80 -0
  33. data/lib/hookit/resource/link.rb +61 -0
  34. data/lib/hookit/resource/logrotate.rb +38 -0
  35. data/lib/hookit/resource/mount.rb +77 -0
  36. data/lib/hookit/resource/package.rb +74 -0
  37. data/lib/hookit/resource/rsync.rb +67 -0
  38. data/lib/hookit/resource/scp.rb +87 -0
  39. data/lib/hookit/resource/service.rb +89 -0
  40. data/lib/hookit/resource/socket.rb +67 -0
  41. data/lib/hookit/resource/template.rb +87 -0
  42. data/lib/hookit/resource/warning.rb +87 -0
  43. data/lib/hookit/resource/zfs.rb +99 -0
  44. data/lib/hookit/resource.rb +21 -0
  45. data/lib/hookit/resources.rb +15 -0
  46. data/lib/hookit/version.rb +3 -0
  47. data/lib/hookit.rb +27 -0
  48. metadata +205 -0
@@ -0,0 +1,58 @@
1
+ module Hookit
2
+ class Logger
3
+
4
+ attr_reader :log_file, :log_level
5
+
6
+ def initialize(file, level)
7
+ @log_file = file || '/var/log/hookit/hookit.log'
8
+ @log_level = level || :error
9
+ end
10
+
11
+ def log(level, message)
12
+
13
+ if not message
14
+ message = level
15
+ level = :error
16
+ end
17
+
18
+ if level_to_int(level) <= level_to_int(log_level)
19
+ send(level, message)
20
+ end
21
+
22
+ end
23
+
24
+ def error(message)
25
+ log! "[error]: #{message}\n"
26
+ end
27
+
28
+ def warn(message)
29
+ log! "[warn]: #{message}\n"
30
+ end
31
+
32
+ def info(message)
33
+ log! "[info]: #{message}\n"
34
+ end
35
+
36
+ def debug(message)
37
+ log! "[debug]: #{message}\n"
38
+ end
39
+
40
+ protected
41
+
42
+ def log!(message)
43
+ File.open log_file, 'a' do |f|
44
+ f.write message
45
+ end
46
+ end
47
+
48
+ def level_to_int(level)
49
+ case level
50
+ when :error then 1
51
+ when :warn then 2
52
+ when :info then 3
53
+ when :debug then 4
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,34 @@
1
+ require 'faraday'
2
+
3
+ module Hookit
4
+ class Logvac
5
+
6
+ def initialize(opts)
7
+ @app = opts[:app]
8
+ @deploy = opts[:deploy]
9
+ @token = opts[:token]
10
+ end
11
+
12
+ def post(message)
13
+ connection.post("/deploy/#{@app}") do |req|
14
+ req.headers[:x_auth_token] = @token
15
+ req.headers[:x_deploy_id] = @deploy
16
+ req.body = message
17
+ end
18
+ end
19
+ alias :print :post
20
+
21
+ def puts(message='')
22
+ post("#{message}\n")
23
+ end
24
+
25
+ protected
26
+
27
+ def connection
28
+ @connection ||= Faraday.new(url: 'http://logvac.admin.pagodabox.io:6361') do |faraday|
29
+ faraday.adapter :excon
30
+ end
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,11 @@
1
+ module Hookit
2
+ module Platform
3
+ class Base
4
+
5
+ def detect?; end
6
+ def name; end
7
+ def os; end
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ module Hookit
2
+ module Platform
3
+ class Smartos < Base
4
+
5
+ def detect?
6
+ ! `cat /etc/release 2>/dev/null | grep -i SmartOS`.empty?
7
+ end
8
+
9
+ def name
10
+ 'smartos'
11
+ end
12
+
13
+ def os
14
+ 'sun'
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ module Hookit
2
+ module Platform
3
+ class Ubuntu < Base
4
+
5
+ def detect?
6
+ ! `[ -x /usr/bin/lsb_release ] && /usr/bin/lsb_release -i 2>/dev/null | grep Ubuntu`.empty?
7
+ end
8
+
9
+ def name
10
+ 'ubuntu'
11
+ end
12
+
13
+ def os
14
+ 'linux'
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ require 'hookit/platform/base'
2
+ require 'hookit/platform/smartos'
3
+ require 'hookit/platform/ubuntu'
4
+
5
+ module Hookit
6
+ module Platform
7
+
8
+ end
9
+ end
@@ -0,0 +1,2 @@
1
+ Hookit.platforms.register(:smartos) { Hookit::Platform::Smartos }
2
+ Hookit.platforms.register(:ubuntu) { Hookit::Platform::Ubuntu }
@@ -0,0 +1,53 @@
1
+ module Hookit
2
+ # Register components in a single location that can be queried.
3
+ #
4
+ # This allows certain components (such as guest systems, configuration
5
+ # pieces, etc.) to be registered and queried.
6
+ class Registry
7
+ def initialize
8
+ @actions = {}
9
+ @results_cache = {}
10
+ end
11
+
12
+ # Register a callable by key.
13
+ #
14
+ # The callable should be given in a block which will be lazily evaluated
15
+ # when the action is needed.
16
+ #
17
+ # If an action by the given name already exists then it will be
18
+ # overwritten.
19
+ def register(key, value=nil, &block)
20
+ @results_cache.delete key
21
+ block = lambda { value } unless value.nil?
22
+ @actions[key] = block
23
+ end
24
+
25
+ # Get an action by the given key.
26
+ #
27
+ # This will evaluate the block given to `register` and return the resulting
28
+ # action stack.
29
+ def get(key)
30
+ return nil unless @actions.has_key?(key)
31
+ return @results_cache[key] if @results_cache.has_key?(key)
32
+ @results_cache[key] = @actions[key].call
33
+ end
34
+ alias :[] :get
35
+
36
+ # Iterate over the keyspace.
37
+ def each(&block)
38
+ @actions.each do |key, _|
39
+ yield key, get(key)
40
+ end
41
+ end
42
+
43
+ # Converts this registry to a hash
44
+ def to_hash
45
+ result = {}
46
+ self.each do |key, value|
47
+ result[key] = value
48
+ end
49
+
50
+ result
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,105 @@
1
+ module Hookit
2
+ module Resource
3
+ class Base
4
+
5
+ class << self
6
+
7
+ def field(key)
8
+ define_method key do |*args, &block|
9
+ if data = block || args[0]
10
+ instance_variable_set("@#{key}", data)
11
+ else
12
+ instance_variable_get("@#{key}")
13
+ end
14
+ end
15
+ end
16
+
17
+ def actions(*actions)
18
+ if actions.any?
19
+ @actions = *actions
20
+ else
21
+ @actions
22
+ end
23
+ end
24
+
25
+ def default_action(action=nil)
26
+ if action
27
+ @default_action = action
28
+ else
29
+ @default_action || :run
30
+ end
31
+ end
32
+
33
+ end
34
+
35
+ attr_accessor :dict
36
+
37
+ field :name
38
+
39
+ def initialize(name)
40
+ name(name)
41
+ end
42
+
43
+ def run(action); end
44
+
45
+ def can_run?
46
+ only_if_res = true
47
+ not_if_res = false
48
+
49
+ if only_if and only_if.respond_to? :call
50
+ only_if_res = only_if.call
51
+ end
52
+
53
+ if not_if and not_if.respond_to? :call
54
+ not_if_res = not_if.call
55
+ end
56
+
57
+ only_if_res and not not_if_res
58
+ end
59
+
60
+ def action(*actions)
61
+ if actions.any?
62
+ actions.each do |action|
63
+ if not self.class.actions.include? action
64
+ raise Hookit::Error::UnknownAction, "unknown action '#{action}'"
65
+ end
66
+ end
67
+ @actions = *actions
68
+ else
69
+ @actions || [default_action]
70
+ end
71
+ end
72
+
73
+ def default_action
74
+ self.class.default_action
75
+ end
76
+
77
+ def not_if(&block)
78
+ if block_given?
79
+ @not_if = block
80
+ else
81
+ @not_if
82
+ end
83
+ end
84
+
85
+ def only_if(&block)
86
+ if block_given?
87
+ @only_if = block
88
+ else
89
+ @only_if
90
+ end
91
+ end
92
+
93
+ protected
94
+
95
+ def run_command!(cmd, expect_code=0)
96
+ `#{cmd}`
97
+ code = $?.exitstatus
98
+ if code != expect_code
99
+ raise Hookit::Error::UnexpectedExit, "#{cmd} failed with exit code '#{code}'"
100
+ end
101
+ end
102
+
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,26 @@
1
+ module Hookit
2
+ module Resource
3
+ class Cron < Execute
4
+
5
+ def initialize(name)
6
+ super
7
+ timeout 60
8
+ cwd '/data'
9
+ end
10
+
11
+ protected
12
+
13
+ def run!
14
+ begin
15
+ Timeout::timeout(timeout) do
16
+ f = IO.popen("#{cmd} || exit 0", :err=>[:child, :out])
17
+ puts f.readline while true
18
+ end
19
+ rescue Timeout::Error
20
+ $stderr.puts 'Timed out running cron! Consider using a worker.'
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,67 @@
1
+ module Hookit
2
+ module Resource
3
+ class Directory < Base
4
+
5
+ field :path
6
+ field :recursive
7
+ field :mode
8
+ field :owner
9
+ field :group
10
+
11
+ actions :create, :delete
12
+ default_action :create
13
+
14
+ def initialize(name)
15
+ path name unless path
16
+ super
17
+ end
18
+
19
+ def run(action)
20
+ case action
21
+ when :create
22
+ create!
23
+ chown!
24
+ chmod!
25
+ when :delete
26
+ delete!
27
+ end
28
+ end
29
+
30
+ protected
31
+
32
+ def create!
33
+ return if ::File.exists? path
34
+ cmd = "mkdir #{"-p " if recursive}#{path}"
35
+ `#{cmd}`
36
+ code = $?.exitstatus
37
+ if code != 0
38
+ raise Hookit::Error::UnexpectedExit, "#{cmd} failed with exit code '#{code}'"
39
+ end
40
+ end
41
+
42
+ def delete!
43
+ return if not ::File.exists? path
44
+ cmd = "rm -rf #{path}"
45
+ `#{cmd}`
46
+ code = $?.exitstatus
47
+ if code != 0
48
+ raise Hookit::Error::UnexpectedExit, "#{cmd} failed with exit code '#{code}'"
49
+ end
50
+ end
51
+
52
+ def chown!
53
+ return unless owner or group
54
+ if ::File.exists? path
55
+ `chown #{(group.nil?) ? owner : "#{owner}:#{group}"} #{path}`
56
+ end
57
+ end
58
+
59
+ def chmod!
60
+ if ::File.exists? path and mode
61
+ ::File.chmod(mode, path)
62
+ end
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,148 @@
1
+ require 'timeout'
2
+
3
+ module Hookit
4
+ module Resource
5
+ class Execute < Base
6
+
7
+ field :command
8
+ field :cwd
9
+ field :environment
10
+ field :user
11
+ field :path
12
+ field :returns
13
+ field :timeout
14
+ field :stream
15
+ field :on_data
16
+ field :validator
17
+ field :ignore_exit
18
+
19
+ actions :run
20
+ default_action :run
21
+
22
+ def initialize(name)
23
+ command name unless command
24
+ timeout 3600
25
+ returns 0
26
+ super
27
+ end
28
+
29
+ def run(action)
30
+ case action
31
+ when :run
32
+ if stream
33
+ stream!
34
+ else
35
+ run!
36
+ end
37
+ end
38
+ end
39
+
40
+ protected
41
+
42
+ def validate!(res)
43
+ if validator.is_a? Proc
44
+ if validator.call(res)
45
+ res
46
+ else
47
+ raise "ERROR: execute resource \"#{name}\" failed validation!"
48
+ end
49
+ else
50
+ res
51
+ end
52
+ end
53
+
54
+ def run!
55
+ Timeout::timeout(timeout) do
56
+ res = `#{cmd}`
57
+ code = $?.exitstatus
58
+ unexpected_exit(code) unless code == returns
59
+ validate! res
60
+ end
61
+ end
62
+
63
+ def stream!
64
+ result = ""
65
+
66
+ STDOUT.sync = STDERR.sync = true # don't buffer stdout/stderr
67
+
68
+ ::IO.popen(cmd, :err=>[:child, :out]) do |out|
69
+ eof = false
70
+ until eof do
71
+ begin
72
+ chunk = out.readpartial(4096)
73
+ if on_data and on_data.respond_to? :call
74
+ on_data.call(chunk)
75
+ end
76
+ rescue EOFError
77
+ eof = true
78
+ end
79
+ result << chunk.to_s
80
+ end
81
+ end
82
+
83
+ code = $?.exitstatus
84
+ unexpected_exit(code) unless code == returns
85
+
86
+ validate! result
87
+ end
88
+
89
+ def cmd
90
+ com = command
91
+
92
+ if environment
93
+ com = "#{env}#{com}"
94
+ end
95
+
96
+ if path
97
+ com = "export PATH=\"#{path}\"; #{com}"
98
+ end
99
+
100
+ if cwd
101
+ com = "cd #{cwd}; #{com}"
102
+ end
103
+
104
+ if user
105
+ com = su(user, com)
106
+ end
107
+
108
+ com
109
+ end
110
+
111
+ # strategy:
112
+ # 1- escape the escapes
113
+ # 2- escape quotes
114
+ # 3- escape dollar signs
115
+ def escape(cmd)
116
+ cmd.gsub!(/\\/, "\\\\\\")
117
+ cmd.gsub!(/"/, "\\\"")
118
+ cmd.gsub!(/\$/, "\\$")
119
+ cmd
120
+ end
121
+
122
+ def su(user, cmd)
123
+ "su - #{user} -c \"#{escape(cmd)}\""
124
+ end
125
+
126
+ def env
127
+ vars = environment || {}
128
+ env = ''
129
+ vars.each do |key, val|
130
+ env += " " if not env == ''
131
+ env += env_string(key, val)
132
+ end
133
+ (env == '')? env : "#{env}"
134
+ end
135
+
136
+ def env_string(key, val)
137
+ key = key.to_s if not key.is_a? String
138
+ val = val.to_s if not val.is_a? String
139
+ %Q{export #{key.upcase}="#{escape(val)}";}
140
+ end
141
+
142
+ def unexpected_exit(res)
143
+ raise Hookit::Error::UnexpectedExit, "'#{name}' exited with #{res}, expected #{returns}" unless ignore_exit
144
+ end
145
+
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,71 @@
1
+ module Hookit
2
+ module Resource
3
+ class File < Base
4
+
5
+ field :path
6
+ field :content
7
+ field :mode
8
+ field :owner
9
+ field :group
10
+
11
+ actions :create, :create_if_missing, :delete, :touch
12
+ default_action :create
13
+
14
+ def initialize(name)
15
+ path name unless path
16
+ super
17
+ end
18
+
19
+ def run(action)
20
+ case action
21
+ when :create
22
+ create!
23
+ chown!
24
+ chmod!
25
+ when :create_if_missing
26
+ create_if_missing!
27
+ chown!
28
+ chmod!
29
+ when :delete
30
+ delete!
31
+ when :touch
32
+ touch!
33
+ end
34
+ end
35
+
36
+ protected
37
+
38
+ def create!
39
+ ::File.write path, (content || "")
40
+ end
41
+
42
+ def create_if_missing!
43
+ if not ::File.exists? path
44
+ create!
45
+ end
46
+ end
47
+
48
+ def delete!
49
+ ::File.delete path
50
+ end
51
+
52
+ def chown!
53
+ return unless owner or group
54
+ if ::File.exists? path
55
+ `chown #{(group.nil?) ? owner : "#{owner}:#{group}"} #{path}`
56
+ end
57
+ end
58
+
59
+ def chmod!
60
+ if ::File.exists? path and mode
61
+ ::File.chmod(mode, path)
62
+ end
63
+ end
64
+
65
+ def touch!
66
+ `touch -c #{path}`
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,80 @@
1
+ module Hookit
2
+ module Resource
3
+ class HookFile < Base
4
+
5
+ field :path
6
+ field :source
7
+ field :mode
8
+ field :owner
9
+ field :group
10
+
11
+ actions :create, :create_if_missing, :delete, :touch
12
+ default_action :create
13
+
14
+ def initialize(name)
15
+ path name unless path
16
+ source ::File.basename(name)
17
+ super
18
+ end
19
+
20
+ def run(action)
21
+ case action
22
+ when :create
23
+ create!
24
+ chown!
25
+ chmod!
26
+ when :create_if_missing
27
+ create_if_missing!
28
+ chown!
29
+ chmod!
30
+ when :delete
31
+ delete!
32
+ when :touch
33
+ touch!
34
+ end
35
+ end
36
+
37
+ protected
38
+
39
+ def create!
40
+ ::File.write path, render
41
+ end
42
+
43
+ def create_if_missing!
44
+ if not ::File.exists? path
45
+ create!
46
+ end
47
+ end
48
+
49
+ def delete!
50
+ ::File.delete path
51
+ end
52
+
53
+ def chown!
54
+ return unless owner or group
55
+ `chown #{(group.nil?) ? owner : "#{owner}:#{group}"} #{path}`
56
+ end
57
+
58
+ def chmod!
59
+ ::File.chmod(mode, path) if mode
60
+ end
61
+
62
+ def touch!
63
+ `touch -c #{path}`
64
+ end
65
+
66
+ def render
67
+ ::File.read("#{file_dir}/#{source}")
68
+ end
69
+
70
+ def file_dir
71
+ "#{module_root}/files"
72
+ end
73
+
74
+ def module_root
75
+ dict[:module_root]
76
+ end
77
+
78
+ end
79
+ end
80
+ end