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