servitor 0.0.1

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.
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