engineyard-local 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/.gitignore +23 -0
  2. data/Gemfile +15 -0
  3. data/Gemfile.lock +62 -0
  4. data/README.md +115 -0
  5. data/Rakefile +15 -0
  6. data/bin/ey-local +4 -0
  7. data/config/dna.json +206 -0
  8. data/config/locales/en.yml +57 -0
  9. data/config/settings.yml +18 -0
  10. data/config/solo.rb +7 -0
  11. data/engineyard-local.gemspec +24 -0
  12. data/install/deb/README.md +72 -0
  13. data/install/deb/Rakefile +157 -0
  14. data/install/deb/Vagrantfile +10 -0
  15. data/install/deb/install.sh +32 -0
  16. data/install/osx/README.md +23 -0
  17. data/install/osx/Rakefile +39 -0
  18. data/install/osx/engineyard-local/engineyard-local.pkgproj +812 -0
  19. data/install/osx/images/eylocal_installer.png +0 -0
  20. data/install/osx/scripts/log.sh +3 -0
  21. data/install/osx/scripts/postinstall +57 -0
  22. data/install/osx/scripts/rvm_install.sh +34 -0
  23. data/lib/engineyard-local.rb +42 -0
  24. data/lib/engineyard-local/command.rb +65 -0
  25. data/lib/engineyard-local/command/base.rb +15 -0
  26. data/lib/engineyard-local/command/exec.rb +11 -0
  27. data/lib/engineyard-local/command/group.rb +86 -0
  28. data/lib/engineyard-local/command/helpers.rb +23 -0
  29. data/lib/engineyard-local/command/list.rb +29 -0
  30. data/lib/engineyard-local/command/rails.rb +19 -0
  31. data/lib/engineyard-local/command/up.rb +87 -0
  32. data/lib/engineyard-local/command/vagrant_action.rb +11 -0
  33. data/lib/engineyard-local/errors.rb +10 -0
  34. data/lib/engineyard-local/middleware.rb +28 -0
  35. data/lib/engineyard-local/middleware/bundle.rb +40 -0
  36. data/lib/engineyard-local/middleware/chef.rb +44 -0
  37. data/lib/engineyard-local/middleware/default_provisioner.rb +34 -0
  38. data/lib/engineyard-local/middleware/dna.rb +80 -0
  39. data/lib/engineyard-local/middleware/exec.rb +27 -0
  40. data/lib/engineyard-local/middleware/helpers.rb +4 -0
  41. data/lib/engineyard-local/middleware/helpers/executable.rb +27 -0
  42. data/lib/engineyard-local/middleware/helpers/network.rb +20 -0
  43. data/lib/engineyard-local/middleware/helpers/rvm.rb +37 -0
  44. data/lib/engineyard-local/middleware/helpers/uploadable.rb +14 -0
  45. data/lib/engineyard-local/middleware/network.rb +64 -0
  46. data/lib/engineyard-local/middleware/rails.rb +3 -0
  47. data/lib/engineyard-local/middleware/rails/command.rb +31 -0
  48. data/lib/engineyard-local/middleware/rails/db.rb +36 -0
  49. data/lib/engineyard-local/middleware/rails/install.rb +36 -0
  50. data/lib/engineyard-local/middleware/rails/new.rb +31 -0
  51. data/lib/engineyard-local/middleware/tag.rb +33 -0
  52. data/lib/engineyard-local/ui.rb +33 -0
  53. data/lib/engineyard-local/version.rb +5 -0
  54. data/lib/engineyard-local/virtualbox.rb +35 -0
  55. data/lib/vagrant_init.rb +1 -0
  56. data/test/engineyard-local/command/group_test.rb +34 -0
  57. data/test/engineyard-local/command/up_test.rb +70 -0
  58. data/test/engineyard-local/command_test.rb +40 -0
  59. data/test/engineyard-local/middelware/bundle_test.rb +32 -0
  60. data/test/engineyard-local/middelware/default_provisioner_test.rb +35 -0
  61. data/test/engineyard-local/middelware/exec_test.rb +19 -0
  62. data/test/engineyard-local/middelware/network_test.rb +94 -0
  63. data/test/engineyard-local/middelware/rails/command_test.rb +24 -0
  64. data/test/engineyard-local/middelware/rails/db_test.rb +23 -0
  65. data/test/engineyard-local/middelware/rails/install_test.rb +24 -0
  66. data/test/engineyard-local/ui_test.rb +22 -0
  67. data/test/engineyard-local/virtualbox_test.rb +34 -0
  68. data/test/integration/up_test.rb +28 -0
  69. data/test/test_helper.rb +78 -0
  70. metadata +178 -0
