servitor 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile +10 -0
  3. data/Gemfile.lock +105 -0
  4. data/LICENSE +19 -0
  5. data/README.md +102 -0
  6. data/lib/cli/cli.rb +96 -0
  7. data/lib/configuration/configuration.rb +2 -0
  8. data/lib/configuration/configuration_resolver.rb +97 -0
  9. data/lib/configuration/yaml_configuration_provider.rb +28 -0
  10. data/lib/control/control.rb +0 -0
  11. data/lib/deployment/bundle_deployer.rb +14 -0
  12. data/lib/deployment/deployment.rb +2 -0
  13. data/lib/deployment/envdir_deployer.rb +34 -0
  14. data/lib/helpers/child_process_helper.rb +41 -0
  15. data/lib/helpers/helpers.rb +1 -0
  16. data/lib/infrastructure/infrastructure.rb +16 -0
  17. data/lib/infrastructure/infrastructure_provisioner.rb +25 -0
  18. data/lib/infrastructure/infrastructure_requirements.rb +25 -0
  19. data/lib/provisioners/Vagrantfile.erb +16 -0
  20. data/lib/provisioners/provisioners.rb +6 -0
  21. data/lib/provisioners/ssh_key.rb +22 -0
  22. data/lib/provisioners/vagrant_box.rb +116 -0
  23. data/lib/provisioners/vagrant_box_provisioner.rb +37 -0
  24. data/lib/provisioners/vagrant_box_ruby_installer.rb +34 -0
  25. data/lib/provisioners/vagrantfile.rb +34 -0
  26. data/lib/provisioners/veewee_box.rb +91 -0
  27. data/lib/resource/mysql_resource.rb +33 -0
  28. data/lib/resource/resource.rb +1 -0
  29. data/lib/service/service.rb +12 -0
  30. data/lib/service/service_definition/Servicefile.erb +54 -0
  31. data/lib/service/service_definition/deployment_stages.rb +16 -0
  32. data/lib/service/service_definition/resource_configuration.rb +14 -0
  33. data/lib/service/service_definition/service_configuration.rb +19 -0
  34. data/lib/service/service_definition/service_definition.rb +49 -0
  35. data/lib/service/service_definition/variable.rb +22 -0
  36. data/lib/service/service_file_parser.rb +21 -0
  37. data/lib/service/service_graph/service_graph.rb +58 -0
  38. data/lib/service/service_graph/service_graph_flattener.rb +34 -0
  39. data/lib/service/service_graph/service_graph_node.rb +15 -0
  40. data/lib/service/service_linker.rb +56 -0
  41. data/lib/service/service_locator.rb +43 -0
  42. data/lib/servitor.rb +47 -0
  43. data/servitor.gemspec +18 -0
  44. data/spec/provisioners/vagrant_basebox_provisioner_spec.rb +63 -0
  45. data/spec/provisioners/vagrant_box_ruby_provisioner_spec.rb +48 -0
  46. data/spec/spec_helper.rb +7 -0
  47. metadata +171 -0
