engineyard-local 0.2.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 (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