rig 0.4.5 → 0.5.0

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 (60) hide show
  1. data/Gemfile +1 -0
  2. data/conf/accounts/default.yml +3 -3
  3. data/conf/config.yml +33 -6
  4. data/conf/plugins/chef.rb +105 -0
  5. data/conf/plugins/mongo.rb +135 -0
  6. data/{lib/rig/command/database.rb → conf/plugins/mysql.rb} +12 -8
  7. data/conf/plugins/scout.rb +81 -0
  8. data/conf/userdata/chef/userdata.sh.erb +7 -1
  9. data/lib/rig.rb +11 -1
  10. data/lib/rig/account.rb +43 -0
  11. data/lib/rig/command.rb +92 -41
  12. data/lib/rig/command/abstract.rb +1 -2
  13. data/lib/rig/command/balancer.rb +102 -0
  14. data/lib/rig/command/dns.rb +47 -0
  15. data/lib/rig/command/environment.rb +67 -0
  16. data/lib/rig/command/keypair.rb +15 -0
  17. data/lib/rig/command/server.rb +181 -0
  18. data/lib/rig/config.rb +35 -105
  19. data/lib/rig/log.rb +66 -0
  20. data/lib/rig/model/account.rb +1 -1
  21. data/lib/rig/model/connection.rb +13 -6
  22. data/lib/rig/model/environment.rb +12 -8
  23. data/lib/rig/model/instance.rb +3 -8
  24. data/lib/rig/model/template.rb +1 -1
  25. data/lib/rig/model/userdata.rb +1 -1
  26. data/lib/rig/plugin.rb +61 -0
  27. data/lib/rig/version.rb +2 -2
  28. metadata +15 -35
  29. data/lib/rig/chef.rb +0 -59
  30. data/lib/rig/command/balancer/destroy.rb +0 -16
  31. data/lib/rig/command/balancer/list.rb +0 -30
  32. data/lib/rig/command/balancer/listener.rb +0 -31
  33. data/lib/rig/command/balancer/main.rb +0 -40
  34. data/lib/rig/command/balancer/view.rb +0 -19
  35. data/lib/rig/command/dns/create.rb +0 -19
  36. data/lib/rig/command/dns/destroy.rb +0 -16
  37. data/lib/rig/command/dns/edit.rb +0 -20
  38. data/lib/rig/command/dns/list.rb +0 -21
  39. data/lib/rig/command/dns/main.rb +0 -13
  40. data/lib/rig/command/environment/create.rb +0 -21
  41. data/lib/rig/command/environment/destroy.rb +0 -18
  42. data/lib/rig/command/environment/list.rb +0 -31
  43. data/lib/rig/command/environment/main.rb +0 -15
  44. data/lib/rig/command/environment/protect.rb +0 -22
  45. data/lib/rig/command/instance/create.rb +0 -60
  46. data/lib/rig/command/instance/destroy.rb +0 -19
  47. data/lib/rig/command/instance/list.rb +0 -26
  48. data/lib/rig/command/instance/main.rb +0 -16
  49. data/lib/rig/command/instance/tag/get.rb +0 -20
  50. data/lib/rig/command/instance/tag/main.rb +0 -14
  51. data/lib/rig/command/instance/tag/remove.rb +0 -44
  52. data/lib/rig/command/instance/tag/set.rb +0 -46
  53. data/lib/rig/command/instance/view.rb +0 -20
  54. data/lib/rig/command/keypair/list.rb +0 -14
  55. data/lib/rig/command/keypair/main.rb +0 -11
  56. data/lib/rig/command/main.rb +0 -90
  57. data/lib/rig/model/database.rb +0 -23
  58. data/lib/rig/model/database/base.rb +0 -11
  59. data/lib/rig/model/database/mongodb.rb +0 -70
  60. data/lib/rig/model/database/mysql.rb +0 -12
data/lib/rig.rb CHANGED
@@ -1,2 +1,12 @@
1
+ require 'clamp'
2
+ require 'fog'
3
+
1
4
  require 'rig/version'
