shelly 0.0.37 → 0.0.38

Sign up to get free protection for your applications and to get access to all the features.
data/lib/shelly.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  require "rubygems"
2
2
  require "core_ext/object"
3
3
  require "core_ext/hash"
4
+
5
+ require "yaml"
6
+ YAML::ENGINE.yamler = "syck"
7
+
4
8
  require "shelly/helpers"
5
9
  require "shelly/model"
6
10
 
data/lib/shelly/app.rb CHANGED
@@ -1,14 +1,13 @@
1
1
  require 'erb'
2
2
  require 'launchy'
3
+ require "shelly/backup"
3
4
 
4
5
  module Shelly
5
6
  class App < Model
6
7
  DATABASE_KINDS = %w(postgresql mongodb redis none)
7
8
 
8
9
  attr_accessor :code_name, :databases, :ruby_version, :environment,
9
- :git_url, :domains
10
-
11
- autoload :Backup, "shelly/backup"
10
+ :git_url, :domains, :web_server_ip, :mail_server_ip
12
11
 
13
12
  def initialize(code_name = nil)
14
13
  self.code_name = code_name
@@ -99,10 +98,6 @@ module Shelly
99
98
  File.basename(Dir.pwd)
100
99
  end
101
100
 
102
- def ips
103
- shelly.app_ips(code_name)
104
- end
105
-
106
101
  def users
107
102
  shelly.app_users(code_name)
108
103
  end
@@ -135,6 +130,18 @@ module Shelly
135
130
  shelly.app_delete_config(code_name, path)
136
131
  end
137
132
 
133
+ def attributes
134
+ @attributes ||= shelly.app(code_name)
135
+ end
136
+
137
+ def web_server_ip
138
+ attributes["web_server_ip"]
139
+ end
140
+
141
+ def mail_server_ip
142
+ attributes["mail_server_ip"]
143
+ end
144
+
138
145
  def open_billing_page
139
146
  url = "#{shelly.shellyapp_url}/login?api_key=#{current_user.token}&return_to=/apps/#{code_name}/edit_billing"
140
147
  Launchy.open(url)
@@ -8,12 +8,13 @@ module Shelly
8
8
  namespace :backup
9
9
  include Helpers
10
10
 
11
+ before_hook :logged_in?, :only => [:list, :get, :create]
12
+ before_hook :cloudfile_present?, :only => [:list]
13
+
11
14
  desc "list", "List database backups"
12
15
  method_option :cloud, :type => :string, :aliases => "-c",
13
16
  :desc => "Specify which cloud to list backups for"
14
17
  def list
15
- logged_in?
16
- say_error "No Cloudfile found" unless Cloudfile.present?
17
18
  multiple_clouds(options[:cloud], "backup list", "Select cloud to view database backups for using:")
18
19
  backups = @app.database_backups
19
20
  if backups.present?
@@ -60,7 +61,6 @@ module Shelly
60
61
  method_option :cloud, :type => :string, :aliases => "-c",
61
62
  :desc => "Specify which cloud to create database snapshot for"
62
63
  def create(kind = nil)
63
- logged_in?
64
64
  multiple_clouds(options[:cloud], "backup create", "Select cloud to create snapshot of database")
65
65
  @app.request_backup(kind)
66
66
  say "Backup requested. It can take up to several minutes for" +
@@ -1,5 +1,6 @@
1
1
  require "shelly"
2
2
  require "thor"
3
+ require "thor/thor"
3
4
  require "thor/group"
4
5
  require "thor/options"
5
6
  require "thor/arguments"
@@ -8,6 +9,7 @@ require "thor/basic"
8
9
  module Shelly
9
10
  module CLI
10
11
  class Command < Thor
12
+ include Helpers
11
13
  class_option :debug, :type => :boolean, :desc => "Show debug information"
12
14
  end
13
15
  end
@@ -6,10 +6,11 @@ module Shelly
6
6
  include Thor::Actions
