shelly 0.0.4 → 0.0.5
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/bin/shelly +2 -2
- data/lib/shelly.rb +1 -0
- data/lib/shelly/app.rb +39 -0
- data/lib/shelly/base.rb +13 -0
- data/lib/shelly/cli/account.rb +56 -0
- data/lib/shelly/cli/apps.rb +65 -0
- data/lib/shelly/cli/main.rb +25 -0
- data/lib/shelly/client.rb +12 -6
- data/lib/shelly/templates/Cloudfile.erb +20 -0
- data/lib/shelly/user.rb +18 -5
- data/lib/shelly/version.rb +1 -1
- data/shelly.gemspec +1 -0
- data/spec/shelly/app_spec.rb +93 -0
- data/spec/shelly/{cli_spec.rb → cli/account_spec.rb} +38 -23
- data/spec/shelly/cli/apps_spec.rb +128 -0
- data/spec/shelly/cli/main_spec.rb +37 -0
- data/spec/shelly/client_spec.rb +14 -4
- data/spec/shelly/user_spec.rb +43 -5
- data/spec/spec_helper.rb +1 -1
- metadata +32 -7
- data/lib/shelly/cli.rb +0 -46
data/bin/shelly
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
require "shelly/cli"
|
3
|
-
Shelly::CLI.start
|
2
|
+
require "shelly/cli/main"
|
3
|
+
Shelly::CLI::Main.start
|
data/lib/shelly.rb
CHANGED
data/lib/shelly/app.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'launchy'
|
3
|
+
|
4
|
+
module Shelly
|
5
|
+
class App < Base
|
6
|
+
DATABASE_KINDS = %w(postgresql mongodb redis none)
|
7
|
+
attr_accessor :purpose, :code_name, :databases
|
8
|
+
|
9
|
+
def add_git_remote
|
10
|
+
system("git remote add #{purpose} git@git.shellycloud.com:#{code_name}.git")
|
11
|
+
end
|
12
|
+
|
13
|
+
def generate_cloudfile
|
14
|
+
@email = current_user.email
|
15
|
+
@databases = databases
|
16
|
+
template = File.read("lib/shelly/templates/Cloudfile.erb")
|
17
|
+
cloudfile = ERB.new(template, 0, "%<>-")
|
18
|
+
cloudfile.result(binding)
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_cloudfile
|
22
|
+
content = generate_cloudfile
|
23
|
+
File.open(cloudfile_path, "a+") { |f| f << content }
|
24
|
+
end
|
25
|
+
|
26
|
+
def cloudfile_path
|
27
|
+
File.join(Dir.pwd, "Cloudfile")
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.guess_code_name
|
31
|
+
File.basename(Dir.pwd)
|
32
|
+
end
|
33
|
+
|
34
|
+
def open_billing_page
|
35
|
+
url = "#{shelly.api_url}/apps/#{code_name}/edit_billing?api_key=#{current_user.token}"
|
36
|
+
Launchy.open(url)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/shelly/base.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require "shelly/user"
|
2
|
+
|
3
|
+
module Shelly
|
4
|
+
module CLI
|
5
|
+
class Account < Thor
|
6
|
+
namespace :account
|
7
|
+
include Helpers
|
8
|
+
|
9
|
+
desc "register", "Registers new user account on Shelly Cloud"
|
10
|
+
def register
|
11
|
+
email = ask_for_email
|
12
|
+
password = ask_for_password
|
13
|
+
|
14
|
+
# FIXME: ask user in loop, until he enters valid values
|
15
|
+
if email.blank? or password.blank?
|
16
|
+
say "Email and password can't be blank" and exit 1
|
17
|
+
end
|
18
|
+
|
19
|
+
user = User.new(email, password)
|
20
|
+
user.register
|
21
|
+
if user.ssh_key_exists?
|
22
|
+
say "Uploading your public SSH key from #{user.ssh_key_path}"
|
23
|
+
end
|
24
|
+
say "Successfully registered!"
|
25
|
+
say "Check you mailbox for email confirmation"
|
26
|
+
rescue Client::APIError => e
|
27
|
+
if e.message == "Validation Failed"
|
28
|
+
e.errors.each { |error| say "#{error.first} #{error.last}" }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Fix for bug with displaying help for subcommands
|
33
|
+
# http://stackoverflow.com/questions/5663519/namespacing-thor-commands-in-a-standalone-ruby-executable
|
34
|
+
def self.banner(task, namespace = true, subcommand = false)
|
35
|
+
"#{basename} #{task.formatted_usage(self, true, subcommand)}"
|
36
|
+
end
|
37
|
+
|
38
|
+
# FIXME: move to helpers
|
39
|
+
no_tasks do
|
40
|
+
def ask_for_email
|
41
|
+
email_question = User.guess_email.blank? ? "Email:" : "Email (#{User.guess_email} - default):"
|
42
|
+
email = ask(email_question)
|
43
|
+
email.blank? ? User.guess_email : email
|
44
|
+
end
|
45
|
+
|
46
|
+
def ask_for_password
|
47
|
+
say "Password: "
|
48
|
+
echo_off
|
49
|
+
password = $stdin.gets.strip
|
50
|
+
echo_on
|
51
|
+
password
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "shelly/app"
|
2
|
+
|
3
|
+
module Shelly
|
4
|
+
module CLI
|
5
|
+
class Apps < Thor
|
6
|
+
namespace :apps
|
7
|
+
|
8
|
+
desc "add", "Add new application to Shelly Cloud"
|
9
|
+
def add
|
10
|
+
@app = Shelly::App.new
|
11
|
+
@app.purpose = ask_for_purpose
|
12
|
+
@app.code_name = ask_for_code_name
|
13
|
+
@app.databases = ask_for_databases
|
14
|
+
@app.add_git_remote
|
15
|
+
@app.create_cloudfile
|
16
|
+
@app.open_billing_page
|
17
|
+
|
18
|
+
info_adding_cloudfile_to_repository
|
19
|
+
info_deploying_to_shellycloud
|
20
|
+
end
|
21
|
+
|
22
|
+
no_tasks do
|
23
|
+
def ask_for_purpose
|
24
|
+
purpose = ask("How will you use this system (production - default,staging):")
|
25
|
+
purpose.blank? ? "production" : purpose
|
26
|
+
end
|
27
|
+
|
28
|
+
def ask_for_code_name
|
29
|
+
default_code_name = "#{Shelly::App.guess_code_name}-#{@app.purpose}"
|
30
|
+
code_name = ask("Application code name (#{default_code_name} - default):")
|
31
|
+
code_name.blank? ? default_code_name : code_name
|
32
|
+
end
|
33
|
+
|
34
|
+
def ask_for_databases
|
35
|
+
kinds = Shelly::App::DATABASE_KINDS
|
36
|
+
databases = ask("Which database do you want to use #{kinds.join(", ")} (postgresql - default):")
|
37
|
+
begin
|
38
|
+
databases = databases.split(/[\s,]/)
|
39
|
+
valid = databases.all? { |kind| kinds.include?(kind) }
|
40
|
+
break if valid
|
41
|
+
databases = ask("Unknown database kind. Supported are: #{kinds.join(", ")}:")
|
42
|
+
end while not valid
|
43
|
+
|
44
|
+
databases.empty? ? ["postgresql"] : databases
|
45
|
+
end
|
46
|
+
|
47
|
+
def info_adding_cloudfile_to_repository
|
48
|
+
say("Project is now configured for use with Shell Cloud:")
|
49
|
+
say("You can review changes using")
|
50
|
+
say(" git diff")
|
51
|
+
end
|
52
|
+
|
53
|
+
def info_deploying_to_shellycloud
|
54
|
+
say("When you make sure all settings are correct please issue following commands:")
|
55
|
+
say(" git add .")
|
56
|
+
say(' git commit -m "Application added to Shelly Cloud"')
|
57
|
+
say(" git push")
|
58
|
+
say("Deploy to #{@app.purpose} using:")
|
59
|
+
say(" git push #{@app.purpose} master")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "shelly"
|
2
|
+
require "thor/group"
|
3
|
+
require "shelly/cli/account"
|
4
|
+
require "shelly/cli/apps"
|
5
|
+
|
6
|
+
module Shelly
|
7
|
+
module CLI
|
8
|
+
class Main < Thor
|
9
|
+
include Thor::Actions
|
10
|
+
register(Account, "account", "account <command>", "Manages your account")
|
11
|
+
register(Apps, "apps", "apps <command>", "Manages your applications")
|
12
|
+
|
13
|
+
map %w(-v --version) => :version
|
14
|
+
desc "version", "Displays shelly version"
|
15
|
+
def version
|
16
|
+
say "shelly version #{Shelly::VERSION}"
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "register", "Registers new user account on Shelly Cloud"
|
20
|
+
def register
|
21
|
+
invoke 'account:register'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/shelly/client.rb
CHANGED
@@ -28,8 +28,12 @@ module Shelly
|
|
28
28
|
ENV["SHELLY_URL"] || "https://admin.winniecloud.com/apiv2"
|
29
29
|
end
|
30
30
|
|
31
|
-
def register_user(email, password)
|
32
|
-
post(
|
31
|
+
def register_user(email, password, ssh_key)
|
32
|
+
post("/users", :user => {:email => email, :password => password, :ssh_key => ssh_key})
|
33
|
+
end
|
34
|
+
|
35
|
+
def token
|
36
|
+
get("/token")
|
33
37
|
end
|
34
38
|
|
35
39
|
def post(path, params = {})
|
@@ -53,14 +57,16 @@ module Shelly
|
|
53
57
|
"shelly-version" => Shelly::VERSION}
|
54
58
|
end
|
55
59
|
|
60
|
+
def http_basic_auth_options
|
61
|
+
@email ? {:username => @email, :password => @password} : {}
|
62
|
+
end
|
63
|
+
|
56
64
|
def request_parameters(path, method, params = {})
|
57
|
-
unless @email.blank? or @password.blank?
|
58
|
-
params.merge!(:email => @email, :password => @password)
|
59
|
-
end
|
60
65
|
{:method => method,
|
61
66
|
:url => "#{api_url}#{path}",
|
62
67
|
:headers => headers,
|
63
|
-
:payload => params.to_json
|
68
|
+
:payload => params.to_json
|
69
|
+
}.merge(http_basic_auth_options)
|
64
70
|
end
|
65
71
|
|
66
72
|
def process_response(response)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<%= @code_name %>:
|
2
|
+
ruby: 1.9.2
|
3
|
+
monitoring_email:
|
4
|
+
- <%= @email %>
|
5
|
+
domains:
|
6
|
+
- <%= @code_name %>.winniecloud.com
|
7
|
+
servers:
|
8
|
+
app1:
|
9
|
+
size: large
|
10
|
+
web:
|
11
|
+
type: thin
|
12
|
+
count: 3
|
13
|
+
clock:
|
14
|
+
type: cron
|
15
|
+
<%- @databases.each_with_index do |kind, index| -%>
|
16
|
+
<%= "#{kind}#{index}" %>:
|
17
|
+
size: large
|
18
|
+
database:
|
19
|
+
type: <%= kind %>
|
20
|
+
<%- end -%>
|
data/lib/shelly/user.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Shelly
|
2
|
-
class User
|
2
|
+
class User < Base
|
3
3
|
attr_reader :email, :password
|
4
4
|
def initialize(email = nil, password = nil)
|
5
5
|
@email = email
|
@@ -7,16 +7,17 @@ module Shelly
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def register
|
10
|
-
|
11
|
-
|
10
|
+
ssh_key = File.read(ssh_key_path) if ssh_key_exists?
|
11
|
+
shelly.register_user(email, password, ssh_key)
|
12
12
|
save_credentials
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
|
15
|
+
def token
|
16
|
+
shelly.token["token"]
|
17
17
|
end
|
18
18
|
|
19
19
|
def load_credentials
|
20
|
+
return unless credentials_exists?
|
20
21
|
@email, @password = File.read(credentials_path).split("\n")
|
21
22
|
end
|
22
23
|
|
@@ -26,6 +27,18 @@ module Shelly
|
|
26
27
|
set_credentials_permissions
|
27
28
|
end
|
28
29
|
|
30
|
+
def ssh_key_exists?
|
31
|
+
File.exists?(ssh_key_path)
|
32
|
+
end
|
33
|
+
|
34
|
+
def ssh_key_path
|
35
|
+
File.expand_path("~/.ssh/id_rsa.pub")
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.guess_email
|
39
|
+
@@guess_email ||= IO.popen("git config --get user.email").read.strip
|
40
|
+
end
|
41
|
+
|
29
42
|
protected
|
30
43
|
def config_dir
|
31
44
|
File.expand_path("~/.shelly")
|
data/lib/shelly/version.rb
CHANGED
data/shelly.gemspec
CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |s|
|
|
24
24
|
s.add_runtime_dependency "thor"
|
25
25
|
s.add_runtime_dependency "rest-client"
|
26
26
|
s.add_runtime_dependency "json"
|
27
|
+
s.add_runtime_dependency "launchy"
|
27
28
|
|
28
29
|
s.files = `git ls-files`.split("\n")
|
29
30
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "shelly/app"
|
3
|
+
|
4
|
+
describe Shelly::App do
|
5
|
+
before do
|
6
|
+
FileUtils.mkdir_p("/projects/foo")
|
7
|
+
Dir.chdir("/projects/foo")
|
8
|
+
@app = Shelly::App.new
|
9
|
+
@app.purpose = "staging"
|
10
|
+
@app.code_name = "foo-staging"
|
11
|
+
end
|
12
|
+
|
13
|
+
describe ".guess_code_name" do
|
14
|
+
it "should return name of current working directory" do
|
15
|
+
Shelly::App.guess_code_name.should == "foo"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#add_git_remote" do
|
20
|
+
it "should add git remote with proper name and git repository" do
|
21
|
+
@app.should_receive(:system).with("git remote add staging git@git.shellycloud.com:foo-staging.git")
|
22
|
+
@app.add_git_remote
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#generate_cloudfile" do
|
27
|
+
it "should return generated cloudfile" do
|
28
|
+
user = mock(:email => "bob@example.com")
|
29
|
+
@app.stub(:current_user).and_return(user)
|
30
|
+
@app.databases = %w(postgresql mongodb)
|
31
|
+
FakeFS.deactivate!
|
32
|
+
expected = <<-config
|
33
|
+
foo-staging:
|
34
|
+
ruby: 1.9.2
|
35
|
+
monitoring_email:
|
36
|
+
- bob@example.com
|
37
|
+
domains:
|
38
|
+
- foo-staging.winniecloud.com
|
39
|
+
servers:
|
40
|
+
app1:
|
41
|
+
size: large
|
42
|
+
web:
|
43
|
+
type: thin
|
44
|
+
count: 3
|
45
|
+
clock:
|
46
|
+
type: cron
|
47
|
+
postgresql0:
|
48
|
+
size: large
|
49
|
+
database:
|
50
|
+
type: postgresql
|
51
|
+
mongodb1:
|
52
|
+
size: large
|
53
|
+
database:
|
54
|
+
type: mongodb
|
55
|
+
config
|
56
|
+
@app.generate_cloudfile.should == expected
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "#create_cloudfile" do
|
61
|
+
before do
|
62
|
+
@app.stub(:generate_cloudfile).and_return("foo-staging:")
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should create file if Cloudfile doesn't exist" do
|
66
|
+
File.exists?("/projects/foo/Cloudfile").should be_false
|
67
|
+
@app.create_cloudfile
|
68
|
+
File.exists?("/projects/foo/Cloudfile").should be_true
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should append content if Cloudfile exists" do
|
72
|
+
File.open("/projects/foo/Cloudfile", "w") { |f| f << "foo-production:\n" }
|
73
|
+
@app.create_cloudfile
|
74
|
+
File.read("/projects/foo/Cloudfile").strip.should == "foo-production:\nfoo-staging:"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "#cloudfile_path" do
|
79
|
+
it "should return path to Cloudfile" do
|
80
|
+
@app.cloudfile_path.should == "/projects/foo/Cloudfile"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#open_billing_page" do
|
85
|
+
it "should open browser window" do
|
86
|
+
user = mock(:token => "abc", :email => nil, :password => nil)
|
87
|
+
@app.stub(:current_user).and_return(user)
|
88
|
+
url = "#{@app.shelly.api_url}/apps/foo-staging/edit_billing?api_key=abc"
|
89
|
+
Launchy.should_receive(:open).with(url)
|
90
|
+
@app.open_billing_page
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -1,50 +1,44 @@
|
|
1
1
|
require "spec_helper"
|
2
|
-
require "shelly/cli"
|
3
|
-
require "shelly/user"
|
2
|
+
require "shelly/cli/account"
|
4
3
|
|
5
|
-
describe Shelly::CLI do
|
4
|
+
describe Shelly::CLI::Account do
|
6
5
|
before do
|
6
|
+
FileUtils.stub(:chmod)
|
7
7
|
@client = mock
|
8
|
-
@
|
8
|
+
@account = Shelly::CLI::Account.new
|
9
9
|
Shelly::Client.stub(:new).and_return(@client)
|
10
10
|
$stdout.stub(:puts)
|
11
11
|
$stdout.stub(:print)
|
12
12
|
end
|
13
13
|
|
14
|
-
describe "#version" do
|
15
|
-
it "should return shelly's version" do
|
16
|
-
$stdout.should_receive(:puts).with("shelly version #{Shelly::VERSION}")
|
17
|
-
@cli.version
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
14
|
describe "#register" do
|
22
15
|
before do
|
23
16
|
Shelly::User.stub(:guess_email).and_return("")
|
24
17
|
@client.stub(:register_user)
|
18
|
+
@key_path = File.expand_path("~/.ssh/id_rsa.pub")
|
25
19
|
end
|
26
20
|
|
27
21
|
it "should ask for email and password" do
|
28
22
|
$stdout.should_receive(:print).with("Email: ")
|
29
23
|
$stdout.should_receive(:print).with("Password: ")
|
30
24
|
fake_stdin(["better@example.com", "secret"]) do
|
31
|
-
@
|
25
|
+
@account.register
|
32
26
|
end
|
33
27
|
end
|
34
28
|
|
35
29
|
it "should suggest email and use it if user enters blank email" do
|
36
30
|
Shelly::User.stub(:guess_email).and_return("kate@example.com")
|
37
|
-
$stdout.should_receive(:print).with("Email (
|
38
|
-
@client.should_receive(:register_user).with("kate@example.com", "secret")
|
31
|
+
$stdout.should_receive(:print).with("Email (kate@example.com - default): ")
|
32
|
+
@client.should_receive(:register_user).with("kate@example.com", "secret", nil)
|
39
33
|
fake_stdin(["", "secret"]) do
|
40
|
-
@
|
34
|
+
@account.register
|
41
35
|
end
|
42
36
|
end
|
43
37
|
|
44
38
|
it "should use email provided by user" do
|
45
|
-
@client.should_receive(:register_user).with("better@example.com", "secret")
|
39
|
+
@client.should_receive(:register_user).with("better@example.com", "secret", nil)
|
46
40
|
fake_stdin(["better@example.com", "secret"]) do
|
47
|
-
@
|
41
|
+
@account.register
|
48
42
|
end
|
49
43
|
end
|
50
44
|
|
@@ -53,7 +47,7 @@ describe Shelly::CLI do
|
|
53
47
|
$stdout.should_receive(:puts).with("Email and password can't be blank")
|
54
48
|
lambda do
|
55
49
|
fake_stdin(["", "only-pass"]) do
|
56
|
-
@
|
50
|
+
@account.register
|
57
51
|
end
|
58
52
|
end.should raise_error(SystemExit)
|
59
53
|
end
|
@@ -62,17 +56,38 @@ describe Shelly::CLI do
|
|
62
56
|
$stdout.should_receive(:puts).with("Email and password can't be blank")
|
63
57
|
lambda do
|
64
58
|
fake_stdin(["better@example.com", ""]) do
|
65
|
-
@
|
59
|
+
@account.register
|
66
60
|
end
|
67
61
|
end.should raise_error(SystemExit)
|
68
62
|
end
|
69
63
|
|
64
|
+
context "ssh key exists" do
|
65
|
+
it "should register with ssh-key" do
|
66
|
+
FileUtils.mkdir_p("~/.ssh")
|
67
|
+
File.open(@key_path, "w") { |f| f << "key" }
|
68
|
+
$stdout.should_receive(:puts).with("Uploading your public SSH key from #{@key_path}")
|
69
|
+
fake_stdin(["kate@example.com", "secret"]) do
|
70
|
+
@account.register
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "ssh key doesn't exist" do
|
76
|
+
it "should register user without the ssh key" do
|
77
|
+
$stdout.should_not_receive(:puts).with("Uploading your public SSH key from #{@key_path}")
|
78
|
+
fake_stdin(["kate@example.com", "secret"]) do
|
79
|
+
@account.register
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
70
84
|
context "on successful registration" do
|
71
85
|
it "should display message about registration and email confirmation" do
|
72
86
|
@client.stub(:register_user).and_return(true)
|
73
|
-
$stdout.should_receive(:puts).with("Successfully registered
|
87
|
+
$stdout.should_receive(:puts).with("Successfully registered!")
|
88
|
+
$stdout.should_receive(:puts).with("Check you mailbox for email confirmation")
|
74
89
|
fake_stdin(["kate@example.com", "pass"]) do
|
75
|
-
@
|
90
|
+
@account.register
|
76
91
|
end
|
77
92
|
end
|
78
93
|
end
|
@@ -84,9 +99,9 @@ describe Shelly::CLI do
|
|
84
99
|
@client.stub(:register_user).and_raise(exception)
|
85
100
|
$stdout.should_receive(:puts).with("email has been already taken")
|
86
101
|
fake_stdin(["kate@example.com", "pass"]) do
|
87
|
-
@
|
102
|
+
@account.register
|
88
103
|
end
|
89
104
|
end
|
90
105
|
end
|
91
106
|
end
|
92
|
-
end
|
107
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "shelly/cli/apps"
|
3
|
+
|
4
|
+
describe Shelly::CLI::Apps do
|
5
|
+
before do
|
6
|
+
@apps = Shelly::CLI::Apps.new
|
7
|
+
$stdout.stub(:print)
|
8
|
+
$stdout.stub(:puts)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#add" do
|
12
|
+
before do
|
13
|
+
FileUtils.mkdir_p("/projects/foo")
|
14
|
+
Dir.chdir("/projects/foo")
|
15
|
+
@app = Shelly::App.new
|
16
|
+
@app.stub(:add_git_remote)
|
17
|
+
@app.stub(:generate_cloudfile).and_return("Example Cloudfile")
|
18
|
+
@app.stub(:open_billing_page)
|
19
|
+
Shelly::App.stub(:new).and_return(@app)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should ask user how he will use application" do
|
23
|
+
$stdout.should_receive(:print).with("How will you use this system (production - default,staging): ")
|
24
|
+
@app.should_receive(:purpose=).with("staging")
|
25
|
+
fake_stdin(["staging", "", ""]) do
|
26
|
+
@apps.add
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "when user provided empty purpose" do
|
31
|
+
it "should use 'production' as default" do
|
32
|
+
$stdout.should_receive(:print).with("How will you use this system (production - default,staging): ")
|
33
|
+
@app.should_receive(:purpose=).with("production")
|
34
|
+
fake_stdin(["", "", ""]) do
|
35
|
+
@apps.add
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should use code name provided by user" do
|
41
|
+
$stdout.should_receive(:print).with("How will you use this system (production - default,staging): ")
|
42
|
+
$stdout.should_receive(:print).with("Application code name (foo-staging - default): ")
|
43
|
+
@app.should_receive(:code_name=).with("mycodename")
|
44
|
+
fake_stdin(["staging", "mycodename", ""]) do
|
45
|
+
@apps.add
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when user provided empty code name" do
|
50
|
+
it "should use 'current_dirname-purpose' as default" do
|
51
|
+
$stdout.should_receive(:print).with("How will you use this system (production - default,staging): ")
|
52
|
+
$stdout.should_receive(:print).with("Application code name (foo-staging - default): ")
|
53
|
+
fake_stdin(["staging", "", ""]) do
|
54
|
+
@apps.add
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should use database provided by user (separated by comma or space)" do
|
60
|
+
$stdout.should_receive(:print).with("Which database do you want to use postgresql, mongodb, redis, none (postgresql - default): ")
|
61
|
+
@app.should_receive(:databases=).with(["postgresql", "mongodb", "redis"])
|
62
|
+
fake_stdin(["staging", "", "postgresql,mongodb redis"]) do
|
63
|
+
@apps.add
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should validate databases" do
|
68
|
+
$stdout.should_receive(:print).with("Which database do you want to use postgresql, mongodb, redis, none (postgresql - default): ")
|
69
|
+
$stdout.should_receive(:print).with("Unknown database kind. Supported are: postgresql, mongodb, redis, none: ")
|
70
|
+
fake_stdin(["staging", "", "postgresql,doesnt-exist", "none"]) do
|
71
|
+
@apps.add
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "when user provided empty database" do
|
76
|
+
it "should use 'postgresql' database as default" do
|
77
|
+
@app.should_receive(:databases=).with(["postgresql"])
|
78
|
+
fake_stdin(["staging", "", ""]) do
|
79
|
+
@apps.add
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should add git remote" do
|
85
|
+
@app.should_receive(:add_git_remote)
|
86
|
+
fake_stdin(["staging", "foooo", ""]) do
|
87
|
+
@apps.add
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should create Cloudfile" do
|
92
|
+
File.exists?("/projects/foo/Cloudfile").should be_false
|
93
|
+
fake_stdin(["staging", "foooo", ""]) do
|
94
|
+
@apps.add
|
95
|
+
end
|
96
|
+
File.read("/projects/foo/Cloudfile").should == "Example Cloudfile"
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should browser window with link to edit billing information" do
|
100
|
+
@app.should_receive(:open_billing_page)
|
101
|
+
fake_stdin(["staging", "foooo", ""]) do
|
102
|
+
@apps.add
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should display info about adding Cloudfile to repository" do
|
107
|
+
$stdout.should_receive(:puts).with("Project is now configured for use with Shell Cloud:")
|
108
|
+
$stdout.should_receive(:puts).with("You can review changes using")
|
109
|
+
$stdout.should_receive(:puts).with(" git diff")
|
110
|
+
fake_stdin(["staging", "foooo", "none"]) do
|
111
|
+
@apps.add
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should display info on how to deploy to ShellyCloud" do
|
116
|
+
$stdout.should_receive(:puts).with("When you make sure all settings are correct please issue following commands:")
|
117
|
+
$stdout.should_receive(:puts).with(" git add .")
|
118
|
+
$stdout.should_receive(:puts).with(' git commit -m "Application added to Shelly Cloud"')
|
119
|
+
$stdout.should_receive(:puts).with(" git push")
|
120
|
+
$stdout.should_receive(:puts).with("Deploy to staging using:")
|
121
|
+
$stdout.should_receive(:puts).with(" git push staging master")
|
122
|
+
fake_stdin(["staging", "foooo", "none"]) do
|
123
|
+
@apps.add
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "shelly/cli/main"
|
3
|
+
|
4
|
+
describe Shelly::CLI::Main do
|
5
|
+
before do
|
6
|
+
@main = Shelly::CLI::Main.new
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "#version" do
|
10
|
+
it "should return shelly's version" do
|
11
|
+
$stdout.should_receive(:puts).with("shelly version #{Shelly::VERSION}")
|
12
|
+
@main.version
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#register" do
|
17
|
+
it "should invoke account:register command" do
|
18
|
+
@main.should_receive(:invoke).with('account:register')
|
19
|
+
@main.register
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#help" do
|
24
|
+
it "should display available commands" do
|
25
|
+
expected = <<-OUT
|
26
|
+
Tasks:
|
27
|
+
shelly account <command> # Manages your account
|
28
|
+
shelly apps <command> # Manages your applications
|
29
|
+
shelly help [TASK] # Describe available tasks or one specific task
|
30
|
+
shelly register # Registers new user account on Shelly Cloud
|
31
|
+
shelly version # Displays shelly version
|
32
|
+
OUT
|
33
|
+
out = IO.popen("bin/shelly").read.strip
|
34
|
+
out.should == expected.strip
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/spec/shelly/client_spec.rb
CHANGED
@@ -25,8 +25,16 @@ describe Shelly::Client do
|
|
25
25
|
|
26
26
|
describe "#register_user" do
|
27
27
|
it "should send post request with login and password" do
|
28
|
-
@client.should_receive(:post).with("/users", {:user => {:email => "test@example.com",
|
29
|
-
|
28
|
+
@client.should_receive(:post).with("/users", {:user => {:email => "test@example.com",
|
29
|
+
:password => "secret", :ssh_key => "ssh-key Abb"}})
|
30
|
+
@client.register_user("test@example.com", "secret", "ssh-key Abb")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#token" do
|
35
|
+
it "should get authentication token" do
|
36
|
+
@client.should_receive(:get).with("/token")
|
37
|
+
@client.token
|
30
38
|
end
|
31
39
|
end
|
32
40
|
|
@@ -36,7 +44,9 @@ describe Shelly::Client do
|
|
36
44
|
:method => :post,
|
37
45
|
:url => "#{@client.api_url}/account",
|
38
46
|
:headers => @client.headers,
|
39
|
-
:payload => {:name => "bob"
|
47
|
+
:payload => {:name => "bob"}.to_json,
|
48
|
+
:username => "bob@example.com",
|
49
|
+
:password => "secret"
|
40
50
|
}
|
41
51
|
@client.request_parameters("/account", :post, :name => "bob").should == expected
|
42
52
|
end
|
@@ -49,7 +59,7 @@ describe Shelly::Client do
|
|
49
59
|
:headers => @client.headers,
|
50
60
|
:payload => {}.to_json
|
51
61
|
}
|
52
|
-
client.
|
62
|
+
client.request_parameters("/account", :get).should == expected
|
53
63
|
end
|
54
64
|
end
|
55
65
|
|
data/spec/shelly/user_spec.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
|
-
require "shelly/user"
|
2
1
|
require "spec_helper"
|
3
2
|
|
4
3
|
describe Shelly::User do
|
5
|
-
include FakeFS::SpecHelpers
|
6
|
-
|
7
4
|
before do
|
5
|
+
FileUtils.mkdir_p("~/.ssh")
|
6
|
+
File.open("~/.ssh/id_rsa.pub", "w") { |f| f << "ssh-key AAbbcc" }
|
8
7
|
@client = mock
|
9
8
|
Shelly::Client.stub(:new).and_return(@client)
|
10
9
|
@user = Shelly::User.new("bob@example.com", "secret")
|
@@ -25,7 +24,7 @@ describe Shelly::User do
|
|
25
24
|
end
|
26
25
|
|
27
26
|
it "should register user at Shelly Cloud" do
|
28
|
-
@client.should_receive(:register_user).with("bob@example.com", "secret")
|
27
|
+
@client.should_receive(:register_user).with("bob@example.com", "secret", "ssh-key AAbbcc")
|
29
28
|
@user.register
|
30
29
|
end
|
31
30
|
|
@@ -33,6 +32,21 @@ describe Shelly::User do
|
|
33
32
|
@user.should_receive(:save_credentials)
|
34
33
|
@user.register
|
35
34
|
end
|
35
|
+
|
36
|
+
context "when ssh key is not available" do
|
37
|
+
it "should register without it" do
|
38
|
+
FileUtils.rm_rf("~/.ssh/id_rsa.pub")
|
39
|
+
@client.should_receive(:register_user).with("bob@example.com", "secret", nil)
|
40
|
+
@user.register
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#token" do
|
46
|
+
it "should return token" do
|
47
|
+
@client.should_receive(:token).and_return({"token" => "abc"})
|
48
|
+
@user.token.should == "abc"
|
49
|
+
end
|
36
50
|
end
|
37
51
|
|
38
52
|
describe "#save_credentials" do
|
@@ -42,7 +56,7 @@ describe Shelly::User do
|
|
42
56
|
File.read("~/.shelly/credentials").should == "bob@example.com\nsecret"
|
43
57
|
end
|
44
58
|
|
45
|
-
it "should create config_dir if doesn't exist" do
|
59
|
+
it "should create config_dir if it doesn't exist" do
|
46
60
|
File.exists?("~/.shelly").should be_false
|
47
61
|
@user.save_credentials
|
48
62
|
File.exists?("~/.shelly").should be_true
|
@@ -61,10 +75,34 @@ describe Shelly::User do
|
|
61
75
|
config_dir = File.expand_path("~/.shelly")
|
62
76
|
FileUtils.mkdir_p(config_dir)
|
63
77
|
File.open(File.join(config_dir, "credentials"), "w") { |f| f << "superman@example.com\nkal-el" }
|
78
|
+
|
64
79
|
user = Shelly::User.new
|
65
80
|
user.load_credentials
|
66
81
|
user.email.should == "superman@example.com"
|
67
82
|
user.password.should == "kal-el"
|
68
83
|
end
|
84
|
+
|
85
|
+
context "credentials file doesn't exist" do
|
86
|
+
it "should return nil" do
|
87
|
+
user = Shelly::User.new
|
88
|
+
user.load_credentials.should be_nil
|
89
|
+
user.email.should be_nil
|
90
|
+
user.password.should be_nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "#ssh_key_path" do
|
96
|
+
it "should return path to public ssh key file" do
|
97
|
+
@user.ssh_key_path.should == File.expand_path("~/.ssh/id_rsa.pub")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "#ssh_key_exists?" do
|
102
|
+
it "should return true if key exists, false otherwise" do
|
103
|
+
@user.should be_ssh_key_exists
|
104
|
+
FileUtils.rm_rf("~/.ssh/id_rsa.pub")
|
105
|
+
@user.should_not be_ssh_key_exists
|
106
|
+
end
|
69
107
|
end
|
70
108
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shelly
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 5
|
10
|
+
version: 0.0.5
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Shelly Cloud team
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-09-
|
18
|
+
date: 2011-09-20 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -158,6 +158,20 @@ dependencies:
|
|
158
158
|
version: "0"
|
159
159
|
type: :runtime
|
160
160
|
version_requirements: *id010
|
161
|
+
- !ruby/object:Gem::Dependency
|
162
|
+
name: launchy
|
163
|
+
prerelease: false
|
164
|
+
requirement: &id011 !ruby/object:Gem::Requirement
|
165
|
+
none: false
|
166
|
+
requirements:
|
167
|
+
- - ">="
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
hash: 3
|
170
|
+
segments:
|
171
|
+
- 0
|
172
|
+
version: "0"
|
173
|
+
type: :runtime
|
174
|
+
version_requirements: *id011
|
161
175
|
description: Tool for managing applications and clouds at shellycloud.com
|
162
176
|
email:
|
163
177
|
- support@shellycloud.com
|
@@ -177,15 +191,23 @@ files:
|
|
177
191
|
- bin/shelly
|
178
192
|
- lib/core_ext/object.rb
|
179
193
|
- lib/shelly.rb
|
180
|
-
- lib/shelly/
|
194
|
+
- lib/shelly/app.rb
|
195
|
+
- lib/shelly/base.rb
|
196
|
+
- lib/shelly/cli/account.rb
|
197
|
+
- lib/shelly/cli/apps.rb
|
198
|
+
- lib/shelly/cli/main.rb
|
181
199
|
- lib/shelly/client.rb
|
182
200
|
- lib/shelly/helpers.rb
|
201
|
+
- lib/shelly/templates/Cloudfile.erb
|
183
202
|
- lib/shelly/user.rb
|
184
203
|
- lib/shelly/version.rb
|
185
204
|
- shelly.gemspec
|
186
205
|
- spec/helpers.rb
|
187
206
|
- spec/input_faker.rb
|
188
|
-
- spec/shelly/
|
207
|
+
- spec/shelly/app_spec.rb
|
208
|
+
- spec/shelly/cli/account_spec.rb
|
209
|
+
- spec/shelly/cli/apps_spec.rb
|
210
|
+
- spec/shelly/cli/main_spec.rb
|
189
211
|
- spec/shelly/client_spec.rb
|
190
212
|
- spec/shelly/user_spec.rb
|
191
213
|
- spec/spec_helper.rb
|
@@ -226,7 +248,10 @@ summary: Shelly Cloud command line tool
|
|
226
248
|
test_files:
|
227
249
|
- spec/helpers.rb
|
228
250
|
- spec/input_faker.rb
|
229
|
-
- spec/shelly/
|
251
|
+
- spec/shelly/app_spec.rb
|
252
|
+
- spec/shelly/cli/account_spec.rb
|
253
|
+
- spec/shelly/cli/apps_spec.rb
|
254
|
+
- spec/shelly/cli/main_spec.rb
|
230
255
|
- spec/shelly/client_spec.rb
|
231
256
|
- spec/shelly/user_spec.rb
|
232
257
|
- spec/spec_helper.rb
|
data/lib/shelly/cli.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
require "shelly"
|
2
|
-
require "shelly/user"
|
3
|
-
|
4
|
-
module Shelly
|
5
|
-
class CLI < Thor
|
6
|
-
include Helpers
|
7
|
-
include Thor::Actions
|
8
|
-
|
9
|
-
map %w(-v --version) => :version
|
10
|
-
desc "version", "Displays shelly version"
|
11
|
-
def version
|
12
|
-
say "shelly version #{Shelly::VERSION}"
|
13
|
-
end
|
14
|
-
|
15
|
-
desc "register", "Registers new user at Shelly Cloud"
|
16
|
-
def register
|
17
|
-
email_question = User.guess_email.blank? ? "Email:" : "Email (default #{User.guess_email}):"
|
18
|
-
email = ask(email_question)
|
19
|
-
email = User.guess_email if email.blank?
|
20
|
-
password = ask_for_password
|
21
|
-
|
22
|
-
if email.blank? or password.blank?
|
23
|
-
say "Email and password can't be blank" and exit 1
|
24
|
-
end
|
25
|
-
|
26
|
-
user = User.new(email, password)
|
27
|
-
if user.register
|
28
|
-
say "Successfully registered!\nCheck you mailbox for email confirmation"
|
29
|
-
end
|
30
|
-
rescue Client::APIError => e
|
31
|
-
if e.message == "Validation Failed"
|
32
|
-
e.errors.each { |error| say "#{error.first} #{error.last}" }
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
no_tasks do
|
37
|
-
def ask_for_password
|
38
|
-
say "Password: "
|
39
|
-
echo_off
|
40
|
-
password = $stdin.gets.strip
|
41
|
-
echo_on
|
42
|
-
password
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|