2
- require 'rig/config'
5
+ require 'rig/config'
6
+ require 'rig/account'
7
+ require 'rig/log'
8
+ require 'rig/plugin'
9
+ require 'rig/model'
10
+
11
+ Rig::Config.data
12
+ Rig::Account.data
@@ -0,0 +1,43 @@
1
+ module Rig
2
+ module Account
3
+ class << self
4
+ attr_reader :name
5
+ attr_reader :loaded
6
+
7
+ def data(name = get)
8
+ if @name != name
9
+ @data = nil
10
+ @loaded = false
11
+ @name = nil
12
+ end
13
+
14
+ @data ||= begin
15
+ Rig::Log.debug "loading account"
16
+ a = Rig::Model::Account.load(name)
17
+ a[:name] = name
18
+
19
+ Rig::Log.debug "loading plugins from account"
20
+ Rig::Plugin.load(a[:plugins]) if a[:plugins]
21
+
22
+ a
23
+ end
24
+ @name = @data[:name]
25
+ @loaded = true
26
+ @data
27
+ end
28
+
29
+ def get
30
+ return ENV['RIG_ACCOUNT'] if ENV['RIG_ACCOUNT']
31
+ return Rig.config[:account] if Rig.config[:account]
32
+ return Rig.config[:default_account] if Rig.config[:default_account]
33
+ return Rig.config[:accounts].first if Rig.config[:accounts] && Rig.config[:accounts].count > 0
34
+ "default"
35
+ end
36
+
37
+ def save
38
+ name = Rig.account[:name]
39
+ Rig::Model::Account.save(name, Rig.account)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,47 +1,98 @@
1
1
  require 'rig'
2
- require 'clamp'
3
- require 'fog'
4
- require 'yaml'
5
-
6
2
  require 'rig/model'
7
3
 
8
4
  require 'rig/command/abstract'
9
5
  require 'rig/command/options'
10
6
 
11
- require 'rig/command/main'
12
- require 'rig/command/ssh'
13
- require 'rig/command/account'
14
- require 'rig/command/config'
15
- require 'rig/command/database'
16
-
17
- require 'rig/command/environment/main'
18
- require 'rig/command/environment/list'
19
- require 'rig/command/environment/create'
20
- require 'rig/command/environment/destroy'
21
- require 'rig/command/environment/protect'
22
-
23
- require 'rig/command/balancer/main'
24
- require 'rig/command/balancer/list'
25
- require 'rig/command/balancer/view'
26
- require 'rig/command/balancer/destroy'
27
- require 'rig/command/balancer/listener'
28
-
29
- require 'rig/command/dns/main'
30
- require 'rig/command/dns/list'
31
- require 'rig/command/dns/create'
32
- require 'rig/command/dns/edit'
33
- require 'rig/command/dns/destroy'
34
-
35
- require 'rig/command/instance/main'
36
- require 'rig/command/instance/list'
37
- require 'rig/command/instance/create'
38
- require 'rig/command/instance/destroy'
39
- require 'rig/command/instance/view'
40
-
41
- require 'rig/command/instance/tag/main'
42
- require 'rig/command/instance/tag/get'
43
- require 'rig/command/instance/tag/set'
44
- require 'rig/command/instance/tag/remove'
45
-
46
- require 'rig/command/keypair/main'
47
- require 'rig/command/keypair/list'
7
+ module Rig
8
+ module Command
9
+ class Main < Abstract
10
+ def merge_file(src, dest)
11
+ old = YAML.load_file(dest)
12
+ FileUtils.mv(dest, "#{dest}.bak.#{Time.now.to_i}")
13
+ new = YAML.load_file(src)
14
+ new.merge!(old)
15
+ File.open(dest, "w") {|f| f.write(new.to_yaml)}
16
+ end
17
+
18
+ subcommand "userdata", "test userdata" do
19
+ parameter "NAME", "name of server"
20
+ parameter "ROLE", "role of server"
21
+ parameter "ENVIRONMENT", "environment of server"
22
+
23
+ def execute
24
+ userdata = Rig::Model::Userdata.create(name, role, environment)
25
+ puts "USERDATA:\n#{userdata}"
26
+ end
27
+ end
28
+
29
+ subcommand "init", "initialize configuration directory" do
30
+ option %w{-f --force}, :flag, "force creation of files", :default => false
31
+ option %w{-d --directory}, "DIRECTORY", "configuration directory", :default => "~/.rig"
32
+
33
+ def execute
34
+ dir = File.expand_path(ENV['RIG_CONFIG'] ? ENV['RIG_CONFIG'] : directory)
35
+ back = "#{dir}.bak.#{Time.now.to_i}"
36
+ conf = File.expand_path("../../../../conf", __FILE__)
37
+
38
+ files = %w{
39
+ config.yml
40
+
41
+ accounts/default.yml
42
+
43
+ templates/solo.yml
44
+ templates/multi.yml
45
+
46
+ userdata/default/userdata.sh.erb
47
+ userdata/default/userdata.yml
48
+ userdata/chef/userdata.sh.erb
49
+ userdata/chef/userdata.yml
50
+ userdata/chef/validation.pem
51
+
52
+ plugins/chef.rb
53
+ plugins/scout.rb
54
+ }
55
+
56
+ unless dir
57
+ puts "directory could not be found: tried environment variables RIG_CONFIG (#{ENV['RIG_CONFIG']}) and HOME (#{ENV['HOME']}"
58
+ exit(1)
59
+ end
60
+
61
+ if File.exists?(dir)
62
+ unless File.directory?(dir)
63
+ puts "moving old config #{dir} to #{back}"
64
+ FileUtils.mv(dir, new)
65
+ end
66
+ end
67
+
68
+ puts "[create] #{dir}/"
69
+ FileUtils.mkdir_p(dir)
70
+
71
+ files.each do |file|
72
+ src = "#{conf}/#{file}"
73
+ dest = "#{dir}/#{file}"
74
+ FileUtils.mkdir_p(File.dirname(dest))
75
+ if File.exists?(dest) && !force?
76
+ puts "[skip] #{dest}"
77
+ else
78
+ puts "[create] #{dest}"
79
+ FileUtils.copy(src, dest)
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ class << self
87
+ def load
88
+ Rig::Plugin.run "commands:before"
89
+
90
+ Dir["#{File.dirname(__FILE__)}/model/*.rb"].each {|file| require file.gsub(/\.rb/,'')}
91
+
92
+ Rig::Plugin.run "commands:loaded", Rig::Command::Main
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+ Rig::Command.load
@@ -9,8 +9,7 @@ module Rig
9
9
  end