7
7
  include Helpers
8
8
 
9
+ before_hook :logged_in?, :only => [:list, :show, :create, :edit, :delete]
10
+ before_hook :cloudfile_present?, :only => [:list, :show, :create, :edit, :delete]
11
+
9
12
  desc "list", "List configuration files"
10
13
  def list
11
- logged_in?
12
- say_error "No Cloudfile found" unless Cloudfile.present?
13
14
  cloudfile = Cloudfile.new
14
15
  cloudfile.clouds.each do |cloud|
15
16
  @app = App.new(cloud)
@@ -46,8 +47,6 @@ module Shelly
46
47
  :desc => "Specify which cloud to show configuration file for"
47
48
  desc "show PATH", "View configuration file"
48
49
  def show(path = nil)
49
- logged_in?
50
- say_error "No Cloudfile found" unless Cloudfile.present?
51
50
  say_error "No configuration file specified" unless path
52
51
  multiple_clouds(options[:cloud], "show #{path}", "Specify cloud using:")
53
52
  config = @app.config(path)
@@ -61,11 +60,9 @@ module Shelly
61
60
 
62
61
  map "new" => :create
63
62
  method_option :cloud, :type => :string, :aliases => "-c",
64
- :desc => "Specify for which cloud create configuration file"
63
+ :desc => "Specify which cloud to create configuration file for"
65
64
  desc "create PATH", "Create configuration file"
66
65
  def create(path = nil)
67
- logged_in?
68
- say_error "No Cloudfile found" unless Cloudfile.present?
69
66
  say_error "No path specified" unless path
70
67
  output = open_editor(path)
71
68
  multiple_clouds(options[:cloud], "create #{path}", "Specify cloud using:")
@@ -84,11 +81,9 @@ module Shelly
84
81
 
85
82
  map "update" => :edit
86
83
  method_option :cloud, :type => :string, :aliases => "-c",
87
- :desc => "Specify for which cloud edit configuration file"
84
+ :desc => "Specify which cloud to edit configuration file for"
88
85
  desc "edit PATH", "Edit configuration file"
89
86
  def edit(path = nil)
90
- logged_in?
91
- say_error "No Cloudfile found" unless Cloudfile.present?
92
87
  say_error "No configuration file specified" unless path
93
88
  multiple_clouds(options[:cloud], "edit #{path}", "Specify cloud using:")
94
89
  config = @app.config(path)
@@ -107,15 +102,13 @@ module Shelly
107
102
  end
108
103
 
109
104
  method_option :cloud, :type => :string, :aliases => "-c",
110
- :desc => "Specify for which cloud delete configuration file"
105
+ :desc => "Specify for which cloud to delete configuration file for"
111
106
  desc "delete PATH", "Delete configuration file"
112
107
  def delete(path = nil)
113
- logged_in?
114
- say_error "No Cloudfile found" unless Cloudfile.present?
115
108
  say_error "No configuration file specified" unless path
116
109
  multiple_clouds(options[:cloud], "delete #{path}", "Specify cloud using:")
117
- answer = ask("Are you sure you want to delete 'path' [y/n]: ")
118
- if answer =~ /yes|YES|y|Y/
110
+ answer = yes?("Are you sure you want to delete 'path' (yes/no): ")
111
+ if answer
119
112
  @app.delete_config(path)
120
113
  say "File deleted, redeploy your cloud to make changes", :green
121
114
  else
@@ -7,12 +7,13 @@ module Shelly
7
7
  namespace :deploys
8
8
  include Helpers
9
9
 
10
+ before_hook :logged_in?, :only => [:list, :show]
11
+ before_hook :cloudfile_present?, :only => [:list, :show]
12
+
10
13
  desc "list", "Lists deploy logs"
11
14
  method_option :cloud, :type => :string, :aliases => "-c",
12
15
  :desc => "Specify which cloud to show deploy logs for"
13
16
  def list
