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 +11 -9
- data/lib/shelly/cli/main.rb +52 -22
- data/lib/shelly/cli/users.rb +30 -0
- data/lib/shelly/client.rb +17 -2
- data/lib/shelly/helpers.rb +4 -3
- data/lib/shelly/templates/Cloudfile.erb +4 -1
- data/lib/shelly/user.rb +6 -1
- data/lib/shelly/version.rb +1 -1
- data/shelly.gemspec +2 -0
- data/spec/shelly/app_spec.rb +18 -26
- data/spec/shelly/cli/main_spec.rb +51 -59
- data/spec/shelly/cli/users_spec.rb +66 -0
- data/spec/shelly/client_spec.rb +27 -2
- data/spec/shelly/user_spec.rb +7 -0
- data/spec/spec_helper.rb +4 -0
- metadata +38 -24
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
|
15
|
-
system("git remote rm #{purpose}")
|
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.
|
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
|
67
|
+
system("git status > /dev/null 2>&1")
|
67
68
|
end
|
68
69
|
end
|
69
70
|
end
|
71
|
+
|
data/lib/shelly/cli/main.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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|
|
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
|
-
|
67
|
-
|
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|
|
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
|
-
|
142
|
+
say_error "Password and password confirmation don't match, please type them again"
|
114
143
|
else
|
115
|
-
|
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 =
|
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
|
+
|
data/lib/shelly/helpers.rb
CHANGED
@@ -11,9 +11,10 @@ module Shelly
|
|
11
11
|
say "\n"
|
12
12
|
end
|
13
13
|
|
14
|
-
def say_error(message)
|
15
|
-
|
16
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/shelly/version.rb
CHANGED
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
|
+
|
data/spec/shelly/app_spec.rb
CHANGED
@@ -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
|
38
|
-
@app.should_receive(:system).with("git remote
|
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
|
43
|
-
@app.should_receive(:system).with("git remote
|
44
|
-
@app.add_git_remote
|
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.
|
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
|
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
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
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
|
+
|
data/spec/shelly/client_spec.rb
CHANGED
@@ -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
|
-
|
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
|
+
|
data/spec/shelly/user_spec.rb
CHANGED
@@ -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.
|
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-
|
12
|
+
date: 2011-10-25 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
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: *
|
24
|
+
version_requirements: *2153577060
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rake
|
27
|
-
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: *
|
35
|
+
version_requirements: *2153576460
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: guard
|
38
|
-
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: *
|
46
|
+
version_requirements: *2153575660
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: guard-rspec
|
49
|
-
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: *
|
57
|
+
version_requirements: *2153575240
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: growl_notify
|
60
|
-
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: *
|
68
|
+
version_requirements: *2153574660
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rb-fsevent
|
71
|
-
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: *
|
79
|
+
version_requirements: *2153574240
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: fakefs
|
82
|
-
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: *
|
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: &
|
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: *
|
112
|
+
version_requirements: *2153572920
|
102
113
|
- !ruby/object:Gem::Dependency
|
103
114
|
name: rest-client
|
104
|
-
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: *
|
123
|
+
version_requirements: *2153572500
|
113
124
|
- !ruby/object:Gem::Dependency
|
114
125
|
name: json
|
115
|
-
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: *
|
134
|
+
version_requirements: *2153572080
|
124
135
|
- !ruby/object:Gem::Dependency
|
125
136
|
name: launchy
|
126
|
-
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: *
|
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
|