shelly 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -2,6 +2,7 @@ require "rubygems"
2
2
  require "thor"
3
3
  require "core_ext/object"
4
4
  require "shelly/helpers"
5
+ require "shelly/base"
5
6
 
6
7
  module Shelly
7
8
  autoload :Client, "shelly/client"
@@ -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
@@ -0,0 +1,13 @@
1
+ module Shelly
2
+ class Base
3
+ def current_user
4
+ @user = User.new
5
+ @user.load_credentials
6
+ @user
7
+ end
8
+
9
+ def shelly
10
+ @shelly ||= Client.new(current_user.email, current_user.password)
11
+ end
12
+ end
13
+ end
@@ -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
@@ -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('/users', :user => {:email => email, :password => password})
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 -%>
@@ -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
- client = Client.new
11
- client.register_user(email, password)
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 self.guess_email
16
- @@guess_email ||= IO.popen("git config --get user.email").read.strip
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")
@@ -1,3 +1,3 @@
1
1
  module Shelly
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -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
- @cli = Shelly::CLI.new
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
- @cli.register
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 (default kate@example.com): ")
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
- @cli.register
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
- @cli.register
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
- @cli.register
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
- @cli.register
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!\nCheck you mailbox for email confirmation")
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
- @cli.register
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
- @cli.register
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
@@ -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", :password => "secret"}})
29
- @client.register_user("test@example.com", "secret")
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", :email => "bob@example.com", :password => "secret"}.to_json
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.request("/account", :get)
62
+ client.request_parameters("/account", :get).should == expected
53
63
  end
54
64
  end
55
65
 
@@ -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
@@ -2,9 +2,9 @@ require "rspec"
2
2
  require "shelly"
3
3
  require "helpers"
4
4
  require "input_faker"
5
- require "fakefs/safe"
6
5
  require "fakefs/spec_helpers"
7
6
 
8
7
  RSpec.configure do |config|
9
8
  config.include RSpec::Helpers
9
+ config.include FakeFS::SpecHelpers
10
10
  end
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: 23
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 4
10
- version: 0.0.4
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-15 00:00:00 +02:00
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/cli.rb
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/cli_spec.rb
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/cli_spec.rb
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
@@ -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