engineyard 0.2.9 → 0.2.10

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