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