shelly 0.0.14 → 0.0.15

Sign up to get free protection for your applications and to get access to all the features.
data/lib/shelly/app.rb CHANGED
@@ -4,25 +4,22 @@ require 'launchy'
4
4
  module Shelly
5
5
  class App < Base
6
6
  DATABASE_KINDS = %w(postgresql mongodb redis none)
7
- attr_accessor :purpose, :code_name, :databases, :ruby_version, :environment, :git_url
7
+ attr_accessor :purpose, :code_name, :databases, :ruby_version, :environment, :git_url, :domains
8
8
 
9
9
  def initialize
10
10
  @ruby_version = "MRI-1.9.2"
11
11
  @environment = "production"
12
12
  end
13
13
 
14
- def add_git_remote(force = false)
15
- system("git remote rm #{purpose}") if force
14
+ def add_git_remote
15
+ system("git remote rm #{purpose} &> /dev/null")
16
16
  system("git remote add #{purpose} #{git_url}")
17
17
  end
18
18
 
19
- def remote_exists?
20
- IO.popen("git remote").read.split("\n").include?(purpose)
21
- end
22
-
23
19
  def generate_cloudfile
24
20
  @email = current_user.email
25
21
  @databases = databases
22
+ @domains = domains.nil? ? ["#{code_name}.winniecloud.com"] : domains
26
23
  template = File.read(cloudfile_template_path)
27
24
  cloudfile = ERB.new(template, 0, "%<>-")
28
25
  cloudfile.result(binding)
@@ -57,13 +54,18 @@ module Shelly
57
54
  File.basename(Dir.pwd)
58
55
  end
59
56
 
57
+ def users(apps)
58
+ shelly.app_users(apps)
59
+ end
60
+
60
61
  def open_billing_page
61
- url = "#{shelly.api_url}/apps/#{code_name}/edit_billing?api_key=#{current_user.token}"
62
+ url = "#{shelly.shellyapp_url}/login?api_key=#{current_user.token}&return_to=/apps/#{code_name}/edit_billing"
62
63
  Launchy.open(url)
63
64
  end
64
65
 
65
66
  def self.inside_git_repository?
66
- system("git status &> /dev/null")
67
+ system("git status > /dev/null 2>&1")
67
68
  end
68
69
  end
69
70
  end
71
+
@@ -1,11 +1,13 @@
1
1
  require "shelly"
2
2
  require "thor/group"
3
+ require "shelly/cli/users"
3
4
 
4
5
  module Shelly
5
6
  module CLI
6
7
  class Main < Thor
7
8
  include Thor::Actions
8
9
  include Helpers
10
+ register(Users, "users", "users <command>", "Manages users using this app")
9
11
 
10
12
  map %w(-v --version) => :version
11
13
  desc "version", "Displays shelly version"
@@ -15,8 +17,11 @@ module Shelly
15
17
 
16
18
  desc "register [EMAIL]", "Registers new user account on Shelly Cloud"
17
19
  def register(email = nil)
20
+ user = User.new
21
+ user.ssh_key_registered?
18
22
  say "Registering with email: #{email}" if email
19
- user = User.new(email || ask_for_email, ask_for_password)
23
+ user.email = (email || ask_for_email)
24
+ user.password = ask_for_password
20
25
  user.register
21
26
  if user.ssh_key_exists?
22
27
  say "Uploading your public SSH key from #{user.ssh_key_path}"
@@ -26,10 +31,14 @@ module Shelly
26
31
  rescue Client::APIError => e
27
32
  if e.validation?
28
33
  e.errors.each do |error|
29
- say "#{error.first} #{error.last}"
34
+ say_error "#{error.first} #{error.last}", :with_exit => false
30
35
  end
31
36
  exit 1
32
37
  end
38
+ rescue RestClient::Conflict
39
+ say_error "User with your ssh key already exists.", :with_exit => false
40
+ say_error "You can login using: shelly login [EMAIL]", :with_exit => false
41
+ exit 1
33
42
  end
34
43
 
35
44
  desc "login [EMAIL]", "Logins user to Shelly Cloud"
@@ -44,34 +53,40 @@ module Shelly
44
53
  say " #{app["code_name"]}"
45
54
  end
46
55
  rescue RestClient::Unauthorized
47
- say "Wrong email or password or your email is unconfirmend"
56
+ say_error "Wrong email or password", :with_exit => false
57
+ say_error "You can reset password by using link:", :with_exit => false
58
+ say_error "https://admin.winniecloud.com/users/password/new", :with_exit => false
48
59
  exit 1
49
- rescue Client::APIError
60
+ rescue Client::APIError => e
50
61
  if e.validation?
51
- e.errors.each { |error| say "#{error.first} #{error.last}" }
62
+ e.errors.each { |error| say_error "#{error.first} #{error.last}", :with_exit => false }
52
63
  exit 1
53
64
  end
54
65
  end
55
66
 
