rumm 0.0.23 → 0.0.24

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.
Files changed (53) hide show
  1. checksums.yaml +8 -8
  2. data/README.md +11 -3
  3. data/app.rb +13 -0
  4. data/app/controllers/authentication_controller.rb +12 -11
  5. data/app/controllers/railsifications_controller.rb +24 -21
  6. data/app/controllers/servers_controller.rb +14 -4
  7. data/app/controllers/users_controller.rb +15 -0
  8. data/app/forms/servers/update_form.rb +7 -0
  9. data/app/forms/volumes/create_form.rb +1 -2
  10. data/app/help/create_container.txt +0 -1
  11. data/app/help/create_dbinstance.txt +1 -1
  12. data/app/help/create_server.txt +1 -1
  13. data/app/help/destroy_node_on_loadbalancer.txt +1 -1
  14. data/app/help/railsify_server.txt +1 -1
  15. data/app/help/show_attachment_on_server.txt +4 -4
  16. data/app/help/show_server.txt +1 -1
  17. data/app/help/update_server.txt +19 -0
  18. data/app/providers/configuration_provider.rb +118 -0
  19. data/app/providers/credentials_provider.rb +4 -4
  20. data/app/providers/login_information_provider.rb +34 -0
  21. data/app/providers/users_provider.rb +13 -0
  22. data/app/routes.rb +1 -0
  23. data/app/views/authentication/login.txt.erb +1 -1
  24. data/app/views/help/show.txt.erb +1 -1
  25. data/app/views/servers/update.txt.erb +1 -0
  26. data/app/views/users/create.txt.erb +1 -1
  27. data/app/views/users/destroy.txt.erb +1 -0
  28. data/app/views/users/show.txt.erb +1 -0
  29. data/lib/rumm/exceptions.rb +5 -0
  30. data/lib/rumm/version.rb +1 -1
  31. data/rumm.gemspec +1 -2
  32. data/spec/features/attachments_spec.rb +1 -1
  33. data/spec/features/configuration_spec.rb +143 -0
  34. data/spec/features/containers_spec.rb +1 -1
  35. data/spec/features/databases_spec.rb +1 -1
  36. data/spec/features/dbinstances_spec.rb +1 -1
  37. data/spec/features/files_spec.rb +1 -1
  38. data/spec/features/help_spec.rb +1 -1
  39. data/spec/features/loadbalancers_spec.rb +1 -1
  40. data/spec/features/login_spec.rb +47 -33
  41. data/spec/features/nodes_spec.rb +1 -1
  42. data/spec/features/servers_spec.rb +1 -1
  43. data/spec/features/users_spec.rb +28 -0
  44. data/spec/features/volumes_spec.rb +1 -1
  45. data/spec/fixtures/cassettes/authentication/successful-login.yml +9 -5
  46. data/spec/fixtures/cassettes/authentication/unsuccessful-login.yml +5 -3
  47. data/spec/fixtures/cassettes/users/create.yml +122 -0
  48. data/spec/fixtures/cassettes/users/destroy.yml +200 -0
  49. data/spec/fixtures/cassettes/users/show-all.yml +122 -0
  50. data/spec/fixtures/cassettes/users/show.yml +165 -0
  51. data/spec/spec_helper.rb +19 -11
  52. metadata +25 -19
  53. data/app/providers/user_provider.rb +0 -23
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NGFlNzA2ZWMyM2RmMjBkZWNlZTk5MzVmMzRkNjFmMGVkNGZiNjQ4Mw==
4
+ YmQzNjJhOTY2NWQwYjdlZTg2ZGRlNWRjZDE1MTczMmRhMWE0ZWQyNg==
5
5
  data.tar.gz: !binary |-