14
- logged_in?
15
- say_error "No Cloudfile found" unless Cloudfile.present?
16
17
  multiple_clouds(options[:cloud], "deploys list", "Select cloud to view deploy logs using:")
17
18
  logs = @app.deploy_logs
18
19
  unless logs.empty?
@@ -35,7 +36,6 @@ module Shelly
35
36
  method_option :cloud, :type => :string, :aliases => "-c",
36
37
  :desc => "Specify which cloud to show deploy logs for"
37
38
  def show(log = nil)
38
- say_error "No Cloudfile found" unless Cloudfile.present?
39
39
  specify_log(log)
40
40
  multiple_clouds(options[:cloud], "deploys show #{log}", "Select log and cloud to view deploy logs using:")
41
41
  content = @app.deploy_log(log)
@@ -8,13 +8,17 @@ module Shelly
8
8
  module CLI
9
9
  class Main < Command
10
10
  include Thor::Actions
11
- include Helpers
11
+
12
12
  register(User, "user", "user <command>", "Manages users using this cloud")
13
13
  register(Backup, "backup", "backup <command>", "Manages database backups from this cloud")
14
14
  register(Deploys, "deploys", "deploys <command>", "View cloud deploy logs")
15
15
  register(Config, "config", "config <command>", "Manages cloud configuration files")
16
16
  check_unknown_options!
17
17
 
18
+ before_hook :logged_in?, :only => [:add, :list, :start, :stop, :logs, :delete]
19
+ before_hook :inside_git_repository?, :only => [:add, :ip]
20
+ before_hook :cloudfile_present?, :only => [:logs, :stop, :start, :ip]
21
+
18
22
  map %w(-v --version) => :version
19
23
  desc "version", "Displays shelly version"
20
24
  def version
@@ -89,7 +93,6 @@ module Shelly
89
93
  :desc => "Array of your domains"
90
94
  desc "add", "Adds new cloud to Shelly Cloud"
91
95
  def add
92
- say_error "Must be run inside your project git repository" unless App.inside_git_repository?
93
96
  check_options(options)
94
97
  @app = Shelly::App.new
95
98
  @app.code_name = options["code-name"] || ask_for_code_name
@@ -121,7 +124,6 @@ module Shelly
121
124
  desc "list", "Lists all your clouds"
122
125
  def list
123
126
  user = Shelly::User.new
124
- user.token
125
127
  apps = user.apps
126
128
  unless apps.empty?
127
129
  say "You have following clouds available:", :green
@@ -141,16 +143,14 @@ module Shelly
141
143
 
142
144
  desc "ip", "Lists clouds IP's"
143
145
  def ip
144
- say_error "Must be run inside your project git repository" unless App.inside_git_repository?
145
146
  say_error "No Cloudfile found" unless Cloudfile.present?
146
147
  @cloudfile = Cloudfile.new
147
148
  @cloudfile.clouds.each do |cloud|
148
149
  begin
149
150
  @app = App.new(cloud)
150
151
  say "Cloud #{cloud}:", :green
151
- ips = @app.ips
152
- print_wrapped "Web server IP: #{ips['web_server_ip']}", :ident => 2
153
- print_wrapped "Mail server IP: #{ips['mail_server_ip']}", :ident => 2
152
+ print_wrapped "Web server IP: #{@app.web_server_ip}", :ident => 2
153
+ print_wrapped "Mail server IP: #{@app.mail_server_ip}", :ident => 2
154
154
  rescue Client::APIError => e
155
155
  if e.unauthorized?
156
156
  say_error "You have no access to '#{cloud}' cloud defined in Cloudfile", :with_exit => false
@@ -165,8 +165,6 @@ module Shelly
165
165
  method_option :cloud, :type => :string, :aliases => "-c",
166
166
  :desc => "Specify which cloud to start"
167
167
  def start
168
- logged_in?
169
- say_error "No Cloudfile found" unless Cloudfile.present?
170
168
  multiple_clouds(options[:cloud], "start", "Select cloud to start using:")