67
+ method_option :code_name, :type => :string, :aliases => "-c",
68
+ :desc => "Unique code_name of your application"
69
+ method_option :environment, :type => :string, :aliases => "-e",
70
+ :desc => "Environment that your application will be running"
71
+ method_option :databases, :type => :array, :aliases => "-d",
72
+ :banner => "#{Shelly::App::DATABASE_KINDS.join(' ')}",
73
+ :desc => "Array of databases of your choice"
74
+ method_option :domains, :type => :array,
75
+ :banner => "CODE_NAME.shellycloud.com YOUR_DOMAIN.com",
76
+ :desc => "Array of your domains"
56
77
  desc "add", "Adds new application to Shelly Cloud"
57
78
  def add
58
79
  say_error "Must be run inside your project git repository" unless App.inside_git_repository?
59
-
80
+ check_options(options)
60
81
  @app = Shelly::App.new
61
- @app.purpose = ask_for_purpose
62
- @app.code_name = ask_for_code_name
63
- @app.databases = ask_for_databases
82
+ @app.purpose = options["environment"] || ask_for_purpose
83
+ @app.code_name = options["code_name"] || ask_for_code_name
84
+ @app.databases = options["databases"] || ask_for_databases
85
+ @app.domains = options["domains"]
64
86
  @app.create
65
87
 
66
- unless @app.remote_exists?
67
- say "Adding remote #{@app.purpose} #{@app.git_url}", :green
68
- @app.add_git_remote
69
- else
70
- say "Remote #{@app.purpose} already exists"
71
- if yes?("Would you like to overwrite remote #{@app.purpose} with #{@app.git_url} (Y/N)?:")
72
- @app.add_git_remote(true)
73
- end
74
- end
88
+ say "Adding remote #{@app.purpose} #{@app.git_url}", :green
89
+ @app.add_git_remote
75
90
 
76
91
  say "Creating Cloudfile", :green
77
92
  @app.create_cloudfile
@@ -83,13 +98,27 @@ module Shelly
83
98
  info_deploying_to_shellycloud
84
99
  rescue Client::APIError => e
85
100
  if e.validation?
86
- e.errors.each { |error| say "#{error.first} #{error.last}" }
101
+ e.errors.each { |error| say_error "#{error.first} #{error.last}", :with_exit => false }
87
102
  exit 1
88
103
  end
89
104
  end
90
105
 
91
106
  # FIXME: move to helpers
92
107
  no_tasks do
108
+ def check_options(options)
109
+ unless ["environment", "code_name", "databases", "domains"].all? do |option|
110
+ options.include?(option.to_s) && options[option.to_s] != option.to_s
111
+ end && valid_databases?(options["databases"])
112
+ say "Wrong parameters. See 'shelly help add' for further information"
113
+ exit 1
114
+ end unless options.empty?
115
+ end
116
+
117
+ def valid_databases?(databases)
118
+ kinds = Shelly::App::DATABASE_KINDS
119
+ databases.all? { |kind| kinds.include?(kind) }
120
+ end
121
+
93
122
  def ask_for_email
94
123
  email_question = User.guess_email.blank? ? "Email:" : "Email (#{User.guess_email} - default):"
95
124
  email = ask(email_question)
@@ -110,9 +139,9 @@ module Shelly
110
139
  say_new_line
111
140
  if password.present?
112
141
  return password if password == password_confirmation
113
- say "Password and password confirmation don't match, please type them again"
142
+ say_error "Password and password confirmation don't match, please type them again"
114
143
  else
115
- say "Password can't be blank"
144
+ say_error "Password can't be blank"
116
145
  end
117
146
  end
118
147
  end
@@ -133,7 +162,7 @@ module Shelly
133
162
  databases = ask("Which database do you want to use #{kinds.join(", ")} (postgresql - default):")
134
163
  begin
135
164
  databases = databases.split(/[\s,]/).reject(&:blank?)
136
- valid = databases.all? { |kind| kinds.include?(kind) }
165
+ valid = valid_databases?(databases)
137
166
  break if valid
138
167
  databases = ask("Unknown database kind. Supported are: #{kinds.join(", ")}:")
139
168
  end while not valid
@@ -163,3 +192,4 @@ module Shelly
163
192
  end
164
193
  end
165
194
  end
195
+
@@ -0,0 +1,30 @@
1
+ require "yaml"
2
+ require "shelly/user"
3
+
4
+ module Shelly
5
+ module CLI
6
+ class Users < Thor
7
+ namespace :users
8
+ include Helpers
9
+
10
+ desc "list", "List users who have access to current application"
11
+ def list
12
+ say_error "Must be run inside your project git repository" unless App.inside_git_repository?
13
+ code_names = YAML.load(File.open(File.join(Dir.pwd, "Cloudfile"))).keys
14
+ @app = Shelly::App.new
15
+ response = @app.users(code_names.sort)
16
+ response.each do |app|
17
+ say "Cloud #{app['code_name']}:"
18
+ app['users'].each do |user|
19
+ say " #{user['email']} (#{user['name']})"
20
+ end
21
+ end
22
+ rescue Client::APIError => e
23
+ say e.message
24
+ exit 1
25
+ end
26
+
27
+ end
28
+ end
29
+ end
30
+
data/lib/shelly/client.rb CHANGED
@@ -30,6 +30,10 @@ module Shelly
30
30
  ENV["SHELLY_URL"] || "https://admin.winniecloud.com/apiv2"