10
10
 
11
11
  option ["-a", "--account"], "ACCOUNT", "set the rig account to use", :default => "default" do |a|
12
- Rig.config[:account] = a
13
- a
12
+ Rig::Account.data(a)
14
13
  end
15
14
 
16
15
  option ["-v", "--version"], :flag, "print version number and exit" do |v|
@@ -0,0 +1,102 @@
1
+ require 'rig'
2
+ require 'rest-client'
3
+
4
+ module Rig
5
+ module Command
6
+ class Balancer < Abstract
7
+ self.default_subcommand = 'list'
8
+
9
+ subcommand "list", "list load balancers" do
10
+ def execute
11
+ list = Rig::Model::Balancer.all
12
+ list.each do |lb|
13
+ puts lb.id
14
+ puts "- listeners"
15
+ lb.listeners.each do |l|
16
+ puts " - #{l.protocol}:#{l.lb_port} => #{l.instance_protocol}:#{l.instance_port}"
17
+ end
18
+ puts "- instances"
19
+ lb.instances.each do |inst|
20
+ puts " - #{inst}"
21
+ end
22
+ puts "- zones"
23
+ lb.availability_zones.each do |z|
24
+ puts " - #{z}"
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ subcommand "destroy", "destroy load balancer" do
31
+ parameter "NAME", "name of load balancer"
32
+
33
+ def execute
34
+ Rig::Model::Balancer.destroy(name)
35
+ end
36
+ end
37
+
38
+ subcommand "listener", "manage listeners for load balancer" do
39
+ subcommand "add", "add a listener to a balancer" do
40
+ parameter "BALANCER", "balancer name"
41
+ parameter "LISTENER", "listener data PROTO:PORT/PROTO:PORT"
42
+ parameter "[CERT]", "arn for server certificate"
43
+ parameter "[POLICY] ...", "policy names"
44
+
45
+ def execute
46
+ (from, to) = listener.split('/')
47
+ #newlist = elb.listeners.create()
48
+ Rig::Model::Balancer.add_listener(balancer, from, to, cert, policy_list)
49
+ end
50
+ end
51
+
52
+ subcommand "rm", "remove a listener from a balancer" do
53
+ parameter "BALANCER", "balancer name"
54
+ parameter "LISTENER", "listener data PROTO:PORT (just the elb side)"
55
+
56
+ def execute
57
+ Rig::Model::Balancer.destroy_listener(balancer, listener)
58
+ end
59
+ end
60
+ end
61
+
62
+ subcommand 'test', 'test a load balancer' do
63
+ option %w{-c --count}, "COUNT", "number of times to loop", :default => 100
64
+ option %w{-p --poll}, "POLL", "poll time", :default => 10
65
+ parameter "NAME", "name of the load balancer to test"
66
+
67
+ def execute
68
+ elb = Rig::Model::Balancer.find(name)
69
+ raise "elb #{name} not found" unless elb
70
+
71
+ dns = elb.dns_name
72
+ count.times do |i|
73
+ puts "### loop #{i}"
74
+ ips = `host #{dns}`.split(/\n/).collect { |e| e.split.last }
75
+ ips.each do |ip|
76
+ val = begin
77
+ RestClient.get("http://#{ip}")
78
+ rescue => e
79
+ e.message
80
+ end
81
+ print ".. #{ip} - #{val}"
82
+ puts
83
+ end
84
+ sleep poll
85
+ end
86
+ end
87
+ end
88
+
89
+ subcommand "view", "view load balancer information" do
90
+ include Options::ShowAll
91
+
92
+ parameter "NAME", "name of instance"
93
+
94
+ def execute
95
+ item = Rig::Model::Balancer.find(name)
96
+ puts item.attributes.to_yaml
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ Rig::Command::Main.subcommand "balancer", "manage load balancers", Rig::Command::Balancer
@@ -0,0 +1,47 @@
1
+
2
+ module Rig
3
+ module Command
4
+ class Dns < Abstract
5
+ self.default_subcommand = 'list'
6
+
7
+ subcommand "create", "create dns entry" do
8
+ parameter 'NAME', 'the name to create'
9
+ parameter 'VALUE', 'where the name points to'
10
+
11
+ def execute
12
+ Rig::Model::Dns.create(name, value)
13
+ end
14
+ end
15
+
16
+ subcommand "destroy", "destroy dns entry" do
17
+ parameter "NAME", "name of dns entry"
18
+ def execute
19
+ Rig::Model::Dns.destroy(name)
20
+ end
21
+ end
22
+
23
+ subcommand "change", "change dns entry" do
24
+ parameter 'NAME', 'the name to change'
25
+ parameter 'VALUE', 'where the name points to'
26
+
27
+ def execute
28
+ Rig::Model::Dns.destroy(name)
29
+ Rig::Model::Dns.create(name, value)
30
+ end
31
+ end
32
+
33
+ subcommand "list", "list dns entries" do
34
+ def execute
35
+ zone = Rig.get_config(:dns_zone)
36
+ records = Rig::Model::Dns.records("#{zone}")
37
+ rows = []
38
+ records.each do |record|
39
+ rows << [zone, record.type, record.ttl, record.name, [*record.value].join(",")]
40
+ end
41
+ print_table(%w{Domain Type TTL Name Value}, rows)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ Rig::Command::Main.subcommand "dns", "manage dns", Rig::Command::Dns
@@ -0,0 +1,67 @@
1
+ require 'rig'
2
+ require 'fog'
3
+
4
+ module Rig
5
+ module Command
6
+ class Environment < Abstract
7
+ self.default_subcommand = "list"
8
+
9
+ subcommand "create", "create environment" do
10
+ parameter "NAME", "the name of the environment"
11
+ parameter "TEMPLATE", "the template to use (solo, multi, etc)", :default => "solo"
12
+
13
+ def execute
14
+ #connection = Rig::Connection.compute
15
+ env = Rig::Model::Environment.create(name, template.to_sym)
16
+ #ap env.servers
17
+ end
18
+ end
19
+
20
+ subcommand "destroy", "destroy environment" do
21
+ parameter "NAME", "name of the environment to destroy"
22
+
23
+ def execute
24
+ Rig::Model::Environment.destroy(name)
25
+ end
26
+ end
27
+
28
+ subcommand "list", "list environments" do
29
+ option %w{-s --simple}, :flag, "just show list of names"
30
+
31
+ def execute
32
+ envs = Rig::Model::Environment.list
33
+
34
+ if simple?
35
+ print_table(%w{Name}, envs)
36
+ else
37
+ rows = []
38
+ envs.each do |e|
39
+ env = Rig::Model::Environment.find(e)
40
+ zones = env.servers.collect { |e| e.availability_zone }
41
+ rows << [env.name, env.template, env.region, env.servers.count, env.balancers.count, env.protected?, zones]
42
+ end
43
+ print_table(%w{Name Template Region Servers# Balancers# Protected? AZs}, rows)
44
+ end
45
+ end
46
+ end
47
+
48
+ subcommand "protect", "set (or disable) protection on environment" do
49
+ option ["-u", "--undo"], :flag, "disable protection instead of enabling"
50
+ parameter "NAME", "the environment to protect"
51
+
52
+ def execute
53
+ env = Rig::Model::Environment.find(name)
54
+ raise "Environment #{name} not found" if env.nil?
55
+ connection = Rig::Connection.compute
56
+ val = undo? ? "false" : "true"
57
+
58
+ env.servers.each do |s|
59
+ connection.create_tags(s.id, "Protected" => val)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ Rig::Command::Main.subcommand "environment", "manage environments", Rig::Command::Environment
@@ -0,0 +1,15 @@
1
+ module Rig
2
+ module Command
3
+ class Keypair < Abstract
4
+ self.default_subcommand = "list"
5
+ subcommand "list", "list key pairs" do
6
+ def execute
7
+ connection = Rig::Connection.compute
8
+ list = connection.key_pairs.all
9
+ puts list.inspect
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ Rig::Command::Main.subcommand "keypair", "manage key pairs", Rig::Command::Keypair
@@ -0,0 +1,181 @@
1
+ require 'rig'
2
+ require 'awesome_print'
3
+
4
+ module Rig
5
+ module Command
6
+ class Server < Abstract
7
+ self.default_subcommand = "list"
8
+ subcommand "create", "create an server" do
9
+ option %w{-i --image}, "IMAGE", "the image / ami to use, default: your account image"
10
+ option %w{-f --flavor}, "FLAVOR", "the flavor / size to use"
11
+ option %w{-k --keypair}, "KEYPAIR", "the key pair / name to use, default: your account keypair"
12
+ option %w{-g --groups}, "GROUPS", "comma separated list of groups, default: your account groups"
13
+ option %w{-r --region}, "GROUPS", "the region, default: your account region"
14
+ option %w{--add}, :flag, "add this to the existing servers of this role and environment"
15
+ option "--[no-]chef", :flag, "use chef to bootstrap", :default => true
16
+
17
+ parameter "ENVIRONMENT", "the environment this server should belong to"
18
+ parameter "ROLE", "the server role", :default => 'solo'
19
+
20
+ def execute
21
+ name = role
22
+ # figure out if there are others of this role, if there are add a number to name to distinguish.
23
+ n = "#{name}.#{environment}.env"
24
+ chef = Rig.config[:chef] ? true : false
25
+
26
+ unless flavor
27
+ raise "must specify flavor"
28
+ end
29
+
30
+ o = {
31
+ :image_id => image,
32
+ :flavor_id => flavor,
33
+ :key_name => keypair,
34
+ :groups => groups.split(','),
35
+ :user_data => Rig::Model::Userdata.create(n, role, environment, :chef => chef)
36
+ }
37
+ tags = { 'Name' => n, 'Environment' => environment, 'Role' => role }
38
+ server = Rig::Model::Instance.create(o, tags)
39
+ puts "created: #{server.id}"
40
+ rescue => e
41
+ puts "error: #{e.message}"
42
+ raise Clamp::HelpWanted, self
43
+ end
44
+
45
+ def default_image
46
+ Rig.account[:image].is_a?(Hash) ? Rig.account[:image].first.last : Rig.account[:image]
47
+ end
48
+
49
+ def default_keypair
50
+ Rig.account[:keypair]
51
+ end
52
+
53
+ def default_groups
54
+ Rig.account[:groups].join(",")
55
+ end
56
+ end
57
+
58
+ subcommand "destroy", "destroy (terminate) server" do
59
+ parameter "NAME", "the name of the server to connect to"
60
+
61
+ def execute
62
+ list = Rig::Model::Instance.find(name)
63
+ Rig::Model::Instance.destroy(list)
64
+ end
65
+ end
66
+ subcommand "list", "list servers" do
67
+ include Options::ShowAll
68
+
69
+ option %w{-l --long}, :flag, "show more information in table"
70
+ parameter "[ENV]", "environment"
71
+
72
+ def execute
73
+ connection = Rig::Connection.compute
74
+ filters = { }
75
+ filters["instance-state-name"] = "running" unless showall?
76
+ filters["tag:Environment"] = env if env
77
+
78
+ list = connection.servers.all(filters)
79
+ instance_list(list, long?)
80
+ end
81
+ end
82
+ subcommand "tag", "manage server tags" do
83
+ subcommand "get", "get tag(s) for this server" do
84
+ parameter "NAME", "the name of the server to get tags for"
85
+
86
+ def execute
87
+ instance = Rig::Model::Instance.find(name)
88
+ raise "server not found #{name}" unless instance
89
+ puts "#{instance.tags.inspect}"
90
+ end
91
+ end
92
+
93
+ subcommand "rm", "remove tag(s) for this server" do
94
+ include Options::Instance
95
+ include Options::InstanceName
96
+
97
+ parameter "TAG", "tag key to remove", :attribute_name => :raw_tag
98
+
99
+ # TODO: fix this to use Model instead
100
+ def execute
101
+ unless iname || iid
102
+ raise "Must set either server name or server id (-iname or -iid)"
103
+ end
104
+
105
+ connection = Rig::Connection.compute
106
+ if iid
107
+ instance = connection.servers.get(iid)
108
+ else
109
+ list = connection.servers.all({ "tag:Name" => iname })
110
+ if list.count > 1
111
+ puts "there is more than one server that matches:"
112
+ instance_list(list)
113
+ return
114
+ end
115
+ instance = list.first
116
+ end
117
+
118
+ unless instance
119
+ puts "could not find server"
120
+ return
121
+ end
122
+
123
+ ap connection.delete_tags(instance.id, raw_tag => instance.tags[raw_tag])
124
+ end
125
+ end
126
+
127
+ subcommand "set", "set tag(s) for this server" do
128
+ include Options::Instance
129
+ include Options::InstanceName
130
+
131
+ parameter "TAGS ...", "tags of the form key:value", :attribute_name => :raw_tags
132
+
133
+ def execute
134
+ unless iname || iid
135
+ raise "Must set either server name or server id (-iname or -iid)"
136
+ end
137
+
138
+ connection = Rig::Connection.compute
139
+ if iid
140
+ instance = connection.servers.get(iid)
141
+ else
142
+ list = connection.servers.all({ "tag:Name" => iname })
143
+ if list.count > 1
144
+ puts "there is more than one server that matches:"
145
+ instance_list(list)
146
+ return
147
+ end
148
+ instance = list.first
149
+ end
150
+
151
+ unless instance
152
+ puts "could not find server"
153
+ return
154
+ end
155
+
156
+ raw_tags.each do |s|
157
+ (k, v) = s.split(':', 2)
158
+ #tags[k] = v if k && v
159
+ connection.create_tags(instance.id, k => v)
160
+ end
161
+ end
162
+ end
163
+ end
164
+ subcommand "view", "view server information" do
165
+ include Options::ShowAll
166
+
167
+ # TODO: something wrong here, view is showing all servers
168
+ parameter "NAME", "name of server"
169
+
170
+ def execute
171
+ list = Rig::Model::Instance.find(name)
172
+ list.each do |item|
173
+ ap item.attributes
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+ Rig::Command::Main.subcommand "server", "manage servers", Rig::Command::Server