171
169
  @app.start
172
170
  say "Starting cloud #{@app.code_name}. Check status with:", :green
@@ -201,8 +199,6 @@ module Shelly
201
199
  method_option :cloud, :type => :string, :aliases => "-c",
202
200
  :desc => "Specify which cloud to stop"
203
201
  def stop
204
- logged_in?
205
- say_error "No Cloudfile found" unless Cloudfile.present?
206
202
  multiple_clouds(options[:cloud], "stop", "Select cloud to stop using:")
207
203
  @app.stop
208
204
  say "Cloud '#{@app.code_name}' stopped"
@@ -216,8 +212,6 @@ module Shelly
216
212
  method_option :cloud, :type => :string, :aliases => "-c",
217
213
  :desc => "Specify which cloud to delete"
218
214
  def delete
219
- user = Shelly::User.new
220
- user.token
221
215
  multiple_clouds(options[:cloud], "delete", "Select cloud to delete using:")
222
216
  say "You are about to delete application: #{@app.code_name}."
223
217
  say "Press Control-C at any moment to cancel."
@@ -244,8 +238,6 @@ module Shelly
244
238
  :desc => "Specify which cloud to show logs for"
245
239
  def logs
246
240
  cloud = options[:cloud]
247
- logged_in?
248
- say_error "No Cloudfile found" unless Cloudfile.present?
249
241
  multiple_clouds(cloud, "logs", "Select which to show logs for using:")
250
242
  begin
251
243
  logs = @app.application_logs
@@ -6,13 +6,13 @@ module Shelly
6
6
  include Helpers
7
7
  attr_accessor :args
8
8
 
9
- def initialize(args)
9
+ def initialize(args = [])
10
10
  super()
11
11
  @args = args
12
12
  end
13
13
 
14
14
  def debug?
15
- args.include?("--debug")
15
+ args.include?("--debug") || ENV['SHELLY_DEBUG'] == "true"
16
16
  end
17
17
 
18
18
  def start
@@ -6,10 +6,12 @@ module Shelly
6
6
  namespace :user
7
7
  include Helpers
8
8
 
9
+ before_hook :logged_in?, :only => [:list, :add]
10
+ before_hook :inside_git_repository?, :only => [:list, :add]
11
+ before_hook :cloudfile_present?, :only => [:list, :add]
12
+
9
13
  desc "list", "List users with access to clouds defined in Cloudfile"
10
14
  def list
11
- say_error "Must be run inside your project git repository" unless App.inside_git_repository?
12
- say_error "No Cloudfile found" unless Cloudfile.present?
13
15
  @cloudfile = Cloudfile.new
14
16
  @cloudfile.clouds.each do |cloud|
15
17
  begin
@@ -28,8 +30,6 @@ module Shelly
28
30
 
29
31
  desc "add [EMAIL]", "Add new developer to clouds defined in Cloudfile"
30
32
  def add(email = nil)
31
- say_error "Must be run inside your project git repository" unless App.inside_git_repository?
32
- say_error "No Cloudfile found" unless Cloudfile.present?
33
33
  @cloudfile = Cloudfile.new
34
34
  @user = Shelly::User.new
35
35
  user_email = email || ask_for_email({:guess_email => false})
@@ -59,4 +59,4 @@ module Shelly
59
59
  end
60
60
  end
61
61
  end
62
- end
62
+ end
data/lib/shelly/client.rb CHANGED
@@ -115,6 +115,10 @@ module Shelly
115
115
  def apps
116
116
  get("/apps")
117
117
  end
118
+
119
+ def app(code_name)
120
+ get("/apps/#{code_name}")
121
+ end
118
122
 
119
123
  def deploy_logs(cloud)
120
124
  get("/apps/#{cloud}/deploys")
@@ -148,10 +152,6 @@ module Shelly
148
152
  get("/apps/#{cloud}/users")
149
153
  end
150
154
 
