lib/kuzushi.rb in kuzushi-0.0.35 vs lib/kuzushi.rb in kuzushi-0.0.36
- old
+ new
@@ -11,350 +11,383 @@
## ruby 1.9 compatibility
## nested configs
## user configs
class Kuzushi
- attr_accessor :config
+ attr_accessor :config
- def initialize(url)
- @url = url
- @base_url = File.dirname(url)
- if @url =~ /s3.amazonaws.com.*\/([^\/]*)[.](\d+)[.]tar[.]gz/
- @name = $1
- @version = $2
- end
- @configs = []
- @packages = []
- @tasks = []
- end
+ def initialize(url)
+ @url = url
+ @base_url = File.dirname(url)
+ if @url =~ /s3.amazonaws.com.*\/([^\/]*)[.](\d+)[.]tar[.]gz/
+ @name = $1
+ @version = $2
+ end
+ @configs = []
+ @packages = []
+ @tasks = []
+ end
- def init
- @init = true
- start
- end
+ def init
+ @init = true
+ start
+ end
- def boot
- shell "mkdir -p /tmp/kuzushi/"
- shell "cd /tmp/kuzushi/ ; curl --silent '#{@url}' | tar xzv"
- @config = JSON.parse(File.read("/tmp/kuzushi/#{@name}/config.json"))
- end
+ def boot
+ shell "mkdir -p /tmp/kuzushi/"
+ shell "cd /tmp/kuzushi/ ; curl --silent '#{@url}' | tar xzv"
+ @config = JSON.parse(File.read("/tmp/kuzushi/#{@name}/config.json"))
+ end
- def start
-# load_config_stack(@name)
- boot
- run
- end
+ def start
+# load_config_stack(@name)
+ boot
+ run
+ end
- def run
- process_stack
- log "----"
- @tasks.each do |t|
- log "TASK: #{t[:description]}"
- t[:blk].call
- end
- log "----"
- end
+ def run
+ process_stack
+ log "----"
+ @tasks.each do |t|
+ log "TASK: #{t[:description]}"
+ t[:blk].call
+ end
+ log "----"
+ end
- protected
+ protected
- def system
- ohai = Ohai::System.new
- ohai.all_plugins
- ohai
- end
+ def system
+ ohai = Ohai::System.new
+ ohai.all_plugins
+ ohai
+ end
- def http_get(url)
- RestClient.get(url)
- end
+ def http_get(url)
+ RestClient.get(url)
+ end
- def process_stack
- script get("before")
+ def process_stack
+ script get("before")
- process :packages
- process :local_packages
- process :gems
- process :volumes
- process :files
- process :users
- process :crontab
+ process :packages
+ process :local_packages
+ process :gems
+ process :volumes
+ process :files
+ process :services
+ process :users
+ process :crontab
- script get("after")
- script get("init") if init?
- end
+ script get("after")
+ script get("init") if init?
+ end
- ## magic goes here
- def process(type)
- ## if the file takes no args - just call it once
- if method("process_#{type}").arity == 0
- send("process_#{type}")
- else
- ## else call it once per item
- get_array(type).each do |item|
- script item["before"]
- if item.is_a? Hash
- send("process_#{type}", OpenStruct.new(item))
- else
- send("process_#{type}", item)
- end
- script item["after"]
- script item["init"] if init?
- end
- end
- end
+ ## magic goes here
+ def process(type)
+ ## if the file takes no args - just call it once
+ if method("process_#{type}").arity == 0
+ send("process_#{type}")
+ else
+ ## else call it once per item
+ get_array(type).each do |item|
+ script item["before"]
+ if item.is_a? Hash
+ send("process_#{type}", OpenStruct.new(item))
+ else
+ send("process_#{type}", item)
+ end
+ script item["after"]
+ script item["init"] if init?
+ end
+ end
+ end
- def process_packages
- @packages = get_array("packages")
- task "install packages" do
- shell "apt-get install -y #{@packages.join(" ")}" unless @packages.empty?
- end
- end
+ def service_file(service)
+ file = []
+ file << "start on stopped rc RUNLEVEL=[2345]"
+ file << "stop on runlevel [!2345]"
+ file << "respawn" unless service.respawn == false
+ if service.user
+ file << "exec su -c '#{service.command}' #{service.user}" ## TODO add shell escaping here
+ else
+ file << "exec #{service.command}"
+ end
+ end
- def process_local_packages(p)
- package(p) do |file|
- task "install local package #{p}" do
- shell "dpkg -i #{file}"
- end
- end
- end
+ def process_service(service)
+ put_file(service_file(service), "/etc/init/#{service.name}.conf")
+ shell "service #{service.name} start"
+ end
- def process_gems(gem)
- task "install gem #{gem}" do
- shell "gem install #{gem} --no-rdoc --no-ri"
- end
- end
+ def process_packages
+ @packages = get_array("packages")
+ task "install packages" do
+ shell "apt-get install -y #{@packages.join(" ")}" unless @packages.empty?
+ end
+ end
- def process_volumes(v)
- handle_ebs v if v.media == "ebs"
- handle_raid v if v.media == "raid"
- set_readahead v if v.readahead
- set_scheduler v if v.scheduler
- handle_format v if v.format
- handle_mount v if v.mount
- end
+ def process_local_packages(p)
+ package(p) do |file|
+ task "install local package #{p}" do
+ shell "dpkg -i #{file}"
+ end
+ end
+ end
- def handle_ebs(v)
- task "wait for volume #{v.device}" do
- wait_for_volume v.device
- end
- end
+ def process_gems(gem)
+ task "install gem #{gem}" do
+ shell "gem install #{gem} --no-rdoc --no-ri"
+ end
+ end
- def handle_raid(r)
- task "create raid #{r.device}", :init => true do
- shell "mdadm --create #{r.device} -n #{r.drives.size} -l #{r.level} -c #{r.chunksize || 64} #{r.drives.join(" ")}"
- end
- task "assemble raid #{r.device}" do ## assemble fails a lot with device busy - is udev to blame :(
- if not dev_exists? r.device
- shell "service stop udev"
- shell "mdadm --assemble #{r.device} #{r.drives.join(" ")}"
- shell "service start udev"
- end
- end
- add_package "mdadm"
- end
+ def process_volumes(v)
+ handle_ebs v if v.media == "ebs"
+ handle_raid v if v.media == "raid"
+ set_readahead v if v.readahead
+ set_scheduler v if v.scheduler
+ handle_format v if v.format
+ handle_mount v if v.mount
+ end
- def mount_options(m)
- o = []
- o << m.options if m.options
- o << "size=#{m.size}M" if m.size and m.media == "tmpfs"
- o << "mode=#{m.mode}" if m.mode
- o << "noatime" if o.empty?
- o.join(",")
- end
+ def handle_ebs(v)
+ task "wait for volume #{v.device}" do
+ wait_for_volume v.device
+ end
+ end
- def handle_mount(m)
- task "mount #{m.mount}" do
- unless mounted?(m.mount)
- shell "mv #{m.mount} #{m.mout}.old" if File.exists?(m.mount)
- shell "mkdir -p #{m.mount} && mount -o #{mount_options(m)} -t #{m.format || m.media} #{m.device || m.media} #{m.mount}"
- shell "chown -R #{m.user}:#{m.group} #{m.mount}" if m.user or m.group
- end
- end
- end
+ def handle_raid(r)
+ task "create raid #{r.device}", :init => true do
+ shell "mdadm --create #{r.device} -n #{r.drives.size} -l #{r.level} -c #{r.chunksize || 64} #{r.drives.join(" ")}"
+ end
+ task "assemble raid #{r.device}" do ## assemble fails a lot with device busy - is udev to blame :(
+ if not dev_exists? r.device
+ shell "service stop udev"
+ shell "mdadm --assemble #{r.device} #{r.drives.join(" ")}"
+ shell "service start udev"
+ end
+ end
+ add_package "mdadm"
+ end
- def system_arch
- system.kernel["machine"]
- end
+ def mount_options(m)
+ o = []
+ o << m.options if m.options
+ o << "size=#{m.size}M" if m.size and m.media == "tmpfs"
+ o << "mode=#{m.mode}" if m.mode
+ o << "noatime" if o.empty?
+ o.join(",")
+ end
- def mounted?(mount)
- ## cant use ohai here b/c it mashes drives together with none or tmpfs devices
- mount = mount.chop if mount =~ /\/$/
- !!(File.read("/proc/mounts") =~ / #{mount} /)
- end
+ def handle_mount(m)
+ task "mount #{m.mount}" do
+ unless mounted?(m.mount)
+ shell "mv #{m.mount} #{m.mout}.old" if File.exists?(m.mount)
+ shell "mkdir -p #{m.mount} && mount -o #{mount_options(m)} -t #{m.format || m.media} #{m.device || m.media} #{m.mount}"
+ shell "chown -R #{m.user}:#{m.group} #{m.mount}" if m.user or m.group
+ end
+ end
+ end
- def package_arch
- `dpkg --print-architecture`.chomp
- end
+ def system_arch
+ system.kernel["machine"]
+ end
- def erb(data)
- @system = system
- ERB.new(data, 0, '<>').result(binding)
- end
+ def mounted?(mount)
+ ## cant use ohai here b/c it mashes drives together with none or tmpfs devices
+ mount = mount.chop if mount =~ /\/$/
+ !!(File.read("/proc/mounts") =~ / #{mount} /)
+ end
- def process_files(f)
- file(f) do |tmp|
- task "write #{f.file}" do
- cp_file(tmp, f.file)
- shell "chmod #{f.mode} #{f.file}" if f.mode
- shell "chown #{f.user} #{f.file}" if f.user
- shell "chgrp #{f.group} #{f.file}" if f.group
- end
- end
- end
+ def package_arch
+ `dpkg --print-architecture`.chomp
+ end
- def process_crontab(cron)
- user = cron.user || "root"
- file(cron) do |tmp|
- task "process crontab for #{user}" do
- shell "crontab -u #{user} #{tmp}"
- end
- end
- end
+ def erb(data)
+ @system = system
+ ERB.new(data, 0, '<>').result(binding)
+ end
- def process_users(user)
- (user.authorized_keys || []).each do |key|
- task "add authorized_key for user #{user.name}" do
- shell "su - #{user.name} -c 'mkdir -p .ssh; echo \"#{key}\" >> .ssh/authorized_keys; chmod -R 0600 .ssh'"
- end
- end
- end
+ def process_files(f)
+ file(f) do |tmp|
+ task "write #{f.file}" do
+ cp_file(tmp, f.file)
+ shell "chmod #{f.mode} #{f.file}" if f.mode
+ shell "chown #{f.user} #{f.file}" if f.user
+ shell "chgrp #{f.group} #{f.file}" if f.group
+ end
+ end
+ end
- def set_readahead(v)
- task "set readahead for #{v.device}" do
- shell "blockdev --setra #{v.readahead} #{v.device}"
- end
- end
+ def process_crontab(cron)
+ user = cron.user || "root"
+ file(cron) do |tmp|
+ task "process crontab for #{user}" do
+ shell "crontab -u #{user} #{tmp}"
+ end
+ end
+ end
- def set_scheduler(v)
- task "set scheduler for #{v.device}" do
- shell "echo #{v.scheduler} > /sys/block/#{File.basename(v.device)}/queue/scheduler"
- end
- end
+ def process_users(user)
+ shell "useradd -m #{user}" ## this will just fail for users like 'root'
+ (user.authorized_keys || []).each do |key|
+ task "add authorized_key for user #{user.name}" do
+ shell "su - #{user.name} -c 'mkdir -p .ssh; echo \"#{key}\" >> .ssh/authorized_keys; chmod -R 0600 .ssh'"
+ end
+ end
+ end
- def handle_format(v)
- return if v.format == "tmpfs"
- task "formatting #{v.device}", :init => true do
- label = "-L " + v.label rescue ""
- shell "mkfs.#{v.format} -q #{label} #{v.device}" unless v.mount && mounted?(v.mount)
- end
- add_package "xfsprogs" if v.format == "xfs"
- end
+ def set_readahead(v)
+ task "set readahead for #{v.device}" do
+ shell "blockdev --setra #{v.readahead} #{v.device}"
+ end
+ end
- def add_package(p)
- @packages << p unless @packages.include? p
- end
+ def set_scheduler(v)
+ task "set scheduler for #{v.device}" do
+ shell "echo #{v.scheduler} > /sys/block/#{File.basename(v.device)}/queue/scheduler"
+ end
+ end
- def package(p, &block)
- fetch("/packages/#{p}_#{package_arch}.deb") do |file|
- block.call(file)
- end
- end
+ def handle_format(v)
+ return if v.format == "tmpfs"
+ task "formatting #{v.device}", :init => true do
+ label = "-L " + v.label rescue ""
+ shell "mkfs.#{v.format} -q #{label} #{v.device}" unless v.mount && mounted?(v.mount)
+ end
+ add_package "xfsprogs" if v.format == "xfs"
+ end
+ def add_package(p)
+ @packages << p unless @packages.include? p
+ end
- def script(scripts)
- to_array(scripts).each do |s|
- if s =~ /^#!/
- inline_script(s)
- else
- external_script(s)
- end
- end
- end
+ def package(p, &block)
+ fetch("/packages/#{p}_#{package_arch}.deb") do |file|
+ block.call(file)
+ end
+ end
- def inline_script(script)
- tmpfile(script) do |tmp|
- task "run inline script" do
- shell "#{tmp}"
- end
- end
- end
- def external_script(script)
- fetch("/scripts/#{script}") do |file|
- task "run script #{script}" do
- shell "chmod +x #{file} ; #{file}"
- end
- end
- end
+ def script(scripts)
+ to_array(scripts).each do |s|
+ if s =~ /^#!/
+ inline_script(s)
+ else
+ external_script(s)
+ end
+ end
+ end
- def tmpfile(content, file = "tmp_#{rand(1_000_000_000)}", &block)
- path = "/tmp/kuzushi/#{File.basename(file)}"
- put_file(content, path)
- block.call(path) if block
- path
- end
+ def inline_script(script)
+ tmpfile(script) do |tmp|
+ task "run inline script" do
+ shell "#{tmp}"
+ end
+ end
+ end
- def file(f, &blk)
- ## no magic here - move along
- fetch("/templates/#{f.template}", lambda { |data| erb data }, &blk) if f.template
- fetch("/files/#{f.source || File.basename(f.file)}", &blk) unless f.template
- end
+ def external_script(script)
+ fetch("/scripts/#{script}") do |file|
+ task "run script #{script}" do
+ shell "chmod +x #{file} ; #{file}"
+ end
+ end
+ end
- ### this needs to be brought up to date - way last version - no need to read and filter...
- def fetch(file, filter = lambda { |d| d }, &block)
- begin
- tmpfile(filter.call(File.read("/tmp/kuzushi/#{@name}/#{file}")), file) do |tmp|
- block.call(tmp)
- end
- rescue Object => e
- error("error fetching file: #{file} : #{e.message}")
- end
- end
+ def tmpfile(content, file = "tmp_#{rand(1_000_000_000)}", &block)
+ path = "/tmp/kuzushi/#{File.basename(file)}"
+ put_file(content, path)
+ block.call(path) if block
+ path
+ end
- def error(message)
- puts "ERROR :#{message}"
- end
+ def file(f, &blk)
+ ## no magic here - move along
+ if f.template
+ fetch("/templates/#{f.template}", lambda { |data| erb data }, &blk)
+ else f.git
+ git_fetch(f)
+ else
+ fetch("/files/#{f.source || File.basename(f.file)}", &blk) unless f.template
+ end
+ end
- def get(key)
- config[key.to_s]
- end
+ def git_fetch(f)
+ FileUtils.mkdir_p(f.dir)
+ shell "cd dir && git init"
+ shell "cd dir && git remote add origin #{f.git}"
+ shell "cd dir && git fetch"
+ shell "cd dir && git checkout master"
+ shell "chown -R #{f.user}:#{f.group} #{f.dir}" if f.user || f.group ## is this needed? handled above in files?
+ end
- def get_array(key)
- to_array( get(key) )
- end
+ ### this needs to be brought up to date - way last version - no need to read and filter...
+ def fetch(file, filter = lambda { |d| d }, &block)
+ begin
+ tmpfile(filter.call(File.read("/tmp/kuzushi/#{@name}/#{file}")), file) do |tmp|
+ block.call(tmp)
+ end
+ rescue Object => e
+ error("error fetching file: #{file} : #{e.message}")
+ end
+ end
- def to_array(value)
- [ value || [] ].flatten
- end
+ def error(message)
+ puts "ERROR :#{message}"
+ end
- def wait_for_volume(vol)
- ## Maybe use ohai here instead -- FIXME
- until dev_exists? vol do
- log "waiting for volume #{vol}"
- sleep 2
- end
- end
+ def get(key)
+ config[key.to_s]
+ end
- def shell(cmd)
- log "# #{cmd}"
- Kernel.system cmd ## FIXME - need to handle/report exceptions here
- end
+ def get_array(key)
+ to_array( get(key) )
+ end
- def init?
- @init ||= false
- end
+ def to_array(value)
+ [ value || [] ].flatten
+ end
- def task(description, options = {}, &blk)
- return if options[:init] and not init?
- @tasks << { :description => description, :blk => blk }
- end
+ def wait_for_volume(vol)
+ ## Maybe use ohai here instead -- FIXME
+ until dev_exists? vol do
+ log "waiting for volume #{vol}"
+ sleep 2
+ end
+ end
- def dev_exists?(dev)
- File.exists?("/sys/block/#{File.basename(dev)}")
- end
+ def shell(cmd)
+ log "# #{cmd}"
+ Kernel.system cmd ## FIXME - need to handle/report exceptions here
+ end
- def cp_file(src, dest)
- FileUtils.mkdir_p(File.dirname(dest))
- FileUtils.cp(src, dest)
- end
+ def init?
+ @init ||= false
+ end
- def put_file(data, dest)
- FileUtils.mkdir_p(File.dirname(dest))
- File.open(dest,"w") do |f|
- f.write(data)
- f.chmod(0700)
- end
- end
+ def task(description, options = {}, &blk)
+ return if options[:init] and not init?
+ @tasks << { :description => description, :blk => blk }
+ end
- def log(message)
- puts message
- end
+ def dev_exists?(dev)
+ File.exists?("/sys/block/#{File.basename(dev)}")
+ end
+
+ def cp_file(src, dest)
+ FileUtils.mkdir_p(File.dirname(dest))
+ FileUtils.cp(src, dest)
+ end
+
+ def put_file(data, dest)
+ FileUtils.mkdir_p(File.dirname(dest))
+ File.open(dest,"w") do |f|
+ f.write(data)
+ f.chmod(0700)
+ end
+ end
+
+ def log(message)
+ puts message
+ end
end