gluez 0.2.1 → 0.3

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.
@@ -0,0 +1,131 @@
1
+ module Gluez
2
+ # A context in gluez is a unit of work. It contains one or multiple resources. Context-wide variables can set/get as properties.
3
+ # A context can include a recipe, which is an isolated context of it's own. We can set properties of a recipe from the outer context.
4
+ # This enables us to write modular and reusable recipes.
5
+ class Context
6
+
7
+ # An array of all resources in this context
8
+ attr_reader :resources
9
+
10
+ # A reference to the outer context
11
+ attr_reader :parent
12
+
13
+ def initialize(parent=nil, name=nil, &block)
14
+ @parent = parent
15
+ @name = name
16
+
17
+ @resources = []
18
+ @properties = {}
19
+
20
+ $gluez = self
21
+ instance_eval(&block) if block
22
+
23
+ if self.root == self
24
+ self.generate($simulate == true)
25
+ else
26
+ $gluez = parent
27
+ end
28
+ end
29
+
30
+ # Returns the outer most context
31
+ def root
32
+ self.parent ? self.parent.root : self
33
+ end
34
+
35
+ def default(name, name2)
36
+ unless @properties.key?(name)
37
+ if name2.is_a?(Symbol)
38
+ set(name, get(name2))
39
+ else
40
+ set(name, name2)
41
+ end
42
+ end
43
+ end
44
+
45
+ def expect(name)
46
+ raise "missing #{name}" unless get(name)
47
+ end
48
+
49
+ def locate(resource)
50
+ File.dirname($0) + (@name.nil? ? "" : "/recipes/#{@name}") + "/files/#{resource}"
51
+ end
52
+
53
+ def read(resource)
54
+ File.read $gluez.locate(resource)
55
+ end
56
+
57
+ # Set a property value
58
+ def set(name, value)
59
+ @properties[name] = value
60
+ end
61
+
62
+ # Get a property value
63
+ def get(name)
64
+ @properties[name]
65
+ end
66
+
67
+ # Includes a recipe. A new context will be created. The passed block will be executed in the scope of the new context.
68
+ def include(name, &block)
69
+ Gluez::Context.new(self, name) do |c|
70
+ c.instance_eval(&block) if block
71
+ load "#{File.dirname($0)}/recipes/#{name}/#{name}.rb"
72
+ end
73
+ end
74
+
75
+ # Loops through all resources, collect their generated code, format and return it.
76
+ def generate(simulate)
77
+ code = "#!/bin/bash"
78
+
79
+ code += "\n" + @resources.collect do |res|
80
+ res.generate(simulate).join("\n")
81
+ end.join("\n")
82
+
83
+ code += "\n" + @resources.select{|r| !r.lazy}.collect do |res|
84
+ res.function_name
85
+ end.join("\n")
86
+
87
+ code = Gluez::format(code)
88
+
89
+ if Gluez.options.include?("--ssh")
90
+ code64 = Base64.encode64(code)
91
+
92
+ cmd = %(code=\\"#{code64.strip}\\" && echo \\\$code | base64 -i -d - | /bin/bash)
93
+ ssh = "ssh -t whale01 \"sudo su -l root -c '#{cmd}'\""
94
+
95
+ puts ssh
96
+ else
97
+ puts code
98
+ end
99
+
100
+ end
101
+
102
+ def self.load_resources
103
+ dirs = [File.dirname($0), File.dirname(__FILE__)].collect{|d| "#{d}/resources"}
104
+
105
+ dirs.each do |dir|
106
+ next unless File.exist?(dir)
107
+ Dir.glob(dir + "/*") do |file|
108
+ require file
109
+ end
110
+ end
111
+ end
112
+
113
+ def self.register(method_name, &block)
114
+ define_method method_name do |name, &block2|
115
+ res = Gluez::Resource.new(self, method_name, name)
116
+
117
+ res.class.class_eval do
118
+ define_method("ready!".to_sym) do
119
+ res.instance_eval(&block2) if block2
120
+ res.validate!
121
+ end
122
+ end
123
+
124
+ res.instance_eval(&block)
125
+
126
+ self.root.resources << res
127
+ end
128
+ end
129
+
130
+ end
131
+ end
@@ -0,0 +1,209 @@
1
+ module Gluez
2
+
3
+ module Resources
4
+ end
5
+
6
+ # Resources are the building blocks of gluez. Each resource has a specification which list the attributes, their default values and the actual code to execute.
7
+ # The code to execute is listed as separate steps. A step consist of a chunk of code and a check, which ensures the chunk of code has been executed correctly.
8
+ # Complex checks can be broken down as multiple simple checks. These will be AND'ed later on.
9
+ #
10
+ # Resources will be executed as root per default. The special attribute user can be set, so that the resource will be executed as this user.
11
+ class Resource
12
+
13
+ # A Step consist of a chunk of code and a check, to ensure the code has been applied successfully.
14
+ class Step
15
+ attr_accessor :checks, :code
16
+ def initialize
17
+ @checks = []
18
+ end
19
+ end
20
+
21
+ # The name of this resource
22
+ attr_reader :name
23
+
24
+ # The type of this resource
25
+ attr_reader :type
26
+
27
+ # A chunk of code which will be executed before any other code defined as steps
28
+ attr_accessor :setup
29
+
30
+ attr_reader :subscriptions
31
+
32
+ def initialize(context, type, name, &block)
33
+ @context = context
34
+ @type = type.to_sym
35
+ @name = name
36
+ @steps = []
37
+
38
+ @notifies = []
39
+ @subscriptions = []
40
+
41
+ @mandatories = []
42
+ @optionals = []
43
+
44
+ self.optional :user, :default => "root"
45
+ self.optional :lazy, :default => false
46
+
47
+ self.accessor :setup
48
+ end
49
+
50
+ def validate!
51
+ @mandatories.each do |it|
52
+ raise "no value for mandatory attribute #{it}" if self.send(it).nil?
53
+ end
54
+ end
55
+
56
+ def mandatory(name, opts={})
57
+ @mandatories << name
58
+ self.accessor(name)
59
+ end
60
+
61
+ def optional(name, opts={})
62
+ @optionals << name
63
+ self.accessor(name, opts[:default])
64
+ end
65
+
66
+ def accessor(name, default=nil)
67
+ self.class.instance_eval do
68
+ define_method(name) do |*args|
69
+ if args.length == 0
70
+ v = instance_variable_get("@#{name}")
71
+ v.nil? ? default : v
72
+ else
73
+ instance_variable_set("@#{name}", args.first)
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ def notify(type, name)
80
+ @notifies << [type, name]
81
+ end
82
+
83
+ def subscribe(type, name)
84
+ @subscriptions << [type, name]
85
+ end
86
+
87
+ def assign(name)
88
+ self.send(name, $gluez.get(name))
89
+ end
90
+
91
+ # Creates a new step for this resource. This method is called within the specification of a resource and should not be called after this phase.
92
+ def steps
93
+ step = Gluez::Resource::Step.new
94
+ yield(step)
95
+
96
+ step.checks.each do |check|
97
+ check.gsub!("\"", "\\\"")
98
+ end
99
+
100
+ @steps << step
101
+ end
102
+
103
+ # Returns this resource as a bash function. The function body contains all the steps checks/codes in the order as specified for this resource.
104
+ # Test if the check part of a step is up2date. If not, execute the code of the step. If it is up2date now, continue with the next step, fail with an error otherwise.
105
+ def generate(simulate)
106
+ g = []
107
+
108
+ fun = self.function_name
109
+
110
+ g << "function #{fun} {"
111
+ g << "su -l #{user} -c \"#{self.setup}\"" if self.setup
112
+
113
+ if @steps.map{|s| s.checks}.flatten.empty?
114
+ unless simulate
115
+ g << "su -l #{user} -c \"#{@steps.map{|s| s.code}.join(' && ')}\""
116
+
117
+ generate_success_or_failure(g, "echo \"[applied] - #{fun}\"") do
118
+ g << "echo \"[not applied] - #{fun}\""
119
+ g << "exit 1"
120
+ end
121
+
122
+ generate_notify_and_subscribe(g)
123
+ end
124
+ else
125
+ generate_steps_checks(g, @steps)
126
+ generate_success_or_failure(g, "echo \"[up2date] - #{fun}\"") do
127
+ g << "echo \"[not up2date] - #{fun}\""
128
+
129
+ unless simulate
130
+ y = @steps.length
131
+ if y > 1
132
+ (0..@steps.length-1).to_a.each do |limit|
133
+ generate_steps_checks(g, @steps[0..limit])
134
+ x = limit + 1
135
+
136
+ generate_success_or_failure(g, "echo \"[up2date] #{x}/#{y} - #{fun}\"") do
137
+ g << "echo \"[not up2date] #{x}/#{y} - #{fun}\""
138
+ g << "su -l #{user} -c \"#{@steps[limit].code}\""
139
+
140
+ generate_success_or_failure(g, "echo \"[applied] #{x}/#{y} - #{fun}\"") do
141
+ g << "echo \"[not applied] #{x}/#{y} - #{fun}\""
142
+ g << "exit 1"
143
+ end
144
+ end
145
+ end
146
+ else
147
+ g << "su -l #{user} -c \"#{@steps.first.code}\""
148
+
149
+ generate_success_or_failure(g, "echo \"[applied] - #{fun}\"") do
150
+ g << "echo \"[not applied] - #{fun}\""
151
+ g << "exit 1"
152
+ end
153
+ end
154
+
155
+ generate_notify_and_subscribe(g)
156
+
157
+ end
158
+ end
159
+ end
160
+
161
+
162
+ g << "}"
163
+ g
164
+ end
165
+
166
+ def generate_notify_and_subscribe(g)
167
+ @notifies.each do |run|
168
+ type, name = run
169
+
170
+ resource = @context.root.resources.detect{|r| r.type == type && r.name == name}
171
+ raise "resource #{type} #{name} does not exist" unless resource
172
+
173
+ g << resource.function_name
174
+ end
175
+
176
+ @context.root.resources.select{|r| r.subscriptions.include?([@type, @name])}.each do |resource|
177
+ g << resource.function_name
178
+ end
179
+ end
180
+
181
+ def generate_steps_checks(g, steps)
182
+ g << steps.map do |step|
183
+ step.checks
184
+ end.flatten.map do |check|
185
+ "su -l #{user} -c \"test #{check}\""
186
+ end.join(" && ")
187
+ end
188
+
189
+ def generate_success_or_failure(g, success)
190
+ g << "if [[ $? -eq 0 ]]; then"
191
+ g << success
192
+ g << "else"
193
+ yield
194
+ g << "fi"
195
+ end
196
+
197
+ # Returns a bash-compatible function name for this resource
198
+ def function_name
199
+ "#{user}_#{self.type}_#{self.name}".
200
+ gsub('/', '_').
201
+ gsub(':', '_').
202
+ gsub("@", "_").
203
+ gsub("~", "_").
204
+ gsub(" ", "_").
205
+ gsub(".", "_")
206
+ end
207
+
208
+ end
209
+ end
@@ -0,0 +1,11 @@
1
+ resource :bash do
2
+ mandatory :code
3
+ mandatory :not_if
4
+
5
+ ready!
6
+
7
+ steps do |step|
8
+ step.checks << self.not_if
9
+ step.code = self.code
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ resource :bash_once do
2
+ mandatory :code
3
+
4
+ ready!
5
+
6
+ steps do |step|
7
+ step.checks << "-f ~/.gluez/bash_once_#{self.function_name}"
8
+ step.code = "#{self.code.strip} && touch ~/.gluez/bash_once_#{self.function_name}"
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ resource :dir do
2
+ optional :chmod, :default => 755
3
+
4
+ ready!
5
+
6
+ steps do |step|
7
+ step.checks << "-d #{self.name}"
8
+ step.code = "mkdir #{self.name}"
9
+ end
10
+ steps do |step|
11
+ step.checks << %Q("\\$(stat -L --format=%a #{self.name})" = "#{self.chmod}")
12
+ step.code = "chmod #{self.chmod} #{self.name}"
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ resource :disable do
2
+ ready!
3
+
4
+ steps do |step|
5
+ step.checks << "\\$(update-rc.d -n -f #{self.name} remove | grep '/etc/rc' | wc -l) -eq 0"
6
+ step.code = "/usr/sbin/update-rc.d -f #{self.name} remove"
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ resource :enable do
2
+ ready!
3
+
4
+ steps do |step|
5
+ step.checks << "\\$(update-rc.d -n -f #{self.name} remove | grep '/etc/rc' | wc -l) -gt 0"
6
+ step.code = "/usr/sbin/update-rc.d #{self.name} defaults"
7
+ end
8
+ end
@@ -0,0 +1,23 @@
1
+ resource :file do
2
+ optional :chmod, :default => 644
3
+ optional :chown
4
+
5
+ ready!
6
+
7
+ steps do |step|
8
+ step.checks << "-f #{self.name}"
9
+ step.code = "touch #{self.name}"
10
+ end
11
+ steps do |step|
12
+ step.checks << %Q("\\$(stat -L --format=%a #{self.name})" = "#{self.chmod}")
13
+ step.code = "chmod #{self.chmod} #{self.name}"
14
+ end
15
+
16
+ if self.chown
17
+ steps do |step|
18
+ step.checks << %Q("\\$(stat -L --format=%U:%G #{self.name})" = "#{self.chown}")
19
+ step.code = "chown #{self.chown} #{self.name}"
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,10 @@
1
+ resource :gem do
2
+ mandatory :version
3
+
4
+ ready!
5
+
6
+ steps do |step|
7
+ step.checks << "\\$(gem list | awk '{print \\$1}' | grep ^#{self.name}$ | wc -l) -eq 1"
8
+ step.code = "gem install #{self.name} --version #{self.version} --user-install --no-rdoc --no-ri"
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ resource :group do
2
+ mandatory :gid
3
+
4
+ ready!
5
+
6
+ steps do |step|
7
+ step.checks << "$(cat /etc/group | grep ^#{self.name}: | wc -l) -eq 1"
8
+ step.code = "groupadd --gid #{self.gid} #{self.name}"
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ resource :link do
2
+ mandatory :target
3
+
4
+ ready!
5
+
6
+ steps do |step|
7
+ step.checks << "-L #{self.name}"
8
+ step.checks << "\\$(file #{self.name} | grep \"#{self.name.gsub('~', '\$HOME')}: symbolic link to \\\\\\`#{self.target.gsub('~', '\$HOME')}'\" | wc -l) -eq 1"
9
+
10
+ step.code = "ln -f -s #{self.target} #{self.name}"
11
+ end
12
+ end
@@ -0,0 +1,19 @@
1
+ resource :mount do
2
+ mandatory :type
3
+ mandatory :options
4
+ mandatory :device
5
+
6
+ optional :options
7
+
8
+ ready!
9
+
10
+ steps do |step|
11
+ step.checks << "\\$(mount | grep \"on #{self.name} type\" | wc -l) -eq 1"
12
+
13
+ cmd = "mount -t #{self.type}"
14
+ cmd = "#{cmd} -o #{self.options}" if self.options
15
+ cmd = "#{cmd} #{self.device} #{self.name}"
16
+
17
+ step.code = cmd
18
+ end
19
+ end
@@ -0,0 +1,8 @@
1
+ resource :package do
2
+ ready!
3
+ steps do |step|
4
+ step.checks << "\\$(apt-cache policy #{self.name} | grep Installed | wc -l) -eq 1"
5
+ step.checks << "\\$(apt-cache policy #{self.name} | grep Installed | grep '(none)' | wc -l) -eq 0"
6
+ step.code = "apt-get install #{self.name} --yes"
7
+ end
8
+ end
@@ -0,0 +1,13 @@
1
+ resource :path do
2
+ ready!
3
+
4
+ steps do |step|
5
+ link = self.name.gsub('/', '_').gsub('~', "/home/#{self.user}/.gluez/path/")
6
+ target = self.name.gsub('~', "/home/#{self.user}")
7
+
8
+ step.checks << "-L #{link}"
9
+ step.checks << "\\$(ls -al #{link} | awk '{print \\$10}' | grep #{target} | wc -l) -eq 1"
10
+
11
+ step.code = "ln -f -s #{self.name} #{link}"
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ resource :restart do
2
+ ready!
3
+ steps do |step|
4
+ step.code = "service #{self.name} restart"
5
+ end
6
+ end
@@ -0,0 +1,22 @@
1
+ resource :source do
2
+
3
+ mandatory :not_if
4
+ mandatory :filename
5
+ mandatory :folder
6
+ mandatory :make
7
+ optional :tmp_dir, :default => "~/tmp"
8
+
9
+ ready!
10
+
11
+ steps do |step|
12
+ step.checks << self.not_if
13
+ step.code = <<-CODE
14
+ [ -f #{self.tmp_dir}/#{self.filename} ] && rm #{self.tmp_dir}/#{self.filename}
15
+ [ -d #{self.tmp_dir}/#{self.folder} ] && rm -rf #{self.tmp_dir}/#{self.folder}
16
+
17
+ wget --no-check-certificate -O #{self.tmp_dir}/#{self.filename} #{self.name}
18
+ tar -C #{self.tmp_dir}/ -x#{self.filename =~ /\.tar\.bz2/ ? "j" : "z"}f #{self.tmp_dir}/#{self.filename}
19
+ cd #{self.tmp_dir}/#{self.folder} && #{self.make}
20
+ CODE
21
+ end
22
+ end
@@ -0,0 +1,10 @@
1
+ resource :start do
2
+ ready!
3
+
4
+ setup = "service --status-all 1>/tmp/gluez.tmp 2>&1"
5
+ steps do |step|
6
+ step.checks << %Q("\\$(grep #{self.name} /tmp/gluez.tmp | wc -l)" = "1")
7
+ step.checks << %Q("\\$(service #{self.name} status | grep -E 'is running|start/running' | wc -l)" = "1")
8
+ step.code = "service #{self.name} start"
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ resource :stop do
2
+ ready!
3
+
4
+ setup = "service --status-all 1>/tmp/gluez.tmp 2>&1"
5
+ steps do |step|
6
+ step.checks << %Q("\\$(grep #{self.name} /tmp/gluez.tmp | wc -l)" = "1")
7
+ step.checks << %Q("\\$(service #{self.name} status | grep -E 'is running|start/running' | wc -l)" = "0")
8
+ step.code = "service #{self.name} stop"
9
+ end
10
+ end
@@ -0,0 +1,51 @@
1
+ require 'erb'
2
+ require 'base64'
3
+
4
+ resource :transfer do
5
+
6
+ mandatory :content
7
+ optional :chmod, :default => 644
8
+
9
+ instance_variable_set("@vars", {})
10
+ def var(name, value)
11
+ @vars[name] = value
12
+ end
13
+
14
+ ready!
15
+
16
+ res = self
17
+ vars = Object.new
18
+
19
+ vars.class.class_eval do
20
+ res.instance_variable_get("@vars").each_pair do |key, val|
21
+ next unless val
22
+ define_method key do
23
+ val
24
+ end
25
+ end
26
+ define_method :get_binding do
27
+ binding
28
+ end
29
+ end
30
+
31
+ data = ERB.new(self.content).result(vars.get_binding)
32
+ base64 = Base64.encode64(data)
33
+
34
+ setup "cat >~/.gluez_transfer <<\\DATA
35
+ #{base64.strip}
36
+ DATA"
37
+
38
+ steps do |step|
39
+ step.checks << "-f #{self.name}"
40
+ step.code = "touch #{self.name}"
41
+ end
42
+ steps do |step|
43
+ step.checks << %Q("\\$(stat -L --format=%a #{self.name})" = "#{self.chmod}")
44
+ step.code = "chmod #{self.chmod} #{self.name}"
45
+ end
46
+ steps do |step|
47
+ step.checks << %Q("\\$(cat ~/.gluez_transfer | base64 -i -d - | md5sum - | awk '{print \\$1}')" = "\\$(md5sum #{self.name} | awk '{print \\$1}')")
48
+ step.code = "chmod +w #{self.name} && cat ~/.gluez_transfer | base64 -i -d - > #{self.name} && chmod #{self.chmod} #{self.name}"
49
+ end
50
+
51
+ end
@@ -0,0 +1,8 @@
1
+ resource :umount do
2
+ ready!
3
+
4
+ steps do |step|
5
+ step.checks << "\\$(mount | grep \"on #{self.name} type\" | wc -l) -eq 0"
6
+ step.code = "umount #{self.name}"
7
+ end
8
+ end
@@ -0,0 +1,11 @@
1
+ resource :user do
2
+ mandatory :uid
3
+ mandatory :gid
4
+
5
+ ready!
6
+
7
+ steps do |step|
8
+ step.checks << "$(cat /etc/passwd | grep ^#{self.name}: | wc -l) -eq 1"
9
+ step.code = "useradd --create-home --uid #{self.uid} --gid #{self.gid} --shell /bin/bash #{self.name}"
10
+ end
11
+ end