6
- NWIwMWU3MjNhNjAxYTQ5MDVkNmNjMmMwM2MwYTBkZDkzZDk5ZDI2Zg==
6
+ OTQ1MTFmNzRmMzI0NDliYmFjMDYxZmY5YTMwNTllNTAyMDQ5NTZjYg==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- NzljYjhkYTc1ZDg3ZDNhZGI0NDMyNWNjOTIyN2I1M2JkNWU4NDY1OTI4NzI4
10
- ZjU0NzMwMzk3ZTA5MDQ4NzViNThjNTQwZTQ2ZWUzNmNjYTFiNzliNGE3N2U0
11
- MTkwM2U1ZDgwMTg0NjA2YjQ3NjczMTQ1OWNiOWVlODYzZDlkZmY=
9
+ ZDEwOTk4MGFiMzQ1N2Y1ZmJlZTExNjU5MzAwNDBkY2VlZDY0MjY2NjBhNzhl
10
+ MzEzZDgzYzdiMTBkMDFiMTVhNTNhZjYzMDZhZmY2MzIwN2M3YTllMWYzNzU4
11
+ NGI0YjRmNGQyN2ZlY2QyZGEyZmUzYWFhYmJlNjdjMDdkZWYxYmE=
12
12
  data.tar.gz: !binary |-