31
31
  end
32
32
 
33
+ def shellyapp_url
34
+ get("/shellyapp")["url"]
35
+ end
36
+
33
37
  def register_user(email, password, ssh_key)
34
38
  post("/users", :user => {:email => email, :password => password, :ssh_key => ssh_key})
35
39
  end
@@ -50,6 +54,16 @@ module Shelly
50
54
  get("/apps")
51
55
  end
52
56
 
57
+ def ssh_key_available?(ssh_key)
58
+ get("/users/new", :ssh_key => ssh_key)
59
+ end
60
+
61
+ def app_users(apps)
62
+ apps.map do |app|
63
+ get("/apps/#{app}/users")
64
+ end
65
+ end
66
+
53
67
  def post(path, params = {})
54
68
  request(path, :post, params)
55
69
  end
@@ -58,8 +72,8 @@ module Shelly
58
72
  request(path, :put, params)
59
73
  end
60
74
 
61
- def get(path)
62
- request(path, :get)
75
+ def get(path, params = {})
76
+ request(path, :get, params)
63
77
  end
64
78
 
65
79
  def request(path, method, params = {})
@@ -97,3 +111,4 @@ module Shelly
97
111
  end
98
112
  end
99
113
  end
114
+
@@ -11,9 +11,10 @@ module Shelly
11
11
  say "\n"
12
12
  end
13
13
 
14
- def say_error(message)
15
- say message
16
- exit 1
14
+ def say_error(message, options = {})
15
+ options = {:with_exit => true}.merge(options)
16
+ say "\033[91m " + message + " \033[0m"
17
+ return exit 1 if options[:with_exit]
17
18
  end
18
19
  end
19
20
  end
@@ -4,7 +4,9 @@
4
4
  monitoring_email:
5
5
  - <%= @email %>
6
6
  domains:
7
- - <%= @code_name %>.winniecloud.com
7
+ <%- @domains.each do |domain| -%>
8
+ - <%= domain %>
9
+ <%- end -%>
8
10
  servers:
9
11
  app1:
10
12
  size: large
@@ -17,3 +19,4 @@
17
19
  database:
18
20
  - <%= kind %>
19
21
  <%- end -%>
22
+
data/lib/shelly/user.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Shelly
2
2
  class User < Base
3
- attr_reader :email, :password
3
+ attr_accessor :email, :password
4
4
 
5
5
  def initialize(email = nil, password = nil)
6
6
  @email = email
@@ -47,6 +47,11 @@ module Shelly
47
47
  def ssh_key_path
48
48
  File.expand_path("~/.ssh/id_rsa.pub")
49
49
  end
50
+
51
+ def ssh_key_registered?
52
+ ssh_key = File.read(ssh_key_path).strip
53
+ shelly.ssh_key_available?(ssh_key)
54
+ end
50
55
 
51
56
  def self.guess_email
52
57
  @@guess_email ||= IO.popen("git config --get user.email").read.strip
@@ -1,3 +1,3 @@
1
1
  module Shelly
2
- VERSION = "0.0.14"
2
+ VERSION = "0.0.15"
3
3
  end
data/shelly.gemspec CHANGED
@@ -21,6 +21,7 @@ Gem::Specification.new do |s|
21
21
  s.add_development_dependency "rb-fsevent"
22
22
  end
23
23
  s.add_development_dependency "fakefs"
24
+ s.add_development_dependency "fakeweb"
24
25
  s.add_runtime_dependency "thor"
25
26
  s.add_runtime_dependency "rest-client"
26
27
  s.add_runtime_dependency "json"
@@ -31,3 +32,4 @@ Gem::Specification.new do |s|
31
32
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
32
33
  s.require_paths = ["lib"]
33
34
  end
35
+
@@ -5,7 +5,7 @@ describe Shelly::App do
5
5
  before do
6
6
  FileUtils.mkdir_p("/projects/foo")
7
7
  Dir.chdir("/projects/foo")
8
- @client = mock(:api_url => "https://api.example.com")
8
+ @client = mock(:api_url => "https://api.example.com", :shellyapp_url => "http://shellyapp.example.com")
9
9
  Shelly::Client.stub(:new).and_return(@client)
10
10
  @app = Shelly::App.new
11
11
  @app.purpose = "staging"
@@ -28,20 +28,27 @@ describe Shelly::App do
28
28
  end
29
29
  end
30
30
 
