shelly 0.0.37 → 0.0.38

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