13
- MzgyNGNjMmIxYzRkOTZlM2EwYmQ1YWE3Y2FmMmIyNzZmOTYzOTUxM2MyZjNl
14
- MDZiMDlmOWRlYmVhZTIzN2QxN2JjOWVkOTQ3ZGZjYjc2N2JmOTQxMGFjN2E1
15
- ZTNkOWRjZmM1ZDNmMDRjODE3Y2I2ZTgyY2RlMjYyMWI4MjZhNjI=
13
+ ZjQyNWMwYzgxMjU2MmU3MDVkMTcyZWEzMWFlNjM3NGIwYjFiYTY0Mzk0Mzg2
14
+ Zjk5MzE5MjUxZDQwMzBkYjAzODkyZjkyZTZjZDU1MTcyZGE4YTJjMDdlZmM5
15
+ ODMyZmUxMGZjZWI0NTQxZGM4NDdlYTJlNmMwZmVmZGViYjM5MjE=
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  ## Rumm: a tasty tool for hackers and pirates
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/rumm.png)](http://badge.fury.io/rb/rumm)
4
- [![Build Status](https://travis-ci.org/rackerlabs/rumm.png?branch=master)](https://travis-ci.org/rackerlabs/rumm)
5
- [![Dependency Status](https://gemnasium.com/rackerlabs/rumm.png)](https://gemnasium.com/rackerlabs/rumm)
4
+ [![Build Status](https://travis-ci.org/rackspace/rumm.png?branch=master)](https://travis-ci.org/rackspace/rumm)
5
+ [![Dependency Status](https://gemnasium.com/rackspace/rumm.png)](https://gemnasium.com/rackspace/rumm)
6
6
 
7
7
 
8
8
  Rumm is a command line interface and API to rackspace. You can use it
@@ -16,7 +16,9 @@ Authenticate with rackspace using your cloud credentials as follows:
16
16
  rumm login
17
17
  username: joe
18
18
  password: ****
19
- logged in, credentials written to ~/.netrc
19
+ Default Region (Enter for ord):
20
+
21
+ logged in as joe, credentials written to ~/.rummrc
20
22
 
21
23
 
22
24
  Now we can see the list of servers we have available:
@@ -35,6 +37,12 @@ For further help, including a full listing of commands, type:
35
37
 
36
38
  rumm help
37
39
 
40
+ To access servers outside of your default region, you can prefix your rumm command with REGION=<region name>.
41
+
42
+ For example to list servers in IAD you would execute the following:
43
+
44
+ REGION=iad rumm show servers
45
+
38
46
  ## Further Reading
39
47
 
40
48
  See the [official rumm website][1] for more information, including documentation.
data/app.rb CHANGED
@@ -1,8 +1,21 @@
1
1
  require "mvcli/app"
2
2
  require "rumm/version"
3
+ require "rumm/exceptions"
3
4
 
4
5
  module Rumm
5
6
  class App < MVCLI::App
6
7
  self.root = Pathname(__FILE__).dirname
8
+
9
+ def main(argv = ARGV.dup, input = $stdin, output = $stdout, log = $stderr, env = ENV.dup)
10
+ super.tap do |code|
11
+
12
+ # HACK: print out usage information if we can't the the command
13
+ if code == MVCLI::Middleware::ExitStatus::EX_USAGE
14
+ puts "\n"
15
+ super(%w[help commands], input, output, log, env) rescue code
16
+ end
17
+ end
18
+ end
19
+
7
20
  end
8
21
  end
@@ -1,19 +1,21 @@
1
1
  require "net/https"
2
2
  require "json"
3
- require "netrc"
4
3
  require "excon"
5
4
 
6
5
  class AuthenticationController < MVCLI::Controller
7
6
 
8
7
  requires :user
8
+ requires :configuration
9
+ requires :login_information
9
10
 
10
11
  def login
11
- login_info = user
12
+ login_info = login_information
12
13
  username = login_info.name
13
14
  password = login_info.password
14
15
 
15
- connection = Excon.new('https://identity.api.rackspacecloud.com')
16
-
16
+ uri = URI.parse(configuration.auth_endpoint)
17
+ connection = Excon.new(uri.to_s)
18
+
17
19
  headers = {'Content-Type' => 'application/json'}
18
20
  body = {auth: {passwordCredentials: {username: username, password: password}}}
19
21
 
@@ -30,16 +32,15 @@ class AuthenticationController < MVCLI::Controller
30
32
 
31
33
  user_credentials = Map(JSON.parse response.body)
32
34
 
33
- netrc = Netrc.read
34
- netrc['api.rackspace.com'] = username, user_credentials["RAX-KSKEY:apiKeyCredentials"].apiKey
35
- netrc.save
36
-
35
+ configuration.username = username
36
+ configuration.api_key = user_credentials["RAX-KSKEY:apiKeyCredentials"].apiKey
37
+ configuration.region = login_info.region
38
+ configuration.save
39
+
37
40
  user_info
38
41
  end
39
42
 
40
43
  def logout
41
- n = Netrc.read
42
- n.delete 'api.rackspace.com'
43
- n.save
44
+ configuration.delete
44
45
  end
45
46
  end
@@ -1,5 +1,6 @@
1
1
  require "tmpdir"
2
2
  require "open3"
3
+ require "bundler"
3
4
 
4
5
  class RailsificationsController < MVCLI::Controller
5
6
  requires :compute
@@ -13,29 +14,31 @@ class RailsificationsController < MVCLI::Controller
13
14
  tmpdir = Pathname(Dir.tmpdir).join 'chef_kitchen'
14
15
  FileUtils.mkdir_p tmpdir
15
16
  Dir.chdir tmpdir do
16
- File.open('Gemfile', 'w') do |f|
17
- f.puts 'source "https://rubygems.org"'
18
- f.puts 'gem "knife-solo", ">= 0.3.0pre3"'
19
- f.puts 'gem "berkshelf"'
20
- end
21
- execute "bundle install --binstubs"
22
- execute "bin/knife solo init ."
23
- File.open 'Berksfile', 'w' do |f|
24
- f.puts "site :opscode"
25
- f.puts ""
26
- f.puts "cookbook 'runit', '>= 1.1.2'"
27
- f.puts "cookbook 'rackbox', github: 'hayesmp/rackbox-cookbook'"
28
- end
29
- execute "bin/berks install --path cookbooks/"
30
- execute "bin/knife solo prepare root@#{server.ipv4_address}"
31
- File.open('nodes/host.json', 'w') do |f|
32
- f.puts('{"run_list":["rackbox"],"rackbox":{"apps":{"unicorn":[{"appname":"app1","hostname":"app1"}]},"ruby":{"global_version":"2.0.0-p195","versions":["2.0.0-p195"]}}}')
33
- end
17
+ Bundler.with_clean_env do
18
+ File.open('Gemfile', 'w') do |f|
19
+ f.puts 'source "https://rubygems.org"'
20
+ f.puts 'gem "knife-solo", ">= 0.3.0pre3"'
21
+ f.puts 'gem "berkshelf"'
22
+ end
23
+ execute "bundle install --binstubs"
24
+ execute "bin/knife solo init ."
25
+ File.open 'Berksfile', 'w' do |f|
26
+ f.puts "site :opscode"
27
+ f.puts ""
28
+ f.puts "cookbook 'runit', '>= 1.1.2'"
29
+ f.puts "cookbook 'rackbox', github: 'hayesmp/rackbox-cookbook'"
30
+ end
31
+ execute "bin/berks install --path cookbooks/"
32
+ execute "bin/knife solo prepare root@#{server.ipv4_address}"
33
+ File.open('nodes/host.json', 'w') do |f|
34
+ f.puts('{"run_list":["rackbox"],"rackbox":{"apps":{"unicorn":[{"appname":"app1","hostname":"app1"}]},"ruby":{"global_version":"2.0.0-p195","versions":["2.0.0-p195"]}}}')
35
+ end
34
36
 
35
- FileUtils.rm_rf "#{server.ipv4_address}.json"
36
- FileUtils.mv "nodes/host.json", "nodes/#{server.ipv4_address}.json"
37
+ FileUtils.rm_rf "#{server.ipv4_address}.json"
38
+ FileUtils.mv "nodes/host.json", "nodes/#{server.ipv4_address}.json"
37
39
 
38
- execute "bin/knife solo cook root@#{server.ipv4_address}"
40
+ execute "bin/knife solo cook root@#{server.ipv4_address}"
41
+ end
39
42
  end
40
43
  return server
41
44
  end
@@ -31,6 +31,20 @@ class ServersController < MVCLI::Controller
31
31
  return server
32
32
  end
33
33
 
34
+ def update
35
+ template = Servers::UpdateForm
36
+ argv = MVCLI::Argv.new command.argv
37
+ form = template.new argv.options
38
+ form.validate!
39
+
40
+ unupdated_server = server
41
+ unupdated_server.name = form.name unless form.name == nil
42
+ unupdated_server.ipv4_address = form.ipv4 unless form.ipv4 == nil
43
+ unupdated_server.ipv6_address = form.ipv6 unless form.ipv6 == nil
44
+
45
+ unupdated_server.update
46
+ end
47
+
34
48
  def destroy
35
49
  server.tap do |s|
36
50
  s.destroy
@@ -43,10 +57,6 @@ class ServersController < MVCLI::Controller
43
57
  index.find {|s| s.name == params[:id]} or fail Fog::Errors::NotFound
44
58
  end
45
59
 
46
- def generate_name
47
- 'divine-reef'
48
- end
49
-
50
60
  def ssh
51
61
  test = server
52
62
  ip_address = test.ipv4_address
@@ -1,5 +1,6 @@
1
1
  class UsersController < MVCLI::Controller
2
2
  requires :instances
3
+ requires :users
3
4
  requires :command
4
5
 
5
6
  def index
@@ -14,6 +15,20 @@ class UsersController < MVCLI::Controller
14
15
  instance.users.create form.value
15
16
  end
16
17
 
18
+ def show
19
+ list = users
20
+ list.instance = instance
21
+ list.all.find { |u| u.name == params[:id] } or fail Fog::Errors::NotFound
22
+ end
23
+
24
+ def destroy
25
+ list = users
26
+ list.instance = instance
27
+ user = list.all.find { |u| u.name == params[:id] }
28
+ user.destroy or fail Fog::Errors::NotFound
29
+ end
30
+
31
+
17
32
  private
18
33
 
19
34
  def instance
@@ -0,0 +1,7 @@
1
+ class Servers::UpdateForm < MVCLI::Form
2
+ requires :naming
3
+
4
+ input :name, String, default: nil
5
+ input :ipv4, String, decode: ->(s) { URI('').hostname = s }
6
+ input :ipv6, String, decode: ->(s) { URI('').hostname = s }
7
+ end
@@ -5,6 +5,5 @@ class Volumes::CreateForm < MVCLI::Form
5
5
  input :type, String, default: "SATA"
6
6
  input :size, Integer, default: 100
7
7
 
8
- validates(:type, "must either be SATA or SSD"){ |type| type == "SATA" or "SSD" }
9
- validates(:size, "must be between 100 and 1024"){ |size| (100..1024) === size }
8
+ validates(:type, "must either be SATA or SSD") { |type| type == "SATA" or "SSD" }
10
9
  end
@@ -3,7 +3,6 @@ Usage:
3
3
 
4
4
  Options:
5
5
  -n, --name STRING # Name to give the new container
6
- none
7
6
 
8
7
  Arguments:
9
8
  none
@@ -2,7 +2,7 @@ Usage:
2
2
  rumm create dbinstance [--name STRING]
3
3
 
4
4
  Options:
5
- -n, --name STRING # Name to give the new server
5
+ -n, --name STRING # Name to give the new dbinstance
6
6
 
7
7
  Arguments:
8
8
  none
@@ -1,5 +1,5 @@
1
1
  Usage:
2
- rumm create server [--name STRING] [--image-id STRING] [--flavor-id STRING] [--ssh-private STRING] [--ssh-publinc STRING]
2
+ rumm create server [--name STRING] [--image-id STRING] [--flavor-id STRING] [--ssh-private STRING] [--ssh-public STRING]
3
3
 
4
4
  Options:
5
5
  -n, --name STRING # Name to give the new server
@@ -5,7 +5,7 @@ Options:
5
5
  none
6
6
 
7
7
  Arguments:
8
- ID: STRING # ID of the node to show
8
+ ID: STRING # ID of the node to destroy
9
9
  LOADBALANCER_ID: STRING # Name of the loadbalancer that contains the node requested
10
10
 
11
11
  Description:
@@ -1,5 +1,5 @@
1
1
  Usage:
2
- rumm railsify server id
2
+ rumm railsify server ID
3
3
 
4
4
  Options:
5
5
  none
@@ -1,14 +1,14 @@
1
1
  Usage:
2
- rumm show attachment on server SERVER_ID
2
+ rumm show attachment on server SERVER_ID --volume STRING
3
3
 
4
4
  Options:
5
- -v, --volume STRING # Name of the volume to attach
5
+ -v, --volume STRING # Name of the attached volume
6
6
 
7
7
  Arguments:
8
- SERVER_ID: STRING # The name of the server the volume is to be detached from
8
+ SERVER_ID: STRING # The name of the attached server
9
9
 
10
10
  Description:
11
- Detaches the specified volume from the named server.
11
+ Shows details about the specified attachment.
12
12
 
13
13
  Examples:
14
14
  rumm show attachment on server superordinate-struthioniformes --volume colorful-caterpillar
@@ -5,7 +5,7 @@ Options:
5
5
  none
6
6
 
7
7
  Arguments:
8
- ID: STRING # Name of the server to play
8
+ ID: STRING # Name of the server to show
9
9
 
10
10
  Description:
11
11
  Shows the attributes of the sever with the name specified by ID.
@@ -0,0 +1,19 @@
1
+ Usage:
2
+ rumm update server ID [--name STRING] [--ipv4 IPV4] [--ipv6 IPV6]
3
+
4
+
5
+ Arguments:
6
+ ID: STRING # Name of the server to update
7
+
8
+ Options:
9
+ --name: STRING # The name you want to change the server to
10
+ --ipv4: IPV4 # The ip you want to change the server to
11
+ --ipv6: IPV6 # The ip you want to change the server to
12
+
13
+ Description:
14
+ Updates the given server to the new values by updating and changing the
15
+ name, ipv4, or ipv6. It only updates the values if they're given in the
16
+ command.
17
+
18
+ Examples:
19
+ rumm update server silly-saffron --name sad-saffron
@@ -0,0 +1,118 @@
1
+ require 'singleton'
2
+ require 'json'
3
+ require 'rbconfig'
4
+ require 'fog'
5
+
6
+ class ConfigurationProvider
7
+
8
+ def value
9
+ self
10
+ end
11
+
12
+ def initialize
13
+ @config = defaults
14
+ reload
15
+ end
16
+
17
+ def [](key)
18
+ @config["environments"]["default"][key.to_s] rescue nil
19
+ end
20
+
21
+ def []=(key, value)
22
+ @config["environments"]["default"][key.to_s] = value
23
+ end
24
+
25
+ [:username, :api_key, :region].each do |sym|
26
+ class_eval <<-META
27
+ def #{sym}
28
+ self['#{sym}']
29
+ end
30
+
31
+ def #{sym}=(value)
32
+ self['#{sym}'] = value
33
+ end
34
+
35
+ META
36
+ end
37
+
38
+ def region
39
+ region_to_str(ENV['REGION'] || self['region'])
40
+ end
41
+
42
+ def lon_region?
43
+ region == "lon"
44
+ end
45
+
46
+ def auth_endpoint
47
+ # Note: You can authenticate against any endpoint regardless of the location of your cloud account, however, to locate the proper service endpoints
48
+ # you must authenticate against the correct cloud endpoint
49
+ self.lon_region? ? Fog::Rackspace::UK_AUTH_ENDPOINT : Fog::Rackspace::US_AUTH_ENDPOINT
50
+ end
51
+
52
+ def reload
53
+ return false unless File.exists? default_path
54
+
55
+ begin
56
+ File.open default_path do |f|
57
+ h = JSON.load f
58
+ @config.merge! h
59
+ true
60
+ end
61
+ rescue => e
62
+ fail "Unable to read #{default_path} - #{e.inspect}"
63
+ end
64
+ end
65
+
66
+ def save
67
+ begin
68
+ File.open default_path, 'w' do |f|
69
+ JSON.dump @config, f
70
+ true
71
+ end
72
+ rescue => e
73
+ fail "Unable to write #{default_path} - #{e.inspect}"
74
+ end
75
+ end
76
+
77
+
78
+ def delete
79
+ @config = defaults
80
+ File.delete(default_path) if File.exists? default_path
81
+ true
82
+ rescue => e
83
+ fail "Unable to delete #{default_path} - #{e.inspect}"
84
+ end
85
+
86
+ def default_path
87
+ if windows? && !cygwin?
88
+ File.join(ENV['USERPROFILE'].gsub("\\","/"), ".rummrc")
89
+ else
90
+ File.join((ENV["HOME"] || "./"), ".rummrc")
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def region_to_str(str)
97
+ str ? str.to_s.downcase : nil
98
+ end
99
+
100
+ def defaults
101
+ { "environments" => {
102
+ "default" => {
103
+ "region" => :ord
104
+ }
105
+ }
106
+ }
107
+ end
108
+
109
+ # see http://stackoverflow.com/questions/4871309/what-is-the-correct-way-to-detect-if-ruby-is-running-on-windows
110
+ def windows?
111
+ RbConfig::CONFIG["host_os"] =~ /mswin|mingw|cygwin/
112
+ end
113
+
114
+ def cygwin?
115
+ RbConfig::CONFIG["host_os"] =~ /cygwin/
116
+ end
117
+
118
+ end