engineyard 0.2.9 → 0.2.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/lib/engineyard.rb +1 -17
  2. data/lib/engineyard/account.rb +13 -49
  3. data/lib/engineyard/account/app.rb +18 -0
  4. data/lib/engineyard/account/app_master.rb +12 -0
  5. data/lib/engineyard/account/environment.rb +31 -0
  6. data/lib/engineyard/account/log.rb +18 -0
  7. data/lib/engineyard/api.rb +4 -2
  8. data/lib/engineyard/cli.rb +34 -11
  9. data/lib/engineyard/cli/ui.rb +1 -1
  10. data/spec/engineyard/api_spec.rb +6 -6
  11. data/spec/engineyard/cli/api_spec.rb +1 -1
  12. data/spec/engineyard/config_spec.rb +9 -9
  13. data/spec/ey/deploy_spec.rb +40 -21
  14. data/spec/ey/logs_spec.rb +28 -0
  15. data/spec/spec_helper.rb +12 -13
  16. data/spec/support/fake_awsm.ru +251 -0
  17. data/spec/support/helpers.rb +50 -38
  18. data/spec/support/ruby_ext.rb +29 -0
  19. metadata +52 -102
  20. data/lib/vendor/thor.rb +0 -244
  21. data/lib/vendor/thor/actions.rb +0 -275
  22. data/lib/vendor/thor/actions/create_file.rb +0 -103
  23. data/lib/vendor/thor/actions/directory.rb +0 -91
  24. data/lib/vendor/thor/actions/empty_directory.rb +0 -134
  25. data/lib/vendor/thor/actions/file_manipulation.rb +0 -223
  26. data/lib/vendor/thor/actions/inject_into_file.rb +0 -104
  27. data/lib/vendor/thor/base.rb +0 -540
  28. data/lib/vendor/thor/core_ext/file_binary_read.rb +0 -9
  29. data/lib/vendor/thor/core_ext/hash_with_indifferent_access.rb +0 -75
  30. data/lib/vendor/thor/core_ext/ordered_hash.rb +0 -100
  31. data/lib/vendor/thor/error.rb +0 -30
  32. data/lib/vendor/thor/group.rb +0 -271
  33. data/lib/vendor/thor/invocation.rb +0 -180
  34. data/lib/vendor/thor/parser.rb +0 -4
  35. data/lib/vendor/thor/parser/argument.rb +0 -67
  36. data/lib/vendor/thor/parser/arguments.rb +0 -150
  37. data/lib/vendor/thor/parser/option.rb +0 -128
  38. data/lib/vendor/thor/parser/options.rb +0 -169
  39. data/lib/vendor/thor/rake_compat.rb +0 -66
  40. data/lib/vendor/thor/runner.rb +0 -314
  41. data/lib/vendor/thor/shell.rb +0 -83
  42. data/lib/vendor/thor/shell/basic.rb +0 -239
  43. data/lib/vendor/thor/shell/color.rb +0 -108
  44. data/lib/vendor/thor/task.rb +0 -102
  45. data/lib/vendor/thor/util.rb +0 -230
  46. data/lib/vendor/thor/version.rb +0 -3
data/lib/engineyard.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module EY
2
- VERSION = "0.2.9"
2
+ VERSION = "0.2.10"
3
3
 
4
4
  autoload :Account, 'engineyard/account'
5
5
  autoload :API, 'engineyard/api'
@@ -24,21 +24,5 @@ module EY
24
24
  @config ||= EY::Config.new
25
25
  end
26
26
 
27
- def config=(config)
28
- @config = config
29
- end
30
-
31
- def library(libname)
32
- begin
33
- require libname
34
- rescue LoadError
35
- unless @tried_rubygems
36
- require 'rubygems' rescue LoadError nil
37
- @tried_rubygems = true
38
- retry
39
- end
40
- end
41
- end
42
-
43
27
  end
44
28
  end
@@ -1,3 +1,8 @@
1
+ require 'engineyard/account/app'
2
+ require 'engineyard/account/app_master'
3
+ require 'engineyard/account/environment'
4
+ require 'engineyard/account/log'
5
+
1
6
  module EY
2
7
  class Account
3
8
 
@@ -5,10 +10,14 @@ module EY
5
10
  @api = api
6
11
  end
7
12
 
13
+ def request(path, options = { })
14
+ @api.request(path, {:method => :get}.merge(options))
15
+ end
16
+
8
17
  def environments
9
18
  return @environments if @environments
