foreman_hooks 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,11 +1,12 @@
1
1
  # foreman_hooks
2
2
 
3
3
  Allows you to trigger scripts and commands on the Foreman server at any point
4
- in an object's lifecycle in Foreman. This lets you run a script when a host
4
+ in an object's lifecycle in Foreman. This lets you run scripts when a host
5
5
  is created, or finishes provisioning etc.
6
6
 
7
- It observes every object in Foreman and exposes the Rails callbacks by running
8
- scripts within its hooks directory.
7
+ It enables extension of Foreman's host orchestration so additional tasks can
8
+ be executed, and can register hooks into standard Rails callbacks for any
9
+ Foreman object, all with shell scripts.
9
10
 
10
11
  # Installation:
11
12
 
@@ -29,7 +30,8 @@ To upgrade to newest version of the plugin:
29
30
 
30
31
  Hooks are stored in `/usr/share/foreman/config/hooks` (`~foreman/config/hooks`)
31
32
  with a subdirectory for the object, then a subdirectory for the event name.
32
- Each file within the directory is executed in alphabetical order.
33
+
34
+ ~foreman/config/hooks/$OBJECT/$EVENT/$HOOK_SCRIPT
33
35
 
34
36
  Examples:
35
37
 
@@ -37,17 +39,15 @@ Examples:
37
39
  ~foreman/config/hooks/host/destroy/15_cleanup_database.sh
38
40
  ~foreman/config/hooks/smart_proxy/after_create/01_email_operations.sh
39
41
 
40
- Note that in Foreman 1.1, hosts are just named `Host` so hooks go in a `host/`
41
- directory, while in Foreman 1.2 they're `Host::Base` and `Host::Managed`, so
42
- the hook directory becomes `host/base/` and `host/managed/` respectively.
42
+ In Foreman 1.1, all hosts are `host` but in Foreman 1.2, managed hosts will
43
+ become `host/managed` instead.
43
44
 
44
45
  ## Objects / Models
45
46
 
46
47
  Every object (or model in Rails terms) in Foreman can have hooks. Check
47
48
  `~foreman/app/models` for the full list, but these are the interesting ones:
48
49
 
49
- * `host` (Foreman 1.1), `host/managed` (Foreman 1.2)
50
- * `host/discovered` (Foreman 1.2)
50
+ * `host` (or `host/managed` in Foreman 1.2)
51
51
  * `report`
52
52
 
53
53
  ## Orchestration events
@@ -65,21 +65,22 @@ To add hooks to these, use these event names:
65
65
 
66
66
  ## Rails events
67
67
 
68
- These are the most interesting events that Rails provides and this plugin
69
- exposes:
68
+ For hooks on anything apart from hosts (which support orchestration, as above)
69
+ then the standard Rails events will be needed. These are the most interesting
70
+ events provided:
70
71
 
71
- * `after_create`
72
- * `after_destroy`
72
+ * `after_create`, `before_create`
73
+ * `after_destroy`, `before_destroy`
73
74
 
74
75
  Every event has a "before" and "after" hook. For the full list, see the
75
76
  Constants section at the bottom of the
76
77
  [ActiveRecord::Callbacks](http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html)
77
78
  documentation.
78
79
 
79
- The host object has two special callbacks in Foreman 1.1 that you can use:
80
+ The host object has two additional callbacks that you can use:
80
81
 
81
- * `host/after_build` triggers when a host is put into Build mode(??)
82
- * `host/before_provision` triggers... (??)
82
+ * `host/after_build` triggers when a host is put into build mode
83
+ * `host/before_provision` triggers when a host completes the OS install
83
84
 
84
85
  ## Execution of hooks
85
86
 
@@ -89,16 +90,33 @@ Hooks are executed in the context of the Foreman server, so usually under the
89
90
  The first argument is always the event name, enabling scripts to be symlinked
90
91
  into multiple event directories. The second argument is the string
91
92
  representation of the object that was hooked, e.g. the hostname for a host.
92
- No other data about the object is currently made available.
93
+
94
+ ~foreman/config/hooks/host/create/50_register_system.sh create foo.example.com
95
+
96
+ A JSON representation of the hook object will be passed in on stdin. A utility
97
+ to read this with jgrep is provided in `examples/hook_functions.sh` and
98
+ sourcing this utility script will be enough for most users. Otherwise, you
99
+ may want to ensure stdin is closed.
100
+
101
+ echo '{"host"=>{"name"=>"foo.example.com"}}' \
102
+ | ~foreman/config/hooks/host/create/50_register_system.sh \
103
+ create foo.example.com
93
104
 
94
105
  Every hook within the event directory is executed in alphabetical order. For
95
106
  orchestration hooks, an integer prefix in the hook filename will be used as
96
107
  the priority value, so influences where it's done in relation to DNS, DHCP, VM
97
108
  creation and other tasks.
98
109
 
99
- If a hook fails (non-zero return code), the event is logged.
110
+ ## Hook failures and rollback
111
+
112
+ If a hook fails (non-zero return code), the event is logged. For Rails events,
113
+ execution of other hooks will continue.
114
+
100
115
  For orchestration events, a failure will halt the action and rollback will
101
- occur. For Rails events, execution of other hooks will continue.
116
+ occur. If another orchestration action fails, the hook might be called again
117
+ to rollback its action - in this case the first argument will change as
118
+ appropriate, so must be obeyed by the script (e.g. a "create" hook will be
119
+ called with "destroy" if it has to be rolled back later).
102
120
 