31
+ describe "#users" do
32
+ it "should " do
33
+ @client.should_receive(:app_users).with(["staging-foo", "production-foo"])
34
+ @app.users(["staging-foo", "production-foo"])
35
+ end
36
+ end
37
+
31
38
  describe "#add_git_remote" do
32
39
  before do
33
40
  @app.stub(:git_url).and_return("git@git.shellycloud.com:foo-staging.git")
34
41
  @app.stub(:system)
35
42
  end
36
43
 
37
- it "should add git remote with proper name and git repository" do
38
- @app.should_receive(:system).with("git remote add staging git@git.shellycloud.com:foo-staging.git")
44
+ it "should try to remove existing git remote" do
45
+ @app.should_receive(:system).with("git remote rm staging &> /dev/null")
39
46
  @app.add_git_remote
40
47
  end
41
48
 
42
- it "should remove existing git remote first if invoked with true as first argument" do
43
- @app.should_receive(:system).with("git remote rm staging")
44
- @app.add_git_remote(true)
49
+ it "should add git remote with proper name and git repository" do
50
+ @app.should_receive(:system).with("git remote add staging git@git.shellycloud.com:foo-staging.git")
51
+ @app.add_git_remote
45
52
  end
46
53
  end
47
54
 
@@ -50,6 +57,7 @@ describe Shelly::App do
50
57
  user = mock(:email => "bob@example.com")
51
58
  @app.stub(:current_user).and_return(user)
52
59
  @app.databases = %w(postgresql mongodb)
60
+ @app.domains = %w(foo-staging.winniecloud.com foo.example.com)
53
61
  FakeFS.deactivate!
54
62
  expected = <<-config
55
63
  foo-staging:
@@ -59,6 +67,7 @@ foo-staging:
59
67
  - bob@example.com
60
68
  domains:
61
69
  - foo-staging.winniecloud.com
70
+ - foo.example.com
62
71
  servers:
63
72
  app1:
64
73
  size: large
@@ -74,7 +83,7 @@ foo-staging:
74
83
  database:
75
84
  - mongodb
76
85
  config
77
- @app.generate_cloudfile.should == expected
86
+ @app.generate_cloudfile.strip.should == expected.strip
78
87
  end
79
88
  end
80
89
 
@@ -106,7 +115,7 @@ config
106
115
  it "should open browser window" do
107
116
  user = mock(:token => "abc", :email => nil, :password => nil, :config_dir => "~/.shelly")
108
117
  @app.stub(:current_user).and_return(user)
109
- url = "#{@app.shelly.api_url}/apps/foo-staging/edit_billing?api_key=abc"
118
+ url = "#{@app.shelly.shellyapp_url}/login?api_key=abc&return_to=/apps/foo-staging/edit_billing"
110
119
  Launchy.should_receive(:open).with(url)
111
120
  @app.open_billing_page
112
121
  end
@@ -133,22 +142,5 @@ config
133
142
  @app.git_url.should == "git@git.example.com:fooo.git"
134
143
  end
135
144
  end
136
-
137
- describe "#remote_exists?" do
138
- context "remote with purpose as name exists" do
139
- it "should return true" do
140
- @app.purpose = "shelly-prod"
141
- IO.stub_chain(:popen, :read => "origin\nshelly-prod\ntest")
142
- @app.should be_remote_exists
143
- end
144
- end
145
-
146
- context "remote with purpose as name doesn't exist" do
147
- it "should return false" do
148
- @app.purpose = "shelly"
149
- IO.stub_chain(:popen, :read => "origin\nshelly-prod\ntest")
150
- @app.should_not be_remote_exists
151
- end
152
- end
153
- end
154
145
  end
146
+
@@ -28,6 +28,7 @@ Tasks:
28
28
  shelly help [TASK] # Describe available tasks or one specific task
29
29
  shelly login [EMAIL] # Logins user to Shelly Cloud
30
30
  shelly register [EMAIL] # Registers new user account on Shelly Cloud
31
+ shelly users <command> # Manages users using this app
31
32
  shelly version # Displays shelly version
32
33
  OUT
33
34
  out = IO.popen("bin/shelly").read.strip
@@ -39,6 +40,18 @@ OUT
39
40
  before do
40
41
  @client.stub(:register_user)
41
42
  @key_path = File.expand_path("~/.ssh/id_rsa.pub")
43
+ @user = Shelly::User.new
44
+ @user.stub(:ssh_key_registered?)
45
+ Shelly::User.stub(:new).and_return(@user)
46
+ end
47
+
48
+ it "should check ssh key in database" do
49
+ @user.stub(:ssh_key_registered?).and_raise(RestClient::Conflict)
50
+ $stdout.should_receive(:puts).with("\e[91m User with your ssh key already exists. \e[0m")
51
+ $stdout.should_receive(:puts).with("\e[91m You can login using: shelly login [EMAIL] \e[0m")
52
+ lambda {
53
+ @main.register
54
+ }.should raise_error(SystemExit)
42
55
  end
43
56
 
44
57
  it "should ask for email, password and password confirmation" do