10
- data = @api.request('/environments', :method => :get)["environments"]
11
- @environments = Environment.from_array(data || [])
19
+ data = request('/environments')["environments"]
20
+ @environments = Environment.from_array(data || [], self)
12
21
  end
13
22
 
14
23
  def environment_named(name)
@@ -17,8 +26,8 @@ module EY
17
26
 
18
27
  def apps
19
28
  return @apps if @apps
20
- data = @api.request('/apps', :method => :get)["apps"]
21
- @apps = App.from_array(data || [])
29
+ data = @api.request('/apps')["apps"]
30
+ @apps = App.from_array(data || [], self)
22
31
  end
23
32
 
24
33
  def app_named(name)
@@ -29,50 +38,5 @@ module EY
29
38
  apps.find{|a| repo.urls.include?(a.repository_url) }
30
39
  end
31
40
 
32
- # Classes to represent the returned data
33
- class Environment < Struct.new(:name, :instances_count, :apps, :app_master, :username)
34
- def self.from_hash(hash)
35
- new(
36
- hash["name"],
37
- hash["instances_count"],
38
- App.from_array(hash["apps"]),
39
- AppMaster.from_hash(hash["app_master"]),
40
- hash["ssh_username"]
41
- ) if hash && hash != "null"
42
- end
43
-
44
- def self.from_array(array)
45
- array.map{|n| from_hash(n) } if array && array != "null"
46
- end
47
-
48
- def configuration
49
- EY.config.environments[self.name]
50
- end
51
- alias_method :config, :configuration
52
- end
53
-
54
- class App < Struct.new(:name, :repository_url, :environments)
55
- def self.from_hash(hash)
56
- new(
57
- hash["name"],
58
- hash["repository_uri"], # We use url canonically in the ey gem
59
- Environment.from_array(hash["environments"])
60
- ) if hash && hash != "null"
61
- end
62
-
63
- def self.from_array(array)
64
- array.map{|n| from_hash(n) } if array && array != "null"
65
- end
66
- end
67
-
68
- class AppMaster < Struct.new(:status, :public_hostname)
69
- def self.from_hash(hash)
70
- new(
71
- hash["status"],
72
- hash["public_hostname"]
73
- ) if hash && hash != "null"
74
- end
75
- end
76
-
77
41
  end # Account
78
42
  end # EY
@@ -0,0 +1,18 @@
1
+ module EY
2
+ class Account
3
+ class App < Struct.new(:name, :repository_url, :environments, :account)
4
+ def self.from_hash(hash, account)
5
+ new(
6
+ hash["name"],
7
+ hash["repository_uri"], # We use url canonically in the ey gem
8
+ Environment.from_array(hash["environments"], account),
9
+ account
10
+ ) if hash && hash != "null"
11
+ end
12
+
13
+ def self.from_array(array, account)
14
+ array.map{|n| from_hash(n, account) } if array && array != "null"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ module EY
2
+ class Account
3
+ class AppMaster < Struct.new(:status, :public_hostname)
4
+ def self.from_hash(hash)
5
+ new(
6
+ hash["status"],
7
+ hash["public_hostname"]
8
+ ) if hash && hash != "null"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,31 @@
1
+ module EY
2
+ class Account
3
+ class Environment < Struct.new(:id, :name, :instances_count, :apps, :app_master, :username, :account)
4
+ def self.from_hash(hash, account)
5
+ new(
6
+ hash["id"],
7
+ hash["name"],
8
+ hash["instances_count"],
9
+ App.from_array(hash["apps"], account),
10
+ AppMaster.from_hash(hash["app_master"]),
11
+ hash["ssh_username"],
12
+ account
13
+ ) if hash && hash != "null"
14
+ end
15
+
16
+ def self.from_array(array, account)
17
+ array.map{|n| from_hash(n, account) } if array && array != "null"
18
+ end
19
+
20
+ def logs
21
+ data = account.request("/environments/#{id}/logs")['logs']
22
+ Log.from_array(data || [])
23
+ end
24
+
25
+ def configuration
26
+ EY.config.environments[self.name]
27
+ end
28
+ alias_method :config, :configuration
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,18 @@
1
+ class Log < Struct.new(:id, :role, :main, :custom)
2
+ def self.from_hash(hash)
3
+ new(
4
+ hash["id"],
5
+ hash["role"],
6
+ hash["main"],
7
+ hash["custom"]
8
+ ) if hash && hash != "null"
9
+ end
10
+
11
+ def self.from_array(array)
12
+ array.map{|n| from_hash(n) } if array && array != "null"
13
+ end
14
+
15
+ def instance_name
16
+ "#{role} #{id}"
17
+ end
18
+ end
@@ -24,8 +24,8 @@ module EY
24
24
  class RequestFailed < EY::Error; end