@@ -0,0 +1,11 @@
1
+ module Engineyard
2
+ module Local
3
+ module Command
4
+ class VagrantAction < Base
5
+ def exec(action)
6
+ run(Vagrant.actions.get(action))
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ module Engineyard
2
+ module Local
3
+ module Errors
4
+ class PossibleIPCollision < Vagrant::Errors::VagrantError
5
+ status_code(101)
6
+ error_key(:possible_ip_collision, "eylocal.actions.network")
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,28 @@
1
+ require "engineyard-local/middleware/bundle"
2
+ require "engineyard-local/middleware/dna"
3
+ require "engineyard-local/middleware/chef"
4
+ require "engineyard-local/middleware/rails"
5
+ require "engineyard-local/middleware/exec"
6
+ require "engineyard-local/middleware/tag"
7
+ require "engineyard-local/middleware/network"
8
+ require "engineyard-local/middleware/default_provisioner"
9
+
10
+ # alter some of the core vagrant middleware stacks to make sure
11
+ # the the default provisioner is run where necessary
12
+ module Engineyard
13
+ module Local
14
+ module Middleware
15
+ registry = Vagrant.actions
16
+ up = registry.get(:up)
17
+ reload = registry.get(:reload)
18
+
19
+ up.insert(4, DefaultProvisioner)
20
+
21
+ # alter the reload middelware to check for default provisioning
22
+ reload.insert(1, DefaultProvisioner)
23
+
24
+ registry.register(:up, up)
25
+ registry.register(:reload, reload)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,40 @@
1
+ module Engineyard
2
+ module Local
3
+ module Middleware
4
+ class Bundle
5
+ include Middleware::Helpers::Rvm
6
+ include Middleware::Helpers::Executable
7
+
8
+ def initialize(app, env)
9
+ @app = app
10
+ @env = env
11
+ end
12
+
13
+ def call(env)
14
+ @env[:ui].info I18n.t("eylocal.setup.bundling")
15
+ ssh_exec!(env, commands)
16
+ @app.call(env)
17
+ end
18
+
19
+ def commands
20
+ rvm_env +
21
+ [
22
+ ensure_bundler_is_installed,
23
+ "cd #{project_dir}",
24
+ "sudo mkdir -p #{project_dir} /data/#{Engineyard::Local.config['app_name']}/",
25
+ "if [[ ! -e /data/#{Engineyard::Local.config['app_name']}/current ]]; then sudo ln -sf #{project_dir} /data/#{Engineyard::Local.config['app_name']}/current; fi",
26
+ if_gemfile_exists("sudo bundle install")
27
+ ]
28
+ end
29
+
30
+ def ensure_bundler_is_installed
31
+ "sudo bash -c 'command -v bundle &>/dev/null || { sudo gem install bundler; exit 0; }'"
32
+ end
33
+
34
+ def if_gemfile_exists(cmd)
35
+ "if [[ -e Gemfile || -e Gemfile.lock ]]; then #{cmd}; fi"
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,44 @@
1
+ module Engineyard
2
+ module Local
3
+ module Middleware
4
+ class Chef
5
+ include Middleware::Helpers::Executable
6
+ include Middleware::Helpers::Rvm
7
+
8
+ def initialize(app, env)
9
+ @app = app
10
+ @env = env
11
+ end
12
+
13
+ def call(env)
14
+ @env[:ui].info I18n.t("eylocal.setup.chef")
15
+ ssh_exec!(env, commands)
16
+ @app.call(env)
17
+ end
18
+
19
+ def commands
20
+ rvm_env +
21
+ [
22
+ ensure_that_recipes_are_installed,
23
+ "cd /etc/chef",
24
+ "sudo env PATH=/usr/local/ey_resin/bin:/sbin:/bin:/usr/sbin:/usr/bin chef-solo -j /etc/chef/dna.json -c /etc/chef/solo.rb",
25
+ run_custom_chef_in_application_if_it_exists
26
+ ]
27
+ end
28
+
29
+ def ensure_that_recipes_are_installed
30
+ "if [[ ! -d /etc/chef/recipes ]]; then curl -s #{cookbook_uri} > /tmp/chef.tar.bz2; sudo mkdir -p /etc/chef/recipes && cd /etc/chef/recipes && sudo tar -xjf /tmp/chef.tar.bz2; rm /tmp/chef.tar.bz2; fi"
31
+ end
32
+
33
+ def run_custom_chef_in_application_if_it_exists
34
+ "if [[ -d /vagrant/deploy ]] ; then shopt -s nullglob; for rb in /vagrant/deploy/*.rb; do sudo env PATH=/usr/local/ey_resin/bin:/sbin:/bin:/usr/sbin:/usr/bin chef-solo -j /etc/chef/dna.json -c ${rb}; done; shopt -u nullglob; fi"
35
+ end
36
+
37
+ def cookbook_uri
38
+ Local.config[:box_defaults][:cookbook_uri]
39
+ end
40
+
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,34 @@
1
+ module Engineyard
2
+ module Local
3
+ module Middleware
4
+ class DefaultProvisioner
5
+ def initialize(app, env)
6
+ @app, @env = app, env
7
+ end
8
+
9
+ # if the default recipe cookbooks/main/recipes/default.rb exists
10
+ # and the user doesn't already have a provisioner configured use
11
+ # the default recipe
12
+ def call(env)
13
+ if File.exists?(default_recipe_path) && !provisioner_configured?
14
+ @env[:ui].info I18n.t("eylocal.provisioner", :recipe_path => default_recipe_path)
15
+ @env[:vm].config.vm.provision :chef_solo do |chef|
16
+ chef.cookbooks_path = "cookbooks"
17
+ chef.add_recipe "main"
18
+ end
19
+ end
20
+
21
+ @app.call(env)
22
+ end
23
+
24
+ def default_recipe_path
25
+ File.join(@env[:root_path], "cookbooks", "main", "recipes", "default.rb")
26
+ end
27
+
28
+ def provisioner_configured?
29
+ !@env[:vm].config.vm.provisioners.empty?
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,80 @@
1
+ require 'fileutils'
2
+
3
+ module Engineyard
4
+ module Local
5
+ module Middleware
6
+ class DNA
7
+ include Middleware::Helpers::Uploadable
8
+ include Middleware::Helpers::Executable
9
+
10
+ def initialize(app, env)
11
+ @app = app
12
+ @env = env
13
+ end
14
+
15
+ def call(env)
16
+ @env[:ui].info I18n.t("eylocal.setup.dna")
17
+
18
+ create_dna
19
+ ssh_upload!(env, dna_tmp_to, dna_tmp_to)
20
+ ssh_upload!(env, solo_from, solo_tmp_to)
21
+ ssh_exec!(env, commands)
22
+ FileUtils.rm dna_tmp_to
23
+ @app.call(env)
24
+ end
25
+
26
+ def create_dna
27
+ dna_data = File.read(dna_from)
28
+
29
+ app_url = %Q{#{`git config remote.origin.url`}}.strip
30
+ app_url = app_url.empty? ? "file://#{Dir.pwd}" : app_url
31
+ Engineyard::Local.config['app_git_url'] = app_url
32
+
33
+ Engineyard::Local.config['app_name'] = Engineyard::Local.config['app_git_url'].sub(/^.*\/([^\/\.]+)(?:\.git)?/,'\1').strip.gsub(/\s/,'_')
34
+
35
+ Engineyard::Local.config['app_revision'] = %Q{#{`git show --format=oneline`.gsub(/^(\w+).*$/m,'\1')}}.strip
36
+ app_type = 'rack'
37
+
38
+ dna_data.gsub!(/APPNAME/,Engineyard::Local.config['app_name'])
39
+ dna_data.gsub!(/APPURL/,Engineyard::Local.config['app_git_url'])
40
+ dna_data.gsub!(/APPREVISION/,Engineyard::Local.config['app_revision'])
41
+ dna_data.gsub!(/APPTYPE/,app_type)
42
+
43
+ File.open(dna_tmp_to, "w+") {|fh| fh.write dna_data}
44
+ end
45
+
46
+ def commands
47
+ [
48
+ "sudo mv #{solo_tmp_to} #{solo_to}",
49
+ "sudo mv #{dna_tmp_to} #{dna_to}",
50
+ ]
51
+ end
52
+
53
+ def dna_from
54
+ File.expand_path("config/dna.json", Engineyard::Local.project_root)
55
+ end
56
+
57
+ def dna_tmp_to
58
+ "/tmp/dna.json.#{$$}"
59
+ end
60
+
61
+ def dna_to
62
+ "/etc/chef/dna.json"
63
+ end
64
+
65
+ def solo_from
66
+ File.expand_path("config/solo.rb", Engineyard::Local.project_root)
67
+ end
68
+
69
+ def solo_tmp_to
70
+ "/tmp/solo.rb.#{$$}"
71
+ end
72
+
73
+ def solo_to
74
+ "/etc/chef/solo.rb"
75
+ end
76
+
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,27 @@
1
+ module Engineyard
2
+ module Local
3
+ module Middleware
4
+ class Exec
5
+ include Middleware::Helpers::Rvm
6
+ include Middleware::Helpers::Executable
7
+
8
+ def initialize(app, env)
9
+ @app, @env = app, env
10
+ end
11
+
12
+ def call(env)
13
+ ssh_exec!(env, commands, :prefix => false)
14
+ @app.call(env)
15
+ end
16
+
17
+ def commands
18
+ rvm_env + [ "cd #{project_dir}", command_args ]
19
+ end
20
+
21
+ def command_args
22
+ @env["eylocal.exec.command_args"].join(" ")
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,4 @@
1
+ require "engineyard-local/middleware/helpers/executable"
2
+ require "engineyard-local/middleware/helpers/network"
3
+ require "engineyard-local/middleware/helpers/rvm"
4
+ require "engineyard-local/middleware/helpers/uploadable"
@@ -0,0 +1,27 @@
1
+ module Engineyard
2
+ module Local
3
+ module Middleware
4
+ module Helpers
5
+ module Executable
6
+ # Depends soley on the commands method
7
+ def ssh_exec!(env, bash_commands, opts = {})
8
+ commands = bash_commands.join(";\n")
9
+
10
+ env[:vm].channel.execute(commands) do |stream, data|
11
+ if stream == :stdout
12
+ env[:vm].ui.info(data.strip)
13
+ else
14
+ env[:vm].ui.error(data.strip)
15
+ end
16
+ end
17
+ end
18
+
19
+ # TODO might belong in a general helpers mixin
20
+ def project_dir
21
+ @env[:vm].config.vm.shared_folders["v-root"][:guestpath]
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ module Engineyard
2
+ module Local
3
+ module Middleware
4
+ module Helpers
5
+ module Network
6
+ def proposed_ip
7
+ # networks is an array of pairs, snd is the ip, fst is type
8
+ # TODO this generally sucks and is tightly coupled to vagrant internals
9
+ (networks.first || [[]]).last.first || Local.config[:network] || '10.0.0.1'
10
+ end
11
+
12
+ def networks
13
+ @env[:vm].config.vm.networks
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,37 @@
1
+ module Engineyard
2
+ module Local
3
+ module Middleware
4
+ module Helpers
5
+ # TODO this whole helper should be removed by setting up rvm properly
6
+ # for the vagrant ssh session user inside the vm
7
+ module Rvm
8
+ def ruby_env_const
9
+ Local.config[:rvm][:ruby_env_const]
10
+ end
11
+
12
+ # get the first ruby listed by rvm list
13
+ # TODO fragile
14
+ def export_ruby
15
+ %Q(export #{ ruby_env_const }=`rvm list | grep "^ " | awk '{ print $1 }' | tail -1`)
16
+ end
17
+
18
+ # TODO determine gemset and app name and export
19
+ def use_ruby_with_gemset
20
+ "rvm use $#{ ruby_env_const } > /dev/null"
21
+ end
22
+
23
+ def bash_rvm_setup
24
+ "source /etc/profile.d/*"
25
+ end
26
+
27
+ # everything needed to setup the proper gemset env
28
+ def rvm_env
29
+ [ bash_rvm_setup,
30
+ export_ruby,
31
+ use_ruby_with_gemset ]
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,14 @@
1
+ module Engineyard
2
+ module Local
3
+ module Middleware
4
+ module Helpers
5
+ module Uploadable
6
+ # Depends soley on the commands method
7
+ def ssh_upload!(env, to, from)
8
+ env[:vm].channel.upload(to, from)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,64 @@
1
+ module Engineyard
2
+ module Local
3
+ module Middleware
4
+ class Network
5
+ include Helpers::Network
6
+
7
+ def initialize(app, env, options)
8
+ @app, @env, @options = app, env, options
9
+ end
10
+
11
+ def call(env)
12
+ Virtualbox.uuid_map.each do |name, uuid|
13
+ # skip the current vm
14
+ next if uuid == env[:vm].uuid
15
+
16
+ # get the configured ip from the tag data stored by ey-local
17
+ existing_ip = Virtualbox.extra_data(uuid, Local.config[:network_ip_key])
18
+
19
+ # if the proposed ip matches a different vms tagged ip
20
+ # let the user decide if this is intended or needs to be fixed
21
+ if(!@options[:silent] && existing_ip == proposed_ip)
22
+ while !["Y", "N"].include?(result ||= nil)
23
+ result = check_with_user(name)
24
+ end
25
+
26
+ if result == "N"
27
+ raise(Errors::PossibleIPCollision,
28
+ :proposed_ip => proposed_ip,
29
+ :vm_name => name)
30
+ end
31
+
32
+ if result == "Y"
33
+ break
34
+ end
35
+ end
36
+ end
37
+
38
+ @app.call(env)
39
+ end
40
+
41
+ def check_with_user(name)
42
+ @env[:ui].ask(prompt(name), :color => Thor::Shell::Color::YELLOW)
43
+ end
44
+
45
+ def prompt(name)
46
+ @prompt ||= I18n.t("eylocal.actions.network.prompt",
47
+ :vm_name => name,
48
+ :proposed_ip => proposed_ip)
49
+ end
50
+
51
+ #taken directly from Vagrant source
52
+ def recover(env)
53
+ if env[:vm].created? && env["vagrant.error"].is_a?(Errors::PossibleIPCollision)
54
+ # Interrupted, destroy the VM. We note that we don't want to
55
+ # validate the configuration here.
56
+ destroy_env = env.clone
57
+ destroy_env[:validate] = false
58
+ env[:action_runner].run(:destroy, destroy_env)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end