shelly 0.0.14 → 0.0.15

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