File without changes
@@ -0,0 +1,14 @@
1
+ module Servitor
2
+ class BundleDeployer
3
+ attr_reader :box, :path
4
+
5
+ def initialize(box, path)
6
+ @box = box
7
+ @path = path
8
+ end
9
+
10
+ def deploy
11
+ box.ssh("cd '#{path}'; bundle")
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,2 @@
1
+ servitor_require 'bundle_deployer'
2
+ servitor_require 'envdir_deployer'
@@ -0,0 +1,34 @@
1
+ module Servitor
2
+ class EnvdirDeployer
3
+ include ChildProcessHelper
4
+
5
+ attr_reader :box, :path, :dirname, :variables
6
+
7
+ def initialize(box, service_name, path, dirname, variables)
8
+ @service_name = service_name
9
+ @box = box
10
+ @path = path
11
+ @dirname = dirname
12
+ @variables = variables
13
+ end
14
+
15
+ def deploy
16
+ # envdir is part of daemontools
17
+ box.ssh('which envdir || sudo apt-get install daemontools',
18
+ :vm_name => @service_name, :ignore_exit_code => true, :capture => true)
19
+
20
+ box.ruby(<<-RUBY, :vm_name => @service_name, :sudo => true)
21
+ require 'fileutils'
22
+ envdir = File.join(#{path.inspect}, #{dirname.inspect})
23
+ variables = #{variables.inspect}
24
+ FileUtils.rm_rf(envdir)
25
+ FileUtils.mkdir_p(envdir)
26
+ Dir.chdir(envdir) do
27
+ variables.each do |name, value|
28
+ File.open(name.upcase, 'w') {|f| f.write(value.gsub("\n", 0.chr)) } if value
29
+ end
30
+ end
31
+ RUBY
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,41 @@
1
+ module Servitor
2
+ module ChildProcessHelper
3
+
4
+ def execute_child_process(*args)
5
+ options = args.pop if args.last.is_a? Hash
6
+ options ||= {}
7
+ #puts "Executing: #{args.inspect}"
8
+ process = ChildProcess.build(*args)
9
+ if block_given?
10
+ yield process
11
+ else
12
+ process.io.inherit!
13
+ end
14
+ process.start
15
+ exit_code = process.wait
16
+ unless exit_code == 0 || options[:ignore_exit_code]
17
+ raise ServitorChildProcessError, "#{args.inspect}: exited with code #{exit_code}"
18
+ end
19
+ end
20
+
21
+ def execute_child_process_and_capture_output(*args)
22
+ output = nil
23
+ Tempfile.open('ChildProcessHelper') do |tempfile|
24
+ execute_child_process(*args) do |process|
25
+ process.io.stdout = process.io.stderr = tempfile
26
+ end
27
+ tempfile.rewind
28
+ output = tempfile.read
29
+ end
30
+ output
31
+ end
32
+
33
+ def self.included(base)
34
+ # this can work as a class method too
35
+ base.extend(ChildProcessHelper)
36
+ end
37
+
38
+ end
39
+
40
+ class ServitorChildProcessError < StandardError; end
41
+ end
@@ -0,0 +1 @@
1
+ servitor_require 'child_process_helper'
@@ -0,0 +1,16 @@
1
+ servitor_require 'infrastructure_provisioner'
2
+ servitor_require 'infrastructure_requirements'
3
+
4
+ module Servitor
5
+
6
+ class Infrastructure
7
+ attr_writer :ports
8
+ attr_accessor :deploy_root
9
+
10
+ def ports
11
+ @ports || { }
12
+ end
13
+
14
+ end
15
+
16
+ end
@@ -0,0 +1,25 @@
1
+ module Servitor
2
+
3
+ class InfrastructureProvisioner
4
+
5
+ class << self
6
+
7
+ # Provisions an infrastructure that meets the requirements for the given service config.
8
+ # Returns an infrastructure.
9
+ def provision(requirements)
10
+ VagrantBox.define(requirements) do |box|
11
+ VagrantBoxRubyInstaller.new(requirements).install(box)
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def create_box
18
+
19
+
20
+ end
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,25 @@
1
+ require 'zlib'
2
+
3
+ module Servitor
4
+
5
+ class InfrastructureRequirements
6
+
7
+ %w(os_name os_version os_arch
8
+ chef_repo chef_refspec chef_runlist chef_attributes
9
+ ruby_version).each do |attr|
10
+ define_method(attr) do |*args|
11
+ value = args.length > 0 ? args.first : nil
12
+ attr_sym = "@#{attr}".to_sym
13
+ instance_variable_set(attr_sym, value) unless value.nil?
14
+ instance_variable_get(attr_sym)
15
+ end
16
+ end
17
+
18
+ # Returns a hash of the requirements that should be consistent between processes
19
+ def consistent_hash
20
+ Zlib.crc32(self.to_yaml, 0)
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,16 @@
1
+ Vagrant::Config.run do |config|
2
+ <% @services.each do |service| %>
3
+ config.vm.define <%= service.name.to_sym.inspect %> do |service_config|
4
+ service_config.vm.box = <%= service.box.to_s.inspect %>
5
+ service_config.vm.network :hostonly, <%= service.ip_address.to_s.inspect %>
6
+ <% unless service.vm_root.blank? || service.root.blank? %>
7
+ service_config.vm.share_folder 'v-root', <%= service.vm_root.inspect %>, <%= service.root.inspect %>
8
+ <% end %>
9
+ service_config.vm.share_folder 'ssh-keys', '/mnt/ssh', <%= @ssh_dir.inspect %>
10
+ #service_config.ssh.private_key_path = <%= File.join(@ssh_dir, 'id_rsa').inspect %>
11
+ <% service.forwarded_ports.each do |from, to| %>
12
+ service_config.vm.forward_port <%= from.to_i.inspect %>, <%= to.to_i.inspect %>
13
+ <% end %>
14
+ end
15
+ <% end %>
16
+ end
@@ -0,0 +1,6 @@
1
+ servitor_require 'vagrant_box'
2
+ servitor_require 'veewee_box'
3
+ servitor_require 'vagrant_box_provisioner'
4
+ servitor_require 'vagrant_box_ruby_installer'
5
+ servitor_require 'vagrantfile'
6
+ servitor_require 'ssh_key'
@@ -0,0 +1,22 @@
1
+ module Servitor
2
+ class SshKey
3
+ def initialize(ssh_dir)
4
+ @ssh_dir = ssh_dir
5
+ end
6
+
7
+ def deploy
8
+ FileUtils.mkdir_p(@ssh_dir)
9
+ FileUtils.chown(ENV['USER'], nil, @ssh_dir)
10
+ home_ssh_dir = File.join(ENV['HOME'], '.ssh')
11
+ private_key_file, public_key_file = %w(id_rsa id_rsa.pub).map do |key_file|
12
+ File.join(home_ssh_dir, key_file).tap do |abs_key_file|
13
+ FileUtils.cp(abs_key_file, File.join(@ssh_dir, File.basename(abs_key_file)))
14
+ end
15
+ end
16
+ FileUtils.chmod(0600, private_key_file)
17
+ File.open(File.join(@ssh_dir, 'authorized_keys'), 'w') do |f|
18
+ f.write(File.read(public_key_file))
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,116 @@
1
+ require 'vagrant'
2
+
3
+ module Servitor
4
+
5
+ class VagrantBox
6
+
7
+ include ChildProcessHelper
8
+
9
+ class << self
10
+ def exists?(name)
11
+ !!self.vagrant.boxes.find(name)
12
+ end
13
+
14
+ def vagrant
15
+ @vagrant ||= Vagrant::Environment.new
16
+ end
17
+
18
+ def add(box_name, box_file)
19
+ execute_child_process('vagrant', 'box', 'add', box_name, box_file)
20
+ end
21
+
22
+ def define(requirements)
23
+ name = box_name_for(requirements)
24
+ return name if exists?(name)
25
+ box = VagrantBoxProvisioner.new(requirements).provision
26
+ yield box
27
+ box_file = box.package
28
+ box.destroy
29
+ self.add(name, box_file)
30
+ name
31
+ end
32
+
33
+ def box_file_for(name)
34
+ File.join(Servitor.boxes_root, "#{name}.box")
35
+ end
36
+
37
+ def box_name_for(requirements)
38
+ "servitor-#{requirements.consistent_hash.to_s(32)}"
39
+ end
40
+ end
41
+
42
+ attr_reader :name, :path
43
+
44
+ def initialize(name, path=nil)
45
+ @name = name
46
+ @path = path || File.join(Servitor.boxes_root, @name)
47
+ end
48
+
49
+ def init(base_box)
50
+ box_dir do
51
+ execute_child_process('vagrant', 'init', base_box)
52
+ end
53
+ end
54
+
55
+ def up
56
+ box_dir do
57
+ execute_child_process('vagrant', 'up', '--no-provision')
58
+ end
59
+ end
60
+
61
+ def halt
62
+ box_dir do
63
+ execute_child_process('vagrant', 'halt')
64
+ end
65
+ end
66
+
67
+ def destroy
68
+ box_dir do
69
+ execute_child_process('vagrant', 'destroy', '--force')
70
+ end
71
+ end
72
+
73
+ def package
74
+ box_dir do
75
+ box_file = self.class.box_file_for(@name)
76
+ execute_child_process('vagrant', 'package', '--output', box_file)
77
+ return box_file
78
+ end
79
+ end
80
+
81
+ def ssh(command, options={})
82
+ box_dir do
83
+ sudo = options[:sudo] ? 'sudo ' : ''
84
+ args = ['vagrant', 'ssh', options[:vm_name], '-c', "#{sudo}#{command}"].compact
85
+ if options[:capture]
86
+ execute_child_process_and_capture_output(*args)
87
+ else
88
+ execute_child_process(*args)
89
+ end
90
+ end
91
+ end
92
+
93
+ def ruby(script, options={})
94
+ escaped = script.gsub('"', '\"')
95
+ lines = escaped.split("\n")
96
+ cmd = "ruby #{lines.map {|l| "-e \"#{l}\""}.join(' ') }"
97
+ #puts "cmd = "+ cmd
98
+ ssh(cmd, options)
99
+ end
100
+
101
+ def provision
102
+ box_dir do
103
+ execute_child_process('vagrant', 'provision')
104
+ end
105
+ end
106
+
107
+ private
108
+
109
+ def box_dir(&block)
110
+ FileUtils.mkdir_p(self.path)
111
+ FileUtils.chdir(self.path, {}, &block)
112
+ end
113
+
114
+ end
115
+
116
+ end
@@ -0,0 +1,37 @@
1
+ module Servitor
2
+
3
+ class VagrantBoxProvisioner
4
+ attr_reader :requirements
5
+
6
+ def initialize(requirements)
7
+ @requirements = requirements
8
+ end
9
+
10
+ def name
11
+ @name ||= "#{@requirements.os_name}_#{@requirements.os_version}".gsub(/[^a-zA-Z0-9\-]/, '-')
12
+ end
13
+
14
+ def provision
15
+ create_base_box unless VagrantBox.exists?(name)
16
+ VagrantBox.new(name).tap do |box|
17
+ box.init(name)
18
+ box.up
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def create_base_box
25
+ base_box = VeeweeBox.new(name, @requirements.os_name, @requirements.os_version)
26
+ base_box.define
27
+ base_box.build(:nogui => true)
28
+ base_box.validate
29
+ base_box.export
30
+ base_box.up
31
+ box_file = "#{name}.box"
32
+ VagrantBox.add(name, box_file)
33
+ FileUtils.rm box_file
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,34 @@
1
+ module Servitor
2
+
3
+ class VagrantBoxRubyInstaller
4
+ attr_reader :requirements
5
+
6
+ def initialize(requirements)
7
+ @requirements = requirements
8
+ end
9
+
10
+ def install(box)
11
+ return if already_installed?(box)
12
+ box.ssh('which curl || sudo apt-get install curl -y')
13
+ case @requirements.ruby_version
14
+ when /jruby/
15
+ box.ssh('sudo apt-get install g++ openjdk-6-jre-headless -y')
16
+ when /ironruby/
17
+ box.ssh('sudo apt-get install mono-2.0-devel -y')
18
+ else
19
+ box.ssh('sudo apt-get install build-essential openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev ncurses-dev automake libtool bison subversion pkg-config -y')
20
+ end
21
+ box.ssh('which rvm || curl -L https://get.rvm.io | bash -s stable', :ignore_exit_code => true)
22
+ box.ssh('which curl || sudo apt-get install curl -y')
23
+ box.ssh("rvm install #{@requirements.ruby_version}", :ignore_exit_code => true)
24
+ box.ssh("rvm use #{@requirements.ruby_version} --default")
25
+ end
26
+
27
+ private
28
+
29
+ def already_installed?(box)
30
+ box.ssh('rvm current', :capture => true, :ignore_exit_code => true) == @requirements.ruby_version
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,34 @@
1
+ require 'erubis'
2
+
3
+ class String
4
+ unless method_defined?(:blank?)
5
+ def blank?
6
+ self =~ /^\s*$/
7
+ end
8
+ end
9
+ end
10
+
11
+ class NilClass
12
+ unless method_defined?(:blank?)
13
+ def blank?
14
+ true
15
+ end
16
+ end
17
+ end
18
+
19
+ module Servitor
20
+
21
+ class Vagrantfile
22
+ def initialize(services, ssh_dir)
23
+ @services = services
24
+ @ssh_dir = ssh_dir
25
+ end
26
+
27
+ def generate
28
+ template_path = File.join(File.dirname(__FILE__), 'Vagrantfile.erb')
29
+ eruby = Erubis::Eruby.load_file(template_path)
30
+ context = Erubis::Context.new(:services => @services, :ssh_dir => @ssh_dir)
31
+ eruby.evaluate(context)
32
+ end
33
+ end
34
+ end