gluez 0.2.1 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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