25
25
 
26
26
  def self.request(path, opts={})
27
- EY.library 'rest_client'
28
- EY.library 'json'
27
+ require 'rest_client'
28
+ require 'json'
29
29
 
30
30
  url = EY.config.endpoint + "api/v2#{path}"
31
31
  method = ((meth = opts.delete(:method)) && meth.to_s || "get").downcase.to_sym
@@ -50,6 +50,8 @@ module EY
50
50
  raise RequestFailed, "The requested resource could not be found"
51
51
  rescue RestClient::RequestFailed => e
52
52
  raise RequestFailed, "#{e.message}"
53
+ rescue OpenSSL::SSL::SSLError
54
+ raise RequestFailed, "SSL is misconfigured on your cloud"
53
55
  end
54
56
  raise RequestFailed, "Response body was empty" if resp.body.empty?
55
57
 
@@ -1,12 +1,10 @@
1
- $:.unshift File.expand_path('../../vendor', __FILE__)
2
1
  require 'thor'
3
-
4
2
  require 'engineyard'
5
3
  require 'engineyard/cli/error'
6
4
 
7
5
  module EY
8
6
  class CLI < Thor
9
- EYSD_VERSION = "~>0.2.4"
7
+ EYSD_VERSION = "~>0.2.5"
10
8
 
11
9
  autoload :API, 'engineyard/cli/api'
12
10
  autoload :UI, 'engineyard/cli/ui'
@@ -22,6 +20,7 @@ module EY
22
20
  method_option :force, :type => :boolean, :aliases => %w(-f),
23
21
  :desc => "Force a deploy of the specified branch"
24
22
  method_option :migrate, :type => :string, :aliases => %w(-m),
23
+ :default => 'rake db:migrate',
25
24
  :desc => "Run migrations via [MIGRATE], defaults to 'rake db:migrate'"
26
25
  method_option :install_eysd, :type => :boolean, :aliases => %(-s),
27
26
  :desc => "Force remote install of eysd"
@@ -39,7 +38,7 @@ module EY
39
38
  invalid_branch = default_branch && (branch != default_branch) && !options[:force]
40
39
  raise BranchMismatch.new(default_branch, branch) if invalid_branch
41
40
 
42
- if env_name
41
+ if env_name && app.environments
43
42
  env = app.environments.find{|e| e.name == env_name }
44
43
  else
45
44
  env = app.environments.first
@@ -84,12 +83,8 @@ module EY
84
83
  deploy_cmd << " --config '#{escaped_config_option}'"
85
84
  end
86
85
 
87
- if options.key(:migrate)
88
- if options[:migrate]
89
- deploy_cmd << " --migrate='#{options[:migrate]}'"
90
- else
91
- deploy_cmd << " --no-migrate"
92
- end
86
+ if options['migrate']
87
+ deploy_cmd << " --migrate='#{options[:migrate]}'"
93
88
  end
94
89
 
95
90
  EY.ui.info "Running deploy on server..."
@@ -129,6 +124,30 @@ module EY
129
124
  end
130
125
  end
131
126
 
127
+ desc "logs environment", "Retrieve the latest logs for an enviornment"
128
+ def logs(environment)
129
+ env = account.environment_named(environment)
130
+
131
+ if env.nil?
132
+ raise EnvironmentError, "Environment '#{env_name}' can't be found\n" +
133
+ "You can create it at #{EY.config.endpoint}"
134
+ else
135
+ env.logs.each do |log|
136
+ EY.ui.info log.instance_name
137
+
138
+ if log.main
139
+ EY.ui.info "Main logs:"
140
+ EY.ui.say log.main
141
+ end
142
+
143
+ if log.custom
144
+ EY.ui.info "Custom logs:"
145
+ EY.ui.say log.custom
146
+ end
147
+ end # logs_for_environment(env).each
148
+ end # env.nil?
149
+ end
150
+
132
151
  desc "version", "Print the version of the engineyard gem"
133
152
  def version
