engineyard-local 0.2.1 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/Gemfile +1 -0
  2. data/Gemfile.lock +73 -1
  3. data/README.md +40 -38
  4. data/config/dna.json +92 -66
  5. data/config/locales/en.yml +94 -9
  6. data/config/patches/chef-ey-1.1.336.patch +55 -0
  7. data/config/settings.yml +6 -1
  8. data/engineyard-local.gemspec +5 -4
  9. data/install/osx/README.md +42 -4
  10. data/install/osx/engineyard-local/engineyard-local.pkgproj +35 -6
  11. data/install/osx/images/dmg_background.png +0 -0
  12. data/install/osx/images/dmg_icon.icns +0 -0
  13. data/lib/engineyard-local.rb +64 -0
  14. data/lib/engineyard-local/command.rb +23 -0
  15. data/lib/engineyard-local/command/base.rb +78 -1
  16. data/lib/engineyard-local/command/group.rb +70 -6
  17. data/lib/engineyard-local/command/help.rb +36 -0
  18. data/lib/engineyard-local/command/helpers.rb +12 -0
  19. data/lib/engineyard-local/command/start.rb +26 -0
  20. data/lib/engineyard-local/command/status.rb +33 -0
  21. data/lib/engineyard-local/command/stop.rb +26 -0
  22. data/lib/engineyard-local/command/terminate.rb +26 -0
  23. data/lib/engineyard-local/command/up.rb +24 -15
  24. data/lib/engineyard-local/command/update.rb +42 -0
  25. data/lib/engineyard-local/middleware.rb +1 -0
  26. data/lib/engineyard-local/middleware/bundle.rb +1 -0
  27. data/lib/engineyard-local/middleware/chef.rb +52 -2
  28. data/lib/engineyard-local/middleware/cookbooks.rb +111 -0
  29. data/lib/engineyard-local/middleware/dna.rb +6 -4
  30. data/lib/engineyard-local/middleware/exec.rb +2 -1
  31. data/lib/engineyard-local/middleware/helpers/executable.rb +14 -2
  32. data/lib/engineyard-local/middleware/network.rb +3 -3
  33. data/lib/engineyard-local/middleware/rails.rb +1 -0
  34. data/lib/engineyard-local/middleware/rails/assets.rb +35 -0
  35. data/lib/engineyard-local/middleware/rails/command.rb +1 -1
  36. data/lib/engineyard-local/middleware/rails/db.rb +0 -1
  37. data/lib/engineyard-local/middleware/rails/new.rb +1 -1
  38. data/lib/engineyard-local/version.rb +1 -1
  39. data/test/engineyard-local/command/up_test.rb +1 -0
  40. data/test/integration/commands_test.rb +30 -0
  41. data/test/test_helper.rb +8 -2
  42. metadata +81 -70
  43. data/install/osx/images/eylocal_installer.png +0 -0
  44. data/test/integration/up_test.rb +0 -28
@@ -0,0 +1,42 @@
1
+ module Engineyard
2
+ module Local
3
+ module Command
4
+ class Update < Base
5
+
6
+ def exec(*args)
7
+ installed_cookbook_version = []
8
+ la_brea_data = []
9
+ run( Vagrant::Action::Builder.new do
10
+ use( Engineyard::Local::Middleware::Cookbooks,:get_installed_version, installed_cookbook_version )
11
+ use( Engineyard::Local::Middleware::Cookbooks,:get_la_brea_data, la_brea_data )
12
+ end )
13
+
14
+ installed_cookbook_version = installed_cookbook_version.first
15
+ la_brea_data = la_brea_data.first
16
+
17
+ if ( installed_cookbook_version == la_brea_data[:cookbooks_version] )
18
+ @env.ui.info I18n.t("eylocal.update.nothing")
19
+ else
20
+ @env.ui.info I18n.t("eylocal.update.updating")
21
+
22
+ new_cookbook_version = []
23
+ run( Vagrant::Action::Builder.new do
24
+ use( Engineyard::Local::Middleware::Cookbooks,:update_cookbooks )
25
+ use( Engineyard::Local::Middleware::Cookbooks,:get_installed_version, new_cookbook_version )
26
+ end )
27
+ new_cookbook_version = new_cookbook_version.first
28
+
29
+ if (installed_cookbook_version == new_cookbook_version)
30
+ @env.ui.info I18n.t("eylocal.update.fail")
31
+ else
32
+ @env.ui.info I18n.t("eylocal.update.success")
33
+ run Engineyard::Local::Command::Up.setup_stack(options)
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
@@ -5,6 +5,7 @@ require "engineyard-local/middleware/rails"
5
5
  require "engineyard-local/middleware/exec"