151
- def app_ips(cloud)
152
- get("/apps/#{cloud}/ips")
153
- end
154
-
155
155
  def post(path, params = {})
156
156
  request(path, :post, params)
157
157
  end
@@ -45,6 +45,14 @@ module Shelly
45
45
  exit 1 unless delete_application == "yes"
46
46
  end
47
47
 
48
+ def inside_git_repository?
49
+ say_error "Must be run inside your project git repository" unless App.inside_git_repository?
50
+ end
51
+
52
+ def cloudfile_present?
53
+ say_error "No Cloudfile found" unless Cloudfile.present?
54
+ end
55
+
48
56
  def logged_in?
49
57
  user = Shelly::User.new
50
58
  user.token
@@ -74,4 +82,3 @@ module Shelly
74
82
 
75
83
  end
76
84
  end
77
-
@@ -1,3 +1,3 @@
1
1
  module Shelly
2
- VERSION = "0.0.37"
2
+ VERSION = "0.0.38"
3
3
  end
data/lib/thor/thor.rb ADDED
@@ -0,0 +1,28 @@
1
+ class Thor
2
+ class << self
3
+ def before_hook(method, options = {})
4
+ @hook = {} unless @hook
5
+ @hook[method] = options
6
+ end
7
+
8
+ def send(*args)
9
+ if args.first == :dispatch
10
+ running_task = args[2].first
11
+ @hook.each do |method, options|
12
+ if options[:only].include?(running_task.to_sym)
13
+ new.send(method)
14
+ end
15
+ end
16
+ end
17
+ super
18
+ end
19
+
20
+ def start(given_args=ARGV, config={})
21
+ config[:shell] ||= Thor::Base.shell.new
22
+ send(:dispatch, nil, given_args.dup, nil, config)
23
+ rescue Thor::Error => e
24
+ ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message)
25
+ exit(1) if exit_on_failure?
26
+ end
27
+ end
28
+ end
data/spec/helpers.rb CHANGED
@@ -4,6 +4,10 @@ module RSpec
4
4
  InputFaker.with_fake_input(strings) { yield }
5
5
  end
6
6
 
7
+ def invoke(object, *args)
8
+ object.class.send(:start, args.map { |arg| arg.to_s })
9
+ end
10
+
7
11
  def green(string)
8
12
  "\e[32m#{string}\e[0m"
9
13
  end
@@ -24,13 +24,6 @@ describe Shelly::App do
24
24
  end
25
25
  end
26
26
 
27
- describe "#ips" do
28
- it "should get app's ips" do
29
- @client.should_receive(:app_ips).with("foo-staging")
30
- @app.ips
31
- end
32
- end
33
-
34
27
  describe "#add_git_remote" do
35
28
  before do
36
29
  @app.stub(:git_url).and_return("git@git.shellycloud.com:foo-staging.git")
@@ -64,6 +57,30 @@ describe Shelly::App do
64
57
  @app.shelly_generated_configs
65
58
  end
66
59
  end
60
+
61
+ describe "#attributes" do
62
+ before do
63
+ @response = {"web_server_ip" => "192.0.2.1", "mail_server_ip" => "192.0.2.3"}
64
+ @client.stub(:app).and_return(@response)
65
+ end
66
+
67
+ it "should fetch app attributes from API and cache them" do
68
+ @client.should_receive(:app).with("foo-staging").exactly(:once).and_return(@response)
69
+ 2.times { @app.attributes }
70
+ end
71
+
72
+ describe "#web_server_ip" do
73
+ it "should return web server ip address" do
74
+ @app.web_server_ip.should == "192.0.2.1"
75
+ end
76
+ end
77
+
78
+ describe "#mail_server_ip" do
79
+ it "should return mail server ip address" do
80
+ @app.mail_server_ip.should == "192.0.2.3"
81
+ end
82
+ end
83
+ end
67
84
 
68
85
  describe "#generate_cloudfile" do
69
86
  it "should return generated cloudfile" do