@@ -76,7 +89,7 @@ OUT
76
89
  context "when user enters blank email" do
77
90
  it "should show error message and exit with 1" do
78
91
  Shelly::User.stub(:guess_email).and_return("")
79
- $stdout.should_receive(:puts).with("Email can't be blank, please try again")
92
+ $stdout.should_receive(:puts).with("\e[91m Email can't be blank, please try again \e[0m")
80
93
  lambda {
81
94
  fake_stdin(["", "bob@example.com", "only-pass", "only-pass"]) do
82
95
  @main.register
@@ -85,24 +98,6 @@ OUT
85
98
  end
86
99
  end
87
100
 
88
- context "when user enters blank password" do
89
- it "should ask for it again" do
90
- $stdout.should_receive(:puts).with("Password can't be blank")
91
- fake_stdin(["better@example.com", "", "", "secret", "secret"]) do
92
- @main.register
93
- end
94
- end
95
- end
96
-
97
- context "when user enters password and password confirmation which don't match each other" do
98
- it "should ask for them again" do
99
- $stdout.should_receive(:puts).with("Password and password confirmation don't match, please type them again")
100
- fake_stdin(["better@example.com", "secret", "sec-TYPO-ret", "secret", "secret"]) do
101
- @main.register
102
- end
103
- end
104
- end
105
-
106
101
  context "public SSH key exists" do
107
102
  it "should register with the public SSH key" do
108
103
  FileUtils.mkdir_p("~/.ssh")
@@ -139,7 +134,7 @@ OUT
139
134
  response = {"message" => "Validation Failed", "errors" => [["email", "has been already taken"]]}
140
135
  exception = Shelly::Client::APIError.new(response.to_json)
141
136
  @client.stub(:register_user).and_raise(exception)
142
- $stdout.should_receive(:puts).with("email has been already taken")
137
+ $stdout.should_receive(:puts).with("\e[91m email has been already taken \e[0m")
143
138
  lambda {
144
139
  fake_stdin(["kate@example.com", "pass", "pass"]) do
145
140
  @main.register
@@ -194,7 +189,10 @@ OUT
194
189
  it "should exit with 1 and display error message" do
195
190
  exception = RestClient::Unauthorized.new
196
191
  @client.stub(:token).and_raise(exception)
197
- $stdout.should_receive(:puts).with("Wrong email or password or your email is unconfirmend")
192
+ $stdout.should_receive(:puts).with("\e[91m Wrong email or password \e[0m")
193
+ $stdout.should_receive(:puts).with("\e[91m You can reset password by using link: \e[0m")
194
+ $stdout.should_receive(:puts).with("\e[91m https://admin.winniecloud.com/users/password/new \e[0m")
195
+
198
196
  lambda {
199
197
  fake_stdin(["megan@example.com", "secret"]) do
200
198
  @main.login
@@ -213,7 +211,6 @@ OUT
213
211
  @app.stub(:create)
214
212
  @app.stub(:generate_cloudfile).and_return("Example Cloudfile")
215
213
  @app.stub(:open_billing_page)
216
- @app.stub(:remote_exists?).and_return(false)
217
214
  @app.stub(:git_url).and_return("git@git.shellycloud.com:foooo.git")
218
215
  Shelly::App.stub(:inside_git_repository?).and_return(true)
219
216
  Shelly::App.stub(:new).and_return(@app)
@@ -221,7 +218,7 @@ OUT
221
218
 
222
219
  it "should exit with message if command run outside git repository" do
223
220
  Shelly::App.stub(:inside_git_repository?).and_return(false)
224
- $stdout.should_receive(:puts).with("Must be run inside your project git repository")
221
+ $stdout.should_receive(:puts).with("\e[91m Must be run inside your project git repository \e[0m")
225
222
  lambda {
226
223
  fake_stdin(["staging", "", ""]) do
227
224
  @main.add
@@ -237,6 +234,30 @@ OUT
237
234
  end
238
235
  end
239
236
 
237
+ context "command line options" do
238
+ context "invalid params" do
239
+ it "should show help and exit if not all options are passed" do
240
+ $stdout.should_receive(:puts).with("Wrong parameters. See 'shelly help add' for further information")
241
+ @main.options = {"code_name" => "foo"}
242
+ lambda { @main.add }.should raise_error(SystemExit)
243
+ end
244
+
245
+ it "should exit if databases are not valid" do
246
+ $stdout.should_receive(:puts).with("Wrong parameters. See 'shelly help add' for further information")
247
+ @main.options = {"code_name" => "foo", "environment" => "production", "databases" => ["not existing"], "domains" => ["foo.example.com"]}
248
+ lambda { @main.add }.should raise_error(SystemExit)
249
+ end
250
+ end
251
+
252
+ context "valid params" do
253
+ it "should create app on shelly cloud" do
254
+ @app.should_receive(:create)
255
+ @main.options = {"code_name" => "foo", "environment" => "production", "databases" => ["postgresql"], "domains" => ["foo.example.com"]}
256
+ @main.add
257
+ end
258
+ end
259
+ end
260
+
240
261
  context "when user provided empty purpose" do
241
262
  it "should use 'production' as default" do
242
263
  $stdout.should_receive(:print).with("How will you use this system (production - default,staging): ")
@@ -302,7 +323,7 @@ OUT
302
323
  response = {"message" => "Validation Failed", "errors" => [["code_name", "has been already taken"]]}
303
324
  exception = Shelly::Client::APIError.new(response.to_json)
304
325
  @app.should_receive(:create).and_raise(exception)
305
- $stdout.should_receive(:puts).with("code_name has been already taken")
326
+ $stdout.should_receive(:puts).with("\e[91m code_name has been already taken \e[0m")
306
327
  lambda {
307
328
  fake_stdin(["", "", ""]) do
308
329
  @main.add
@@ -310,41 +331,11 @@ OUT
310
331
  }.should raise_error(SystemExit)
311
332
  end
312
333
 
313
- context "git remote doesn't exist" do
314
- it "should add git remote" do
315
- $stdout.should_receive(:puts).with("\e[32mAdding remote staging git@git.shellycloud.com:foooo.git\e[0m")
316
- @app.should_receive(:add_git_remote)
317
- fake_stdin(["staging", "foooo", ""]) do
318
- @main.add
319
- end
320
- end
321
- end
322
-
323
- context "git remote exist" do
324
- before do
325
- @app.stub(:remote_exists?).and_return(true)
326
- end
327
-
328
- it "should ask user if he wants to overwrite existing git remote" do
329
- $stdout.should_receive(:puts).with("Remote staging already exists")
330
- $stdout.should_receive(:print).with("Would you like to overwrite remote staging with git@git.shellycloud.com:foooo.git (Y/N)?: ")
331
- fake_stdin(["staging", "foooo", "", "y"]) do
332
- @main.add
333
- end
334
- end
335
-
336
- it "should overwrite existing git remote on 'yes' from user" do
337
- @app.should_receive(:add_git_remote).with(true)
338
- fake_stdin(["staging", "foooo", "", "y"]) do
339
- @main.add
340
- end
341
- end
342
-
343
- it "should not overwrite existing git remote on 'no' from user" do
344
- @app.should_not_receive(:add_git_remote).with(true)
345
- fake_stdin(["staging", "foooo", "", "n"]) do
346
- @main.add
347
- end
334
+ it "should add git remote" do
335
+ $stdout.should_receive(:puts).with("\e[32mAdding remote staging git@git.shellycloud.com:foooo.git\e[0m")
336
+ @app.should_receive(:add_git_remote)
337
+ fake_stdin(["staging", "foooo", ""]) do
338
+ @main.add
348
339
  end
349
340
  end
350
341
 
@@ -386,3 +377,4 @@ OUT
386
377
  end
387
378
  end
388
379
  end
380
+
@@ -0,0 +1,66 @@
1
+ require "spec_helper"
2
+ require "shelly/cli/users"
3
+
4
+ describe Shelly::CLI::Users do
5
+ before do
6
+ FileUtils.stub(:chmod)
7
+ @users = Shelly::CLI::Users.new
8
+ @client = mock
9
+ Shelly::Client.stub(:new).and_return(@client)
10
+ Shelly::User.stub(:guess_email).and_return("")
11
+ $stdout.stub(:puts)
12
+ $stdout.stub(:print)
13
+ end
14
+
15
+ describe "#list" do
16
+ before do
17
+ FileUtils.mkdir_p("/projects/foo")
18
+ Dir.chdir("/projects/foo")
19
+ File.open("Cloudfile", 'w') {|f| f.write("foo-staging:\nfoo-production:\n") }
20
+ @app = mock
21
+ Shelly::App.stub(:new).and_return(@app)
22
+ Shelly::App.stub(:inside_git_repository?).and_return(true)
23
+ end
24
+
25
+ it "should exit with message if command run outside git repository" do
26
+ Shelly::App.stub(:inside_git_repository?).and_return(false)
27
+ $stdout.should_receive(:puts).with("\e[91m Must be run inside your project git repository \e[0m")
28
+ lambda {
29
+ @users.list
30
+ }.should raise_error(SystemExit)
31
+ end
32
+
33
+ context "on success" do
34
+ it "should receive clouds from the Cloudfile" do
35
+ @app.should_receive(:users).with(["foo-production","foo-staging"]).
36
+ and_return(response)
37
+ @users.list
38
+ end
39
+
40
+ it "should display clouds and users" do
41
+ @app.stub(:users).and_return(response)
42
+ $stdout.should_receive(:puts).with("Cloud foo-staging:")
43
+ $stdout.should_receive(:puts).with(" user@example.com (username)")
44
+ $stdout.should_receive(:puts).with("Cloud foo-production:")
45
+ $stdout.should_receive(:puts).with(" user2@example.com (username2)")
46
+ @users.list
47
+ end
48
+
49
+ def response
50
+ [{'code_name' => 'foo-staging','users' => [{'name' => 'username','email' => 'user@example.com'}]},
51
+ {'code_name' => 'foo-production','users' => [{'name' => 'username2','email' => 'user2@example.com'}]}]
52
+ end
53
+ end
54
+
55
+ context "on failure" do
56
+ it "should raise an error if user does not have to any app" do
57
+ response = {"message" => "You do not have access to this app"}
58
+ exception = Shelly::Client::APIError.new(response.to_json)
59
+ @app.stub(:users).and_raise(exception)
60
+ $stdout.should_receive(:puts).with("You do not have access to this app")
61
+ lambda { @users.list }.should raise_error(SystemExit)
62
+ end
63
+ end
64
+ end
65
+ end
66
+
@@ -35,7 +35,7 @@ describe Shelly::Client do
35
35
  before do
36
36
  ENV['SHELLY_URL'] = nil
37
37
  @client = Shelly::Client.new("bob@example.com", "secret")
38
- RestClient::Request.stub!(:execute)
38
+ @url = "https://#{CGI.escape("bob@example.com")}:secret@admin.winniecloud.com/apiv2"
39
39
  end
40
40
 
41
41
  describe "#api_url" do
@@ -54,6 +54,13 @@ describe Shelly::Client do
54
54
  end
55
55
  end
56
56
 
57
+ describe "#shellyapp_url" do
58
+ it "should sent get request" do
59
+ @client.should_receive(:get).with("/shellyapp").and_return({"url" => "shellyurl"})
60
+ @client.shellyapp_url.should == "shellyurl"
61
+ end
62
+ end
63
+
57
64
  describe "#register_user" do
58
65
  it "should send post request with login and password" do
59
66
  @client.should_receive(:post).with("/users", {:user => {:email => "test@example.com",
@@ -76,6 +83,16 @@ describe Shelly::Client do
76
83
  end
77
84
  end
78
85
 
86
+ describe "#app_users" do
87
+ it "should send post with app code_names" do
88
+ FakeWeb.register_uri(:get, @url + "/apps/staging-foo/users", :body => {:code_name => "staging-foo"}.to_json)
89
+ FakeWeb.register_uri(:get, @url + "/apps/production-foo/users", :body => {:code_name => "production-foo"}.to_json)
90
+ response = @client.app_users(["staging-foo", "production-foo"])
91
+ response.should == [{"code_name" => "staging-foo"},
92
+ {"code_name" => "production-foo"}]
93
+ end
94
+ end
95
+
79
96
  describe "#update_ssh_key" do
80
97
  it "should send put with give SSH key" do
81
98
  @client.should_receive(:put).with("/ssh_key", {:ssh_key => "abc"})
@@ -90,6 +107,13 @@ describe Shelly::Client do
90
107
  end
91
108
  end
92
109
 
110
+ describe "#ssh_key_available?" do
111
+ it "should send get request with ssh key" do
112
+ @client.should_receive(:get).with("/users/new", {:ssh_key => "ssh-key Abb"})
113
+ @client.ssh_key_available?("ssh-key Abb")
114
+ end
115
+ end
116
+
93
117
  describe "#request_parameters" do
94
118
  it "should return hash of resquest parameters" do
95
119
  expected = {
@@ -172,7 +196,7 @@ describe Shelly::Client do
172
196
 
173
197
  describe "#get" do
174
198
  it "should make GET request to given path" do
175
- @client.should_receive(:request).with("/account", :get)
199
+ @client.should_receive(:request).with("/account", :get, {})
176
200
  @client.get("/account")
177
201
  end
178
202
  end
@@ -191,3 +215,4 @@ describe Shelly::Client do
191
215
  end
192
216
  end
193
217
  end
218
+
@@ -105,6 +105,13 @@ describe Shelly::User do
105
105
  @user.should_not be_ssh_key_exists
106
106
  end
107
107
  end
108
+
109
+ describe "#ssh_key_registered?" do
110
+ it "should read and check if ssh key exists in database" do
111
+ @client.should_receive(:ssh_key_available?).with('ssh-key AAbbcc')
112
+ @user.ssh_key_registered?
113
+ end
114
+ end
108
115
 
109
116
  describe "#upload_ssh_key" do
110
117
  it "should read and upload user's public SSH key" do
data/spec/spec_helper.rb CHANGED
@@ -3,8 +3,12 @@ require "shelly"
3
3
  require "helpers"
4
4
  require "input_faker"
5
5
  require "fakefs/spec_helpers"
6
+ require "fakeweb"
7
+
8
+ FakeWeb.allow_net_connect = false
6
9
 
7
10
  RSpec.configure do |config|
8
11
  config.include RSpec::Helpers
9
12
  config.include FakeFS::SpecHelpers
10
13
  end
14
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shelly
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.14
4
+ version: 0.0.15
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-17 00:00:00.000000000Z
12
+ date: 2011-10-25 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &2153611320 !ruby/object:Gem::Requirement
16
+ requirement: &2153577060 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *2153611320
24
+ version_requirements: *2153577060
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &2153610440 !ruby/object:Gem::Requirement
27
+ requirement: &2153576460 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *2153610440
35
+ version_requirements: *2153576460
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: guard
38
- requirement: &2153609940 !ruby/object:Gem::Requirement
38
+ requirement: &2153575660 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *2153609940
46
+ version_requirements: *2153575660
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: guard-rspec
49
- requirement: &2153609480 !ruby/object:Gem::Requirement
49
+ requirement: &2153575240 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *2153609480
57
+ version_requirements: *2153575240
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: growl_notify
60
- requirement: &2153609000 !ruby/object:Gem::Requirement
60
+ requirement: &2153574660 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *2153609000
68
+ version_requirements: *2153574660
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rb-fsevent
71
- requirement: &2153608560 !ruby/object:Gem::Requirement
71
+ requirement: &2153574240 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *2153608560
79
+ version_requirements: *2153574240
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: fakefs
82
- requirement: &2153608100 !ruby/object:Gem::Requirement
82
+ requirement: &2153573820 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,21 @@ dependencies:
87
87
  version: '0'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *2153608100
90
+ version_requirements: *2153573820
91
+ - !ruby/object:Gem::Dependency
92
+ name: fakeweb
93
+ requirement: &2153573360 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *2153573360
91
102
  - !ruby/object:Gem::Dependency
92
103
  name: thor
93
- requirement: &2153607680 !ruby/object:Gem::Requirement
104
+ requirement: &2153572920 !ruby/object:Gem::Requirement
94
105
  none: false
95
106
  requirements:
96
107
  - - ! '>='
@@ -98,10 +109,10 @@ dependencies:
98
109
  version: '0'
99
110
  type: :runtime
100
111
  prerelease: false
101
- version_requirements: *2153607680
112
+ version_requirements: *2153572920
102
113
  - !ruby/object:Gem::Dependency
103
114
  name: rest-client
104
- requirement: &2153607220 !ruby/object:Gem::Requirement
115
+ requirement: &2153572500 !ruby/object:Gem::Requirement
105
116
  none: false
106
117
  requirements:
107
118
  - - ! '>='
@@ -109,10 +120,10 @@ dependencies:
109
120
  version: '0'
110
121
  type: :runtime
111
122
  prerelease: false
112
- version_requirements: *2153607220
123
+ version_requirements: *2153572500
113
124
  - !ruby/object:Gem::Dependency
114
125
  name: json
115
- requirement: &2153606680 !ruby/object:Gem::Requirement
126
+ requirement: &2153572080 !ruby/object:Gem::Requirement
116
127
  none: false
117
128
  requirements:
118
129
  - - ! '>='
@@ -120,10 +131,10 @@ dependencies:
120
131
  version: '0'
121
132
  type: :runtime
122
133
  prerelease: false
123
- version_requirements: *2153606680
134
+ version_requirements: *2153572080
124
135
  - !ruby/object:Gem::Dependency
125
136
  name: launchy
126
- requirement: &2153606160 !ruby/object:Gem::Requirement
137
+ requirement: &2153571660 !ruby/object:Gem::Requirement
127
138
  none: false
128
139
  requirements:
129
140
  - - ! '>='
@@ -131,7 +142,7 @@ dependencies:
131
142
  version: '0'
132
143
  type: :runtime
133
144
  prerelease: false
134
- version_requirements: *2153606160
145
+ version_requirements: *2153571660
135
146
  description: Tool for managing applications and clouds at shellycloud.com
136
147
  email:
137
148
  - support@shellycloud.com
@@ -152,6 +163,7 @@ files:
152
163
  - lib/shelly/app.rb
153
164
  - lib/shelly/base.rb
154
165
  - lib/shelly/cli/main.rb
166
+ - lib/shelly/cli/users.rb
155
167
  - lib/shelly/client.rb
156
168
  - lib/shelly/helpers.rb
157
169
  - lib/shelly/templates/Cloudfile.erb
@@ -163,6 +175,7 @@ files:
163
175
  - spec/shelly/app_spec.rb
164
176
  - spec/shelly/base_spec.rb
165
177
  - spec/shelly/cli/main_spec.rb
178
+ - spec/shelly/cli/users_spec.rb
166
179
  - spec/shelly/client_spec.rb
167
180
  - spec/shelly/user_spec.rb
168
181
  - spec/spec_helper.rb
@@ -196,6 +209,7 @@ test_files:
196
209
  - spec/shelly/app_spec.rb
197
210
  - spec/shelly/base_spec.rb
198
211
  - spec/shelly/cli/main_spec.rb
212
+ - spec/shelly/cli/users_spec.rb
199
213
  - spec/shelly/client_spec.rb
200
214
  - spec/shelly/user_spec.rb
201
215
  - spec/spec_helper.rb