103
121
  # Copyright
104
122
 
@@ -0,0 +1,28 @@
1
+ # Utilities for hook scripts, used with foreman_hooks
2
+ #
3
+ # Source this at the start of any bash hook script:
4
+ #
5
+ # . hook_functions.sh
6
+ #
7
+ # It reads in a JSON representation of the object and then provides a
8
+ # hook_data wrapper around jgrep to read fields from it, e.g.
9
+ #
10
+ # hostname=$(hook_data host.name)
11
+ # comment=$(hook_data host.comment)
12
+
13
+ # update, create, before_destroy etc.
14
+ HOOK_EVENT=$1
15
+ # to_s representation of the object, e.g. host's fqdn
16
+ HOOK_OBJECT=$2
17
+
18
+ HOOK_OBJECT_FILE=$(mktemp foreman_hooks.XXXXXXXXXX)
19
+ trap "rm -f $HOOK_OBJECT_FILE" EXIT
20
+ cat > $HOOK_OBJECT_FILE
21
+
22
+ hook_data() {
23
+ if [ $# -eq 1 ]; then
24
+ jgrep -s "$1" < $HOOK_OBJECT_FILE
25
+ else
26
+ jgrep "$*" < $HOOK_OBJECT_FILE
27
+ fi
28
+ }
data/examples/log.sh ADDED
@@ -0,0 +1,19 @@
1
+ #!/bin/bash
2
+
3
+ . $(dirname $0)/hook_functions.sh
4
+
5
+ # event name (create, before_destroy etc.)
6
+ # orchestration hooks must obey this to support rollbacks (create/update/destroy)
7
+ event=${HOOK_EVENT}
8
+
9
+ # to_s representation of the object, e.g. host's fqdn
10
+ object=${HOOK_OBJECT}
11
+
12
+ # Example of using hook_data to query the JSON representation of the object
13
+ # passed by foreman_hooks. `cat $HOOK_OBJECT_FILE` to see the contents.
14
+ hostname=$(hook_data host.name)
15
+
16
+ echo "$(date): received ${event} on ${object}" >> /tmp/hook.log
17
+
18
+ # exit code is important on orchestration tasks
19
+ exit 0
@@ -1,8 +1,8 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "foreman_hooks"
3
3
 
4
- s.version = "0.2.0"
5
- s.date = "2013-03-24"
4
+ s.version = "0.3.0"
5
+ s.date = "2013-04-06"
6
6
 
7
7
  s.summary = "Run custom hook scripts on Foreman events"
8
8
  s.description = "Plugin engine for Foreman that enables running custom hook scripts on Foreman events"
@@ -13,10 +13,7 @@ module ForemanHooks
13
13
 
14
14
  # Find any orchestration related hooks and register in those classes
15
15
  ForemanHooks::HooksObserver.hooks.each do |klass,events|
16
- orchestrate = false
17
- events.keys.each do |event|
18
- orchestrate = true if ['create', 'update', 'destroy'].include? event
19
- end
16
+ orchestrate = events.keys.detect { |event| ['create', 'update', 'destroy'].include? event }
20
17
  klass.send(:include, ForemanHooks::OrchestrationHook) if orchestrate
21
18
  end
22
19
  end
@@ -53,7 +53,7 @@ module ForemanHooks::OrchestrationHook
53
53
  end
54
54
 
55
55
  def hook_execute_set
56
- @obj.exec_hook(@filename, @event, *args)
56
+ @obj.exec_hook(@filename, @event == 'destroy' ? 'create' : @event, *args)
57
57
  end
58
58
 
59
59
  def hook_execute_del
@@ -63,14 +63,25 @@ module ForemanHooks::Util
63
63
  def exec_hook(*args)
64
64
  logger.debug "Running hook: #{args.join(' ')}"
65
65
  success = if defined? Bundler && Bundler.responds_to(:with_clean_env)
66
- Bundler.with_clean_env { system(*args) }
66
+ Bundler.with_clean_env { exec_hook_int(self.to_json, *args) }
67
67
  else
68
- system(*args)
69
- end
68
+ exec_hook_int(self.to_json, *args)
69
+ end.success?
70
70
 
71
71
  unless success
72
72
  logger.warn "Hook failure running `#{args.join(' ')}`: #{$?}"
73
73
  end
74
74
  success
75
75
  end
76
+
77
+ def exec_hook_int(stdin_data, *args)
78
+ output = nil
79
+ IO.popen(args.push(:err=>[:child, :out]), mode='r+') do |io|
80
+ io.write(stdin_data)
81
+ io.close_write
82
+ output = io.read
83
+ end
84
+ logger.debug "Hook output: #{output}" if output && !output.empty?
85
+ $?
86
+ end
76
87
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_hooks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-24 00:00:00.000000000 Z
12
+ date: 2013-04-06 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Plugin engine for Foreman that enables running custom hook scripts on
15
15
  Foreman events
@@ -28,6 +28,8 @@ files:
28
28
  - README.md
29
29
  - Rakefile
30
30
  - TODO
31
+ - examples/hook_functions.sh
32
+ - examples/log.sh
31
33
  - foreman_hooks.gemspec
32
34
  - lib/foreman_hooks.rb
33
35
  - lib/foreman_hooks/engine.rb