kuzushi 0.0.1 → 0.0.2
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.
- data/README +6 -6
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/bin/kuzushi +8 -1
- data/lib/kuzushi.rb +221 -0
- metadata +12 -2
data/README
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
|
2
|
-
Kuzushi (
|
3
|
-
|
2
|
+
Kuzushi ( ka ZU she ) - a technique used by martial artists to control of an
|
3
|
+
opponent by keeping them slightly off balance.
|
4
4
|
|
5
|
-
The 'kuzushi' is a companion
|
6
|
-
management of a server based on its specificatoion.
|
7
|
-
management from inside the server by installing packages,
|
8
|
-
users, writing config files, etc
|
5
|
+
The 'kuzushi' gem is a companion to the 'sumo' gem. Sumo handles
|
6
|
+
AWS/instance/volume management of a server based on its specificatoion.
|
7
|
+
Kuzushi handles the management from inside the server by installing packages,
|
8
|
+
setting up drives and users, writing config files, etc
|
9
9
|
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2
|
data/bin/kuzushi
CHANGED
data/lib/kuzushi.rb
CHANGED
@@ -1,4 +1,225 @@
|
|
1
|
+
require 'rubygems'
|
1
2
|
require 'json'
|
3
|
+
require 'restclient'
|
4
|
+
require 'ostruct'
|
2
5
|
|
3
6
|
class Kuzushi
|
7
|
+
def initialize(url)
|
8
|
+
@base_url = File.dirname(url)
|
9
|
+
@name = File.basename(url)
|
10
|
+
@config_names = []
|
11
|
+
@config = []
|
12
|
+
@packages = []
|
13
|
+
@tasks = []
|
14
|
+
load_config_stack(@name)
|
15
|
+
@merge = @config.reverse.inject({}) { |i,c| i.merge(c) }
|
16
|
+
process_stack
|
17
|
+
end
|
18
|
+
|
19
|
+
def load_config_stack(name)
|
20
|
+
@config_names << name
|
21
|
+
@config << JSON.parse(RestClient.get("#{@base_url}/#{name}"))
|
22
|
+
if import = @config.last["import"]
|
23
|
+
load_config_stack(import)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def process_stack
|
28
|
+
script get("before")
|
29
|
+
|
30
|
+
process :packages
|
31
|
+
process :local_packages
|
32
|
+
process :gems
|
33
|
+
process :volumes
|
34
|
+
process :raids
|
35
|
+
process :mounts
|
36
|
+
process :files
|
37
|
+
process :users
|
38
|
+
|
39
|
+
script get("after")
|
40
|
+
end
|
41
|
+
|
42
|
+
## magic goes here
|
43
|
+
def process(type)
|
44
|
+
## if the file takes no args - just call it once
|
45
|
+
if method("process_#{type}").arity == 0
|
46
|
+
send("process_#{type}")
|
47
|
+
else
|
48
|
+
## else call it once per item
|
49
|
+
get_array(type).each do |item|
|
50
|
+
script item["before"]
|
51
|
+
if item.is_a? Hash
|
52
|
+
send("process_#{type}", OpenStruct.new(item))
|
53
|
+
else
|
54
|
+
send("process_#{type}", item)
|
55
|
+
end
|
56
|
+
script item["after"]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def process_packages
|
62
|
+
@packages = get_array("packages")
|
63
|
+
task "install packages" do
|
64
|
+
shell "apt-get update && apt-get upgrade -y && apt-get install -y #{@packages.join(" ")}", "DEBIAN_FRONTEND" => "noninteractive", "DEBIAN_PRIORITY" => "critical"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def process_local_packages(p)
|
69
|
+
package(p) do |file|
|
70
|
+
task "install local package #{p}" do
|
71
|
+
shell "dpkg -i #{file}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def process_gems(gem)
|
77
|
+
task "install gem #{gem}" do
|
78
|
+
shell "gem install #{gem} --no-rdoc --no-ri"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def process_volumes(v)
|
83
|
+
task "wait for volume #{v.device}" do
|
84
|
+
wait_for_volume v.device
|
85
|
+
end
|
86
|
+
set_scheduler(v)
|
87
|
+
check_format(v)
|
88
|
+
end
|
89
|
+
|
90
|
+
def process_raids(r)
|
91
|
+
task "assemble raid #{r.device}" do
|
92
|
+
shell "mdadm --assemble #{r.device} #{r.drives.join(" ")}"
|
93
|
+
end
|
94
|
+
set_scheduler r
|
95
|
+
check_format r
|
96
|
+
add_package "mdadm"
|
97
|
+
end
|
98
|
+
|
99
|
+
def process_mounts(m)
|
100
|
+
task "mount #{m.label}" do
|
101
|
+
shell "mount -o #{m.options} -L #{m.label} #{m.label}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def process_files(f)
|
106
|
+
fetch("/templates/#{f.template}") do |file|
|
107
|
+
task "setting up #{f.file}" do
|
108
|
+
shell "erb #{f.template} > #{f.file}" ## FIXME
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def process_users(user)
|
114
|
+
(user.authorized_keys || []).each do |key|
|
115
|
+
task "add authorized_key for user #{user.name}" do
|
116
|
+
shell "su - #{user.name} -c 'mkdir -p .ssh; echo \"#{key}\" >> .ssh/authorized_keys; chmod -R 600 .ssh'"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def set_scheduler(v)
|
122
|
+
if v.scheduler
|
123
|
+
task "set scheduler for #{v.device}" do
|
124
|
+
shell "echo #{v.scheduler} > /sys/block/#{File.basename(v.device)}/queue/scheduler"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def check_format(v)
|
130
|
+
add_package "xfsprogs" if v.format == "xfs"
|
131
|
+
end
|
132
|
+
|
133
|
+
def add_package(p)
|
134
|
+
@packages << p unless @packages.include? p
|
135
|
+
end
|
136
|
+
|
137
|
+
def package(p, &block)
|
138
|
+
fetch("/packages/#{p}_i386.deb") do |file|
|
139
|
+
block.call(file)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def inline_script(script)
|
144
|
+
tmpfile(script) do |tmp|
|
145
|
+
task "run inline script" do
|
146
|
+
shell "#{tmp}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def script(script)
|
152
|
+
return if script.nil?
|
153
|
+
return inline_script(script) if script =~ /^#!/
|
154
|
+
|
155
|
+
fetch("/scripts/#{script}") do |file|
|
156
|
+
task "run script #{script}" do
|
157
|
+
shell "#{file}"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def tmpfile(content, file = "tmp_#{rand(1_000_000_000)}", &block)
|
163
|
+
tmp_dir = "/tmp/kuzushi"
|
164
|
+
Dir.mkdir(tmp_dir) unless File.exists?(tmp_dir)
|
165
|
+
file = "#{tmp_dir}/#{File.basename(file)}"
|
166
|
+
File.open(file,"w") do |f|
|
167
|
+
f.write(content)
|
168
|
+
f.chmod(700)
|
169
|
+
end if content
|
170
|
+
block.call(file) if block
|
171
|
+
file
|
172
|
+
end
|
173
|
+
|
174
|
+
def fetch(file, &block)
|
175
|
+
names = @config_names.clone
|
176
|
+
begin
|
177
|
+
tmpfile RestClient.get("#{@base_url}/#{names.first}#{file}"), file do |tmp|
|
178
|
+
block.call(tmp)
|
179
|
+
end
|
180
|
+
rescue RestClient::ResourceNotFound
|
181
|
+
names.shift
|
182
|
+
retry unless names.empty?
|
183
|
+
error("file not found: #{file}")
|
184
|
+
rescue Object => e
|
185
|
+
error("error fetching file: #{names.first}/#{file}", e)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def error(message, exception = nil)
|
190
|
+
puts "ERROR :#{message}"
|
191
|
+
end
|
192
|
+
|
193
|
+
def get(key)
|
194
|
+
@merge[key.to_s]
|
195
|
+
end
|
196
|
+
|
197
|
+
def get_array(key)
|
198
|
+
[ get(key) || [] ].flatten
|
199
|
+
end
|
200
|
+
|
201
|
+
def wait_for_volume(vol)
|
202
|
+
puts "waiting for volume #{vol}"
|
203
|
+
end
|
204
|
+
|
205
|
+
def start
|
206
|
+
puts "----"
|
207
|
+
@tasks.each do |t|
|
208
|
+
puts "TASK: #{t[:description]}"
|
209
|
+
t[:blk].call
|
210
|
+
end
|
211
|
+
puts "----"
|
212
|
+
end
|
213
|
+
|
214
|
+
def shell(cmd, env = {})
|
215
|
+
puts " SHELL: #{cmd}"
|
216
|
+
end
|
217
|
+
|
218
|
+
def task(description, &blk)
|
219
|
+
@tasks << { :description => description, :blk => blk }
|
220
|
+
end
|
221
|
+
|
222
|
+
def path
|
223
|
+
Dir["**/config.json"]
|
224
|
+
end
|
4
225
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kuzushi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Orion Henry
|
@@ -9,9 +9,19 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-02-
|
12
|
+
date: 2010-02-19 00:00:00 -05:00
|
13
13
|
default_executable: kuzushi
|
14
14
|
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rest-client
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
15
25
|
- !ruby/object:Gem::Dependency
|
16
26
|
name: json
|
17
27
|
type: :runtime
|