6
6
  require "engineyard-local/middleware/tag"
7
7
  require "engineyard-local/middleware/network"
8
+ require "engineyard-local/middleware/cookbooks"
8
9
  require "engineyard-local/middleware/default_provisioner"
9
10
 
10
11
  # alter some of the core vagrant middleware stacks to make sure
@@ -23,6 +23,7 @@ module Engineyard
23
23
  "cd #{project_dir}",
24
24
  "sudo mkdir -p #{project_dir} /data/#{Engineyard::Local.config['app_name']}/",
25
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 [[ ! -e /data/#{Engineyard::Local.config['app_name']}/current/public ]]; then sudo mkdir -p /data/#{Engineyard::Local.config['app_name']}/current/public; fi",
26
27
  if_gemfile_exists("sudo bundle install")
27
28
  ]
28
29
  end
@@ -2,6 +2,7 @@ module Engineyard
2
2
  module Local
3
3
  module Middleware
4
4
  class Chef
5
+ include Middleware::Helpers::Uploadable
5
6
  include Middleware::Helpers::Executable
6
7
  include Middleware::Helpers::Rvm
7
8
 
@@ -12,22 +13,38 @@ module Engineyard
12
13
 
13
14
  def call(env)
14
15
  @env[:ui].info I18n.t("eylocal.setup.chef")
16
+ copy_patches(env)
15
17
  ssh_exec!(env, commands)
16
18
  @app.call(env)
17
19
  end
18
20
 
21
+ def cached_data_path
22
+ "#{@env[:root_path]}/.ey_local_data"
23
+ end
24
+
25
+ def copy_patches(env)
26
+ patchdir = "/tmp/eylocal_patches"
27
+ ssh_exec!(env, ["mkdir -p #{patchdir}"])
28
+
29
+ Local.config[:box_defaults][:patches].each do |p|
30
+ from = File.expand_path("config/patches/#{p}", Engineyard::Local.project_root)
31
+ ssh_upload!(env, from, File.join(patchdir,p))
32
+ end
33
+ end
34
+
19
35
  def commands
20
36
  rvm_env +
21
37
  [
22
38
  ensure_that_recipes_are_installed,
23
39
  "cd /etc/chef",
24
40
  "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",
41
+ "if [[ ! -e /usr/bin/rake ]]; then sudo ln -s /usr/bin/rake19 /usr/bin/rake; fi",
25
42
  run_custom_chef_in_application_if_it_exists
26
43
  ]
27
44
  end
28
45
 
29
46
  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"
47
+ "if [[ ! -d /etc/chef/recipes ]]; then curl -s #{cookbook_uri} > /tmp/chef.tar#{cookbook_suffix}; sudo mkdir -p /etc/chef/recipes && cd /etc/chef/recipes && sudo tar #{tar_flags} /tmp/chef.tar#{cookbook_suffix}; sudo patch -p1 < /tmp/eylocal_patches/* >& /dev/null; sudo touch #{Engineyard::Local::Middleware::Cookbooks::COOKBOOK_VERSION_FILE}; sudo chown vagrant #{Engineyard::Local::Middleware::Cookbooks::COOKBOOK_VERSION_FILE}; echo \"#{cookbook_version}\" > #{Engineyard::Local::Middleware::Cookbooks::COOKBOOK_VERSION_FILE}; rm /tmp/chef.tar#{cookbook_suffix}; fi"
31
48
  end
32
49
 
33
50
  def run_custom_chef_in_application_if_it_exists
@@ -35,7 +52,40 @@ module Engineyard
35
52
  end
36
53
 
37
54
  def cookbook_uri
38
- Local.config[:box_defaults][:cookbook_uri]
55
+ ey_local_data = nil
56
+ if ( !@cookbook_uri && File.exist?( cached_data_path ) )
57
+ ey_local_data = YAML.load( File.read( cached_data_path ) )
58
+ end
59
+ @la_brea_data ||= ey_local_data ? ey_local_data[:la_brea_data] : nil
60
+ @cookbook_uri ||= ( ey_local_data && ey_local_data[:la_brea_data][:cookbooks_url] ) ?
61
+ ey_local_data[:la_brea_data][:cookbooks_url] : Local.config[:box_defaults][:cookbook_uri]
62
+ end
63
+
64
+ def cookbook_suffix
65
+ cookbook_uri =~ /\.(\w+)$/
66
+ case $1
67
+ when /bz2|tbz/
68
+ ".bz2"
69
+ when /gz|tgz/
70
+ ".gz"
71
+ else
72
+ ""
73
+ end
74
+ end
75
+
76
+ def tar_flags
77
+ case cookbook_suffix
78
+ when /bz2|tbz/
79
+ "-xjf"
80
+ when /gz|tgz/
81
+ "-xzf"
82
+ else
83
+ "-xf"
84
+ end
85
+ end
86
+
87
+ def cookbook_version
88
+ ( @la_brea_data && @la_brea_data[:cookbooks_version] ) ? @la_brea_data[:cookbooks_version] : Local.config[:box_defaults][:cookbook_version]
39
89
  end
40
90
 
41
91
  end
@@ -0,0 +1,111 @@
1
+ require 'open-uri'
2
+
3
+ module Engineyard
4
+ module Local
5
+ module Middleware
6
+ class Cookbooks
7
+ include Middleware::Helpers::Executable
8
+ include Middleware::Helpers::Rvm
9
+ extend Middleware::Helpers::Executable
10
+
11
+ DATA_EXPIRATION = 600
12
+ COOKBOOK_VERSION_FILE = "/etc/chef/ey_local_cookbook_version"
13
+
14
+ def cookbook_query_commands
15
+ [
16
+ "cat #{COOKBOOK_VERSION_FILE}"
17
+ ]
18
+ end
19
+
20
+ def initialize(app, env, *args)
21
+ @app = app
22
+ @env = env
23
+ @args = args
24
+ end
25
+
26
+ def cached_data_path
27
+ "#{@env[:root_path]}/.ey_local_data"
28
+ end
29
+
30
+ def call(env)
31
+ cmd,response = @args
32
+
33
+ if File.exist?(cached_data_path)
34
+ ey_local_data = YAML.load(File.read(cached_data_path))
35
+ else
36
+ ey_local_data = {
37
+ :installed_cookbook_version => {
38
+ :data => nil,
39
+ :as_of => Time.at(1)
40
+ },
41
+ :la_brea_data => {
42
+ :changelog => nil,
43
+ :cookbooks_url => nil,
44
+ :cookbooks_version => nil,
45
+ :created_at => nil,
46
+ :updated_at => nil,
47
+ :uuid => nil,
48
+ :as_of => Time.at(1)
49
+ }
50
+ }
51
+ end
52
+
53
+ case cmd
54
+ when :get_installed_version
55
+ get_installed_cookbook_version(ey_local_data)
56
+ response << ey_local_data[:installed_cookbook_version][:data]
57
+
58
+ when :get_la_brea_data
59
+ get_la_brea_data(ey_local_data)
60
+ response << ey_local_data[:la_brea_data]
61
+
62
+ when :update_cookbooks
63
+ get_la_brea_data(ey_local_data)
64
+
65
+ ssh_exec!(@env, update_cookbook_commands(
66
+ ey_local_data[:la_brea_data][:cookbooks_url],
67
+ ey_local_data[:la_brea_data][:cookbooks_version]))
68
+ get_installed_cookbook_version(ey_local_data, true)
69
+ end
70
+
71
+ @app.call(env)
72
+ end
73
+
74
+ def get_installed_cookbook_version(ey_local_data, force = false)
75
+ if ( force || ( Time.now > ( ey_local_data[:installed_cookbook_version][:as_of] + DATA_EXPIRATION ) ) )
76
+ cmd_output = ssh_exec(@env, cookbook_query_commands, {:error_check => false})
77
+ ey_local_data[:installed_cookbook_version][:data] = cmd_output[:stdout] ? cmd_output[:stdout].join.chomp : nil
78
+ ey_local_data[:installed_cookbook_version][:as_of] = Time.now
79
+ write_cached_data(ey_local_data)
80
+ end
81
+ end
82
+
83
+ def get_la_brea_data(ey_local_data)
84
+ if ( Time.now > ( ey_local_data[:la_brea_data][:as_of] + DATA_EXPIRATION ) )
85
+ r = YAML.load(open("http://#{Engineyard::Local.config[:la_brea_bridge]}/stack")) rescue nil
86
+ if r
87
+ r[:as_of] = Time.now
88
+ ey_local_data[:la_brea_data] = r
89
+ write_cached_data(ey_local_data)
90
+ end
91
+ end
92
+ end
93
+
94
+ def write_cached_data(ey_local_data)
95
+ File.open(cached_data_path, "w+") {|fh| fh.write YAML.dump(ey_local_data)}
96
+ end
97
+
98
+ def update_cookbook_commands(cookbook_uri, cookbook_version)
99
+ rvm_env +
100
+ [
101
+ "curl -s #{cookbook_uri} > /tmp/_chef.tgz",
102
+ "sudo mkdir -p /etc/chef/recipes && cd /etc/chef/recipes && sudo tar -xzf /tmp/_chef.tgz",
103
+ "if (( ! $? )); then sudo chown \`whoami\` #{COOKBOOK_VERSION_FILE}; echo \"#{cookbook_version}\" > #{COOKBOOK_VERSION_FILE}; fi",
104
+ "rm /tmp/_chef.tgz"
105
+ ]
106
+ end
107
+
108
+ end
109
+ end
110
+ end
111
+ end
@@ -45,8 +45,10 @@ module Engineyard
45
45
 
46
46
  def commands
47
47
  [
48
- "sudo mv #{solo_tmp_to} #{solo_to}",
49
- "sudo mv #{dna_tmp_to} #{dna_to}",
48
+ # Windows slashes are eliminated when these commands are
49
+ # passed, so we need to ensure they are correctly escaped.
50
+ "sudo mv #{solo_tmp_to.gsub("\\","\\\\\\")} #{solo_to}",
51
+ "sudo mv #{dna_tmp_to.gsub("\\","\\\\\\")} #{dna_to}",
50
52
  ]
51
53
  end
52
54
 
@@ -55,7 +57,7 @@ module Engineyard
55
57
  end
56
58
 
57
59
  def dna_tmp_to
58
- "/tmp/dna.json.#{$$}"
60
+ File.join(Engineyard::Local.tmp_dir,"dna.json.#{$$}")
59
61
  end
60
62
 
61
63
  def dna_to
@@ -67,7 +69,7 @@ module Engineyard
67
69
  end
68
70
 
69
71
  def solo_tmp_to
70
- "/tmp/solo.rb.#{$$}"
72
+ File.join(Engineyard::Local.tmp_dir,"solo.rb.#{$$}")
71
73
  end
72
74
 
73
75
  def solo_to
@@ -15,7 +15,8 @@ module Engineyard
15
15
  end
16
16
 
17
17
  def commands
18
- rvm_env + [ "cd #{project_dir}", command_args ]
18
+ rvm_env +
19
+ [ "cd #{project_dir}", command_args ]
19
20
  end
20
21
 
21
22
  def command_args
@@ -9,13 +9,25 @@ module Engineyard
9
9
 
10
10
  env[:vm].channel.execute(commands) do |stream, data|
11
11
  if stream == :stdout
12
- env[:vm].ui.info(data.strip)
12
+ env[:vm].ui.info(data.strip, opts)
13
13
  else
14
- env[:vm].ui.error(data.strip)
14
+ env[:vm].ui.error(data.strip, opts)
15
15
  end
16
16
  end
17
17
  end
18
18
 
19
+ def ssh_exec(env, bash_commands, opts = {})
20
+ commands = bash_commands.join(";\n")
21
+
22
+ r = Hash.new {|h,k| h[k] = []}
23
+
24
+ env[:vm].channel.execute(commands, opts) do |stream, data|
25
+ r[stream] << data
26
+ end
27
+
28
+ r
29
+ end
30
+
19
31
  # TODO might belong in a general helpers mixin
20
32
  def project_dir
21
33
  @env[:vm].config.vm.shared_folders["v-root"][:guestpath]
@@ -19,17 +19,17 @@ module Engineyard
19
19
  # if the proposed ip matches a different vms tagged ip
20
20
  # let the user decide if this is intended or needs to be fixed
21
21
  if(!@options[:silent] && existing_ip == proposed_ip)
22
- while !["Y", "N"].include?(result ||= nil)
22
+ while !["Y","y","N","n"].include?(result ||= nil)
23
23
  result = check_with_user(name)
24
24
  end
25
25
 
26
- if result == "N"
26
+ if result.upcase == "N"
27
27
  raise(Errors::PossibleIPCollision,
28
28
  :proposed_ip => proposed_ip,
29
29
  :vm_name => name)
30
30
  end
31
31
 
32
- if result == "Y"
32
+ if result.upcase == "Y"
33
33
  break
34
34
  end
35
35
  end
@@ -1,3 +1,4 @@
1
1
  require "engineyard-local/middleware/rails/db"
2
+ require "engineyard-local/middleware/rails/assets"
2
3
  require "engineyard-local/middleware/rails/command"
3
4
  require "engineyard-local/middleware/rails/install"
@@ -0,0 +1,35 @@
1
+ module Engineyard
2
+ module Local
3
+ module Middleware
4
+ module Rails
5
+ class Assets
6
+ include Middleware::Helpers::Rvm
7
+ include Middleware::Helpers::Executable
8
+
9
+ def initialize(app, env)
10
+ @app = app
11
+ @env = env
12
+ end
13
+
14
+ def call(env)
15
+ @env[:ui].info I18n.t("eylocal.setup.rails.assets")
16
+ ssh_exec!(env, commands)
17
+ @app.call(env)
18
+ end
19
+
20
+ def commands
21
+ [ "cd #{project_dir}",
22
+ if_task_exists("assets:precompile"),
23
+ "sudo /etc/init.d/nginx restart" ]
24
+ end
25
+
26
+ # if there's a Rakefile, and rake -T contains the task we're looking for
27
+ # execute the task
28
+ def if_task_exists(task)
29
+ "if [[ `rake -T > /dev/null 2>&1 && rake -T | grep -e '#{task}'` ]]; then rake #{task}; fi"
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -16,7 +16,7 @@ module Engineyard
16
16
  end
17
17
 
18
18
  def commands
19
- rvm_env +
19
+ #rvm_env +
20
20
  [ "cd #{project_dir}",
21
21
  "rails #{command_args}" ]
22
22
  end
@@ -18,7 +18,6 @@ module Engineyard
18
18
  end
19
19
 
20
20
  def commands
21
- rvm_env +
22
21
  [ "cd #{project_dir}",
23
22
  if_task_exists("db:create"),
24
23
  if_task_exists("db:migrate") ]
@@ -16,7 +16,7 @@ module Engineyard
16
16
  end
17
17
 
18
18
  def commands
19
- rvm_env +
19
+ #rvm_env +
20
20
  [ "cd #{project_dir}",
21
21
  "rails #{command_args}" ]
22
22
  end
@@ -1,5 +1,5 @@
1
1
  module Engineyard
2
2
  module Local
3
- VERSION = "0.2.1"
3
+ VERSION = "1.0.1"
4
4
  end
5
5
  end
@@ -16,6 +16,7 @@ class CommandUpTest < Test::Unit::TestCase
16
16
  # prevent the bundle middleware from executing
17
17
  @klass.any_instance.stubs(:with_target_vms).returns([])
18
18
  @klass.any_instance.stubs(:env).returns(@env)
19
+ @klass.any_instance.stubs(:pwd_matches_root_path).returns(true)
19
20
 
20
21
  @env.stubs(:cli)
21
22
  @env.stubs(:root_path).returns(nil)
@@ -0,0 +1,30 @@
1
+ require "test_helper"
2
+
3
+ module Integration
4
+ class CommandsTest < Test::Unit::TestCase
5
+ include Engineyard::Local::TestHelpers::Integration
6
+
7
+ def setup
8
+ Dir.chdir(ensure_test_directory)
9
+ fail_on_stderr
10
+ clean_env
11
+ end
12
+
13
+ # TODO cramming it all in here to ensure sequence
14
+ should "up completes as do dependent commands" do
15
+ assert_cmd("ey-local up --silent")
16
+ assert(`ey-local list` != "", "the output should not be empty")
17
+ assert(`ey-local exec echo "foo"`.include?("foo"), "output should include the echo value")
18
+
19
+ log_setup
20
+ assert(`ey-local log`.strip == "dev", "log output default should come from dev log without prefix")
21
+ assert(`ey-local log --environment=production`.strip == "prod", "log output default should come from dev log without prefix")
22
+ assert_cmd("ey-local destroy -f")
23
+ end
24
+
25
+ def log_setup
26
+ `echo "dev" > log/development.log`
27
+ `echo "prod" > log/production.log`
28
+ end
29
+ end
30
+ end