134
153
  EY.ui.say %{engineyard version #{EY::VERSION}}
@@ -167,7 +186,11 @@ module EY
167
186
  cmd << %{ &> /dev/null} unless output
168
187
  EY.ui.debug(cmd)
169
188
  puts cmd if output
170
- system cmd unless ENV["NO_SSH"]
189
+ unless ENV["NO_SSH"]
190
+ system cmd
191
+ else
192
+ true
193
+ end
171
194
  end
172
195
 
173
196
  end # CLI
@@ -45,7 +45,7 @@ module EY
45
45
 
46
46
  def ask(message, password = false)
47
47
  begin
48
- EY.library 'highline'
48
+ require 'highline'
49
49
  @hl ||= HighLine.new($stdin)
50
50
  if not $stdin.tty?
51
51
  @hl.ask(message)
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe EY::API do
4
4
  it "gets the api token from ~/.eyrc if possible" do
5
- write_config({"api_token" => "asdf"}, '~/.eyrc')
5
+ write_yaml({"api_token" => "asdf"}, '~/.eyrc')
6
6
  EY::API.new.should == EY::API.new("asdf")
7
7
  end
8
8
 
@@ -17,7 +17,7 @@ describe EY::API do
17
17
  end
18
18
 
19
19
  it "puts the api token into .eyrc" do
20
- load_config('~/.eyrc')["api_token"].should == "asdf"
20
+ read_yaml('~/.eyrc')["api_token"].should == "asdf"
21
21
  end
22
22
  end
23
23
 
@@ -25,18 +25,18 @@ describe EY::API do
25
25
  context "without a custom endpoint" do
26
26
  it "saves the api token at the root of the data" do
27
27
  EY::API.save_token("asdf")
28
- load_config('~/.eyrc')["api_token"].should == "asdf"
28
+ read_yaml('~/.eyrc')["api_token"].should == "asdf"
29
29
  end
30
30
  end
31
31
 
32
32
  context "with a custom endpoint" do
33
- before do
34
- write_config({"endpoint" => "http://localhost/"}, 'ey.yml')
33
+ before(:each) do
34
+ write_yaml({"endpoint" => "http://localhost/"}, 'ey.yml')
35
35
  EY::API.save_token("asdf")
36
36
  end
37
37
 
38
38
  it "saves the api token" do
39
- load_config('~/.eyrc').should == {"http://localhost/" => {"api_token" => "asdf"}}
39
+ read_yaml('~/.eyrc').should == {"http://localhost/" => {"api_token" => "asdf"}}
40
40
  end
41
41
 
42
42
  it "reads the api token" do
@@ -22,7 +22,7 @@ describe EY::CLI::API do
22
22
  before(:each) do
23
23
  FakeWeb.register_uri(:post, "https://cloud.engineyard.com/api/v2/authenticate", :body => %|{"api_token": "asdf"}|)
24
24
 
25
- capture_stdout("\n\n") do
25
+ capture_stdio("\n\n") do
26
26
  @token = EY::CLI::API.new
27
27
  end
28
28
  end
@@ -4,12 +4,12 @@ require 'uri'
4
4
  describe EY::Config do
5
5
  describe "environments" do
6
6
  it "get loaded from the config file" do
7
- write_config("environments" => {"production" => {"default" => true}})
7
+ write_yaml("environments" => {"production" => {"default" => true}})
8
8
  EY::Config.new.environments["production"]["default"].should be_true
9
9
  end
10
10
 
11
11
  it "are present when the config file has no environments key" do
12
- write_config("endpoint" => "http://localhost/")
12
+ write_yaml("endpoint" => "http://localhost/")
13
13
  EY::Config.new.environments.should == {}
14
14
  end
15
15
  end
@@ -20,36 +20,36 @@ describe EY::Config do
20
20
  end
21
21
 
22
22
  it "gets loaded from the config file" do
23
- write_config("endpoint" => "http://localhost/")
23
+ write_yaml("endpoint" => "http://localhost/")
24
24
  EY::Config.new.endpoint.should == URI.parse("http://localhost/")
25
25
  end
26
26
 
27
27
  it "raises on an invalid endpoint" do
28
- write_config("endpoint" => "non/absolute")
28
+ write_yaml("endpoint" => "non/absolute")
29
29
  lambda { EY::Config.new.endpoint }.
30
30
  should raise_error(EY::Config::ConfigurationError)
31
31
  end
32
32
  end
33
33
 
34
34
  it "provides default_endpoint?" do
35
- write_config("endpoint" => "http://localhost/")
35
+ write_yaml("endpoint" => "http://localhost/")
36
36
  EY::Config.new.default_endpoint?.should_not be_true
37
37
  end
38
38
 
39
39
  describe "files" do
40
40
  it "looks for config/ey.yml" do
41
- write_config({"endpoint" => "http://something/"}, "ey.yml")
42
- write_config({"endpoint" => "http://localhost/"}, "config/ey.yml")
41
+ write_yaml({"endpoint" => "http://something/"}, "ey.yml")
42
+ write_yaml({"endpoint" => "http://localhost/"}, "config/ey.yml")
43
43
  EY::Config.new.endpoint.should == URI.parse("http://localhost/")
44
44
  end
45
45
 
46
46
  it "looks for ey.yml" do
47
- write_config({"endpoint" => "http://foo/"}, "ey.yml")
47
+ write_yaml({"endpoint" => "http://foo/"}, "ey.yml")
48
48
  EY::Config.new.endpoint.should == URI.parse("http://foo/")
49
49
  end
50
50
 
51
51
  it "looks for the file given" do
52
- write_config({"endpoint" => "http://bar/"}, "summat.yml")
52
+ write_yaml({"endpoint" => "http://bar/"}, "summat.yml")
53
53
  EY::Config.new("summat.yml").endpoint.should == URI.parse("http://bar/")
54
54
  end
55
55
  end
@@ -2,14 +2,16 @@ require 'spec_helper'
2
2
 
3
3
  describe "ey deploy" do
4
4
  before(:all) do
5
- ENV['EYRC'] = "/tmp/eyrc"
6
- ENV['CLOUD_URL'] = "http://localhost:4000"
7
5
  FakeFS.deactivate!
6
+ ENV['EYRC'] = "/tmp/eyrc"
7
+ ENV['CLOUD_URL'] = EY.fake_awsm
8
+ FakeWeb.allow_net_connect = true
8
9
  end
9
10
 
10
11
  after(:all) do
11
12
  ENV['CLOUD_URL'] = nil
12
13
  FakeFS.activate!
14
+ FakeWeb.allow_net_connect = false
13
15
  end
14
16
 
15
17
  describe "without an eyrc file" do
@@ -18,10 +20,11 @@ describe "ey deploy" do
18
20
  end
19
21
 
20
22
  it "prompts for authentication" do
21
- ey("deploy") do |input|
22
- input.puts("aarko@engineyard.com")
23
- input.puts("reversal")
23
+ ey("deploy", :hide_err => true) do |input|
24
+ input.puts("test@test.test")
25
+ input.puts("test")
24
26
  end
27
+
25
28
  @out.should include("We need to fetch your API token, please login")
26
29
  @out.should include("Email:")
27
30
  @out.should include("Password:")
@@ -36,30 +39,46 @@ describe "ey deploy" do
36
39
  end
37
40
 
38
41
  it "complains when there is no app" do
39
- return pending "this should not hit a live app"
42
+ api_scenario "empty"
40
43
  ey "deploy", :hide_err => true
41
- @err.should include %|no application configured|
44
+ @err.should include(%|no application configured|)
42
45
  end
43
46
 
44
- it "complains when there is no environment" do
45
- return pending
46
- api_scenario :no_environments
47
- ey "deploy"
48
- @out.should match(/no environment/i)
47
+ it "complains when there is no environment for the app" do
48
+ api_scenario "one app, one environment, not linked"
49
+ ey "deploy giblets master", :hide_err => true
50
+ @err.should match(/doesn't run this application/i)
49
51
  end
50
52
 
51
53
  it "runs when environment is known" do
52
- return pending
53
- api_scenario :one_environment
54
- ey "deploy"
55
- @out.should match(/deploying/i)
54
+ api_scenario "one app, one environment"
55
+ ey "deploy", :hide_err => true
56
+ @out.should match(/running deploy/i)
57
+ @err.should be_empty
56
58
  end
57
59
 
58
60
  it "complains when environment is ambiguous" do
59
- return pending
60
- api_scenario :two_environments
61
- ey "deploy"
62
- @out.should match(/was called incorrectly/i)
61
+ api_scenario "one app, two environments"
62
+ ey "deploy", :hide_err => true
63
+ @err.should match(/was called incorrectly/i)
64
+ end
65
+
66
+ context "migration command" do
67
+ before(:each) do
68
+ api_scenario "one app, one environment"
69
+ end
70
+
71
+ it "defaults to 'rake db:migrate'" do
72
+ ey "deploy"
73
+ @ssh_commands.size.should == 1
74
+ @ssh_commands.first.should =~ /--migrate='rake db:migrate'/
75
+ end
76
+
77
+ it "can be disabled with --no-migrate" do
78
+ ey "deploy --no-migrate"
79
+ @ssh_commands.size.should == 1
80
+ @ssh_commands.first.should_not =~ /--migrate/
81
+ end
63
82
  end
64
83
  end
65
- end
84
+ end