outliers 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-gemset +1 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +5 -0
  5. data/CHANGELOG.md +3 -0
  6. data/LICENSE.txt +10 -19
  7. data/README.md +175 -8
  8. data/Rakefile +7 -0
  9. data/bin/outliers +6 -0
  10. data/lib/outliers/cli/evaluate.rb +115 -0
  11. data/lib/outliers/cli/process.rb +56 -0
  12. data/lib/outliers/cli/providers.rb +29 -0
  13. data/lib/outliers/cli/resources.rb +57 -0
  14. data/lib/outliers/cli.rb +78 -0
  15. data/lib/outliers/collection.rb +124 -0
  16. data/lib/outliers/credentials.rb +28 -0
  17. data/lib/outliers/evaluation.rb +85 -0
  18. data/lib/outliers/exceptions.rb +40 -0
  19. data/lib/outliers/mixins.rb +27 -0
  20. data/lib/outliers/provider.rb +32 -0
  21. data/lib/outliers/providers/aws/base.rb +33 -0
  22. data/lib/outliers/providers/aws/cloud_formation.rb +20 -0
  23. data/lib/outliers/providers/aws/ec2.rb +20 -0
  24. data/lib/outliers/providers/aws/elb.rb +20 -0
  25. data/lib/outliers/providers/aws/iam.rb +20 -0
  26. data/lib/outliers/providers/aws/rds.rb +20 -0
  27. data/lib/outliers/providers/aws/s3.rb +20 -0
  28. data/lib/outliers/providers/aws/sqs.rb +20 -0
  29. data/lib/outliers/providers/aws.rb +9 -0
  30. data/lib/outliers/providers/github.rb +23 -0
  31. data/lib/outliers/providers.rb +19 -0
  32. data/lib/outliers/resource.rb +34 -0
  33. data/lib/outliers/resources/aws/cloud_formation/stack.rb +10 -0
  34. data/lib/outliers/resources/aws/cloud_formation/stack_collection.rb +15 -0
  35. data/lib/outliers/resources/aws/ec2/instance.rb +75 -0
  36. data/lib/outliers/resources/aws/ec2/instance_collection.rb +15 -0
  37. data/lib/outliers/resources/aws/ec2/security_group.rb +28 -0
  38. data/lib/outliers/resources/aws/ec2/security_group_collection.rb +15 -0
  39. data/lib/outliers/resources/aws/elb/load_balancer.rb +51 -0
  40. data/lib/outliers/resources/aws/elb/load_balancer_collection.rb +15 -0
  41. data/lib/outliers/resources/aws/iam/user.rb +40 -0
  42. data/lib/outliers/resources/aws/iam/user_collection.rb +15 -0
  43. data/lib/outliers/resources/aws/rds/db_instance.rb +35 -0
  44. data/lib/outliers/resources/aws/rds/db_instance_collection.rb +15 -0
  45. data/lib/outliers/resources/aws/rds/db_snapshot.rb +13 -0
  46. data/lib/outliers/resources/aws/rds/db_snapshot_collection.rb +15 -0
  47. data/lib/outliers/resources/aws/s3/bucket.rb +73 -0
  48. data/lib/outliers/resources/aws/s3/bucket_collection.rb +18 -0
  49. data/lib/outliers/resources/aws/sqs/queue.rb +13 -0
  50. data/lib/outliers/resources/aws/sqs/queue_collection.rb +15 -0
  51. data/lib/outliers/resources/aws.rb +18 -0
  52. data/lib/outliers/resources/github/repo.rb +24 -0
  53. data/lib/outliers/resources/github/repo_collection.rb +13 -0
  54. data/lib/outliers/resources/github.rb +2 -0
  55. data/lib/outliers/resources.rb +12 -0
  56. data/lib/outliers/result.rb +24 -0
  57. data/lib/outliers/run.rb +40 -0
  58. data/lib/outliers/verifications/shared.rb +31 -0
  59. data/lib/outliers/verifications.rb +1 -0
  60. data/lib/outliers/version.rb +1 -1
  61. data/lib/outliers.rb +24 -1
  62. data/outliers.gemspec +9 -5
  63. data/spec/collection_spec.rb +103 -0
  64. data/spec/credentials_spec.rb +42 -0
  65. data/spec/evaluation_spec.rb +96 -0
  66. data/spec/fixtures/credentials1.yml +5 -0
  67. data/spec/fixtures/credentials2.yml +5 -0
  68. data/spec/helpers/fixtures.rb +8 -0
  69. data/spec/mixins_spec.rb +33 -0
  70. data/spec/provider_spec.rb +35 -0
  71. data/spec/providers_spec.rb +18 -0
  72. data/spec/resource_spec.rb +19 -0
  73. data/spec/resources_spec.rb +15 -0
  74. data/spec/results_spec.rb +33 -0
  75. data/spec/run_spec.rb +56 -0
  76. data/spec/spec_helper.rb +21 -0
  77. data/spec/verifications/shared_spec.rb +35 -0
  78. metadata +145 -29
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 96ee1fb7ec544e936e6a55b6c0d4bac0b8a9baea
4
+ data.tar.gz: 098ddc59e2cd429d938785b42336b592823e38a1
5
+ SHA512:
6
+ metadata.gz: 13c134dbeb4901b3016502c188cbfacf41aef8c8f11db1f1d8a85d8f81b08937759fed1c257039b5259130b46756d66e7b19174d742f43acbfe2ecaf2a28ca2b
7
+ data.tar.gz: 49dc924d5ea0694f3e8c4e98abae6ea93f327bf2c828309a375e4269430f14117b7169fc4e5dba91562fa8c81bc93e920917d2ab5bfdb73cea5ef63f6ffdb1ed
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ outliers
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+ - 2.0.0
5
+ script: "rake spec"
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## 0.0.1
2
+
3
+ * Initial release
data/LICENSE.txt CHANGED
@@ -1,22 +1,13 @@
1
- Copyright (c) 2013 Brett Weaver
1
+ Copyright 2013 Brett Weaver
2
2
 
3
- MIT License
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
4
6
 
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
7
+ http://www.apache.org/licenses/LICENSE-2.0
12
8
 
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md CHANGED
@@ -1,24 +1,191 @@
1
1
  # Outliers
2
2
 
3
- TODO: Write a gem description
3
+ Outliers is a framework for verifying configuration of resources.
4
+
5
+ ## Overview
6
+
7
+ * Applications and teams rely on multiple service providers (AWS, Github, etc).
8
+ * Providers deliver like resources with complex configuration (EC2 Instances, S3 Buckets, etc).
9
+ * Resource configuration can be verified (launched from given AMI, contain private objects, etc).
10
+ * Those not passing verifications, are flagged as Outliers.
4
11
 
5
12
  ## Installation
6
13
 
7
- Add this line to your application's Gemfile:
14
+ Install the gem:
15
+
16
+ gem install outliers
17
+
18
+ ## Setup
8
19
 
9
- gem 'outliers'
20
+ Create **~/outliers.yml** with a list of credentials in the following format:
10
21
 
11
- And then execute:
22
+ credential_name:
23
+ key1: value1
24
+ key2: value2
12
25
 
13
- $ bundle
26
+ Multiple accounts can be specified, to add a prod and preprod AWS account:
14
27
 
15
- Or install it yourself as:
28
+ aws_pre_prod:
29
+ region: us-east-1
30
+ access_key_id: YYY
31
+ secret_access_key: XXX
16
32
 
17
- $ gem install outliers
33
+ aws_prod:
34
+ region: us-east-1
35
+ access_key_id: AAA
36
+ secret_access_key: BBB
37
+
38
+ Depending on the provider, different keys and values are required. For a list of providers:
39
+
40
+ outliers providers
18
41
 
19
42
  ## Usage
20
43
 
21
- TODO: Write usage instructions here
44
+ Outliers can be used in two modes, as a **CLI** or a **DSL**.
45
+
46
+ The CLI is good for testing and quick verifications.
47
+
48
+ The DSL can be used to build up comprehensive list of verifications for a project or company.
49
+
50
+ ### CLI
51
+
52
+ To verify all EC2 instances are in a VPC:
53
+
54
+ outliers evaluate -c aws_prod -p aws_ec2 -r instance -v vpc
55
+
56
+ Credential keys can be overriden. To specify region us-west-1.
57
+
58
+ outliers evaluate -c aws_prod -p aws_ec2 -r instance -v vpc -c region=us-west-1
59
+
60
+ Verifications may require arguments. To verify your RDS databases have a 2 day retention period:
61
+
62
+ outliers evaluate -c aws_prod -p aws_rds -r db_instance -v backup_retention_period -a days=2
63
+
64
+ Verifications may accept multiple values for an argument. Values are separated by commas.
65
+
66
+ To verify EC2 instances are launched from a list of known images:
67
+
68
+ outliers evaluate -c aws_prod -p aws_ec2 -r instance -v valid_image_id -a image_ids=ami-12345678,ami-87654321
69
+
70
+ To only target a specific resource:
71
+
72
+ outliers evaluate -c aws_prod -p aws_ec2 -r instance -t i-87654321
73
+
74
+ To exclude resources that are known exceptions:
75
+
76
+ outliers evaluate -c aws_prod -p aws_ec2 -r instance -e i-12345678
77
+
78
+ ### DSL
79
+
80
+ To run Outliers as a DSL
81
+
82
+ * Create a directory to store your evaluations.
83
+ * Evalutions are read from from files within the directory.
84
+ * All files ending in **.rb** will be processed.
85
+ * Each file can have one or more evaluation blocks.
86
+
87
+ To process a directory:
88
+
89
+ outliers process -d /home/user/outliers
90
+
91
+ To verify all instances are in a VPC, create the file **ec2.rb** and add the following block:
92
+
93
+ evaluate do |e|
94
+ e.connect 'aws_prod', provider: 'aws_ec2'
95
+ e.resources 'instance'
96
+ e.verify 'vpc'
97
+ end
98
+
99
+ Files can have multiple evaluations, to add a validation that overrides the region:
100
+
101
+ evaluate do |e|
102
+ e.connect 'aws_prod', provider: 'aws_ec2'
103
+ e.resources 'instance'
104
+ e.verify 'vpc'
105
+ end
106
+
107
+ evaluate do |e|
108
+ e.connect 'aws_prod', provider: 'aws_ec2', region: 'us-west-1'
109
+ e.resources 'instance'
110
+ e.verify 'vpc'
111
+ end
112
+
113
+ The DSL supports any valid Ruby code. To iterate over multiple regions:
114
+
115
+ ['us-west-1', 'us-west-2', 'us-east-1'].each do |region|
116
+ evaluate do |e|
117
+ e.connect 'aws_prod', provider: 'aws_ec2', region: region
118
+ e.resources 'instance'
119
+ e.verify 'vpc'
120
+ end
121
+ end
122
+
123
+ Evaluations can be given names to help identify Outliers in results.
124
+
125
+ evaluate "validate_database_retention_period" do |e|
126
+ e.connect 'aws_prod', provider: 'aws_rds', region: 'us-west-1'
127
+ e.resources 'db_instance'
128
+ e.verify 'backup_retention_period', days: 2
129
+ end
130
+
131
+ To pass arguments to a verification:
132
+
133
+ evaluate do |e|
134
+ e.connect 'aws_prod', provider: 'aws_rds', region: 'us-west-1'
135
+ e.resources 'db_instance'
136
+ e.verify 'backup_retention_period', days: 2
137
+ end
138
+
139
+ To pass multiple arguments, specify them as an array:
140
+
141
+ evaluate do |e|
142
+ e.connect 'aws_prod', provider: 'aws_ec2', region: 'us-west-1'
143
+ e.resources 'instance'
144
+ e.verify 'valid_image_id', image_ids: ['ami-12345678','ami-87654321']
145
+ end
146
+
147
+ To only target a specific resource:
148
+
149
+ evaluate do |e|
150
+ e.connect 'aws_prod', provider: 'aws_ec2', region: 'us-west-1'
151
+ e.resources 'instance', 'i-12345678'
152
+ e.verify 'valid_image_id', image_ids: ['ami-12345678','ami-87654321']
153
+ end
154
+
155
+ To target multiple resources, you can pass an array:
156
+
157
+ evaluate do |e|
158
+ e.connect 'aws_prod', provider: 'aws_ec2', region: 'us-west-1'
159
+ e.resources 'instance', ['i-12345678', 'i-abcdef12']
160
+ e.verify 'valid_image_id', image_ids: ['ami-12345678','ami-87654321']
161
+ end
162
+
163
+ Sometimes you want to exclude resources that are known exceptions, to exclude an instance from the VPC validation:
164
+
165
+ evaluate do |e|
166
+ e.connect 'aws_prod', provider: 'aws_ec2', region: 'us-west-1'
167
+ e.resources 'instance'
168
+ e.exclude 'i-12345678'
169
+ e.verify 'valid_image_id', image_ids: ['ami-12345678','ami-87654321']
170
+ end
171
+
172
+ ### Help
173
+
174
+ For a list of providers and required credentials:
175
+
176
+ outliers providers
177
+
178
+ For a list of resources, and available verifications, for a given provider:
179
+
180
+ outliers resources -p PROVIDER_NAME
181
+
182
+ For a fule list of commands run:
183
+
184
+ outliers -h
185
+
186
+ For full help on a command, append -h:
187
+
188
+ outliers evaluate -h
22
189
 
23
190
  ## Contributing
24
191
 
data/Rakefile CHANGED
@@ -1 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
+
3
+ require "rspec/core/rake_task"
4
+
5
+ desc 'Run specs'
6
+ RSpec::Core::RakeTask.new do |t|
7
+ t.rspec_opts = %w(--color)
8
+ end
data/bin/outliers ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'outliers'
4
+ require 'outliers/cli'
5
+
6
+ Outliers::CLI.start
@@ -0,0 +1,115 @@
1
+ module Outliers
2
+ module CLI
3
+ class Evaluate
4
+ def evaluate
5
+ @options = { arguments: [], exclude: [], credentials: [], target_resources: [] }
6
+ @credentials = {}
7
+
8
+ option_parser.parse!
9
+
10
+ Outliers.config_path @options[:config]
11
+
12
+ @logger = Outliers.logger
13
+ @run = Run.new
14
+
15
+ load_credentials
16
+
17
+ begin
18
+ @run.evaluate "Validationf from CLI." do |e|
19
+ e.connect 'cli'
20
+ e.resources @options[:resources], target_resources
21
+ e.exclude @options[:exclude] if @options[:exclude].any?
22
+ e.verify @options[:verification], arguments
23
+ end
24
+ rescue Outliers::Exceptions::Base => e
25
+ @logger.error e.message
26
+ exit 1
27
+ end
28
+
29
+ exit 1 if @run.results.first.failed?
30
+ end
31
+
32
+ def command_name
33
+ 'evaluate'
34
+ end
35
+
36
+ def command_summary
37
+ 'Evaluate the given verification.'
38
+ end
39
+
40
+ private
41
+
42
+ def load_credentials
43
+ credentials_name = @options[:credential_name]
44
+ credentials = Credentials.load_from_file("#{ENV['HOME']}/.outliers.yml").fetch credentials_name
45
+ credentials.merge! 'provider' => @options[:provider]
46
+
47
+ if @options[:credentials].any?
48
+ @options[:credentials].each do |c|
49
+ key = c.split('=').first
50
+ value = c.split('=').last
51
+ credentials.merge! key => value
52
+ end
53
+ end
54
+
55
+ @run.credentials = { 'cli' => credentials }
56
+ end
57
+
58
+ def target_resources
59
+ @options[:target_resources]
60
+ end
61
+
62
+ def arguments
63
+ arguments = {}
64
+
65
+ @options[:arguments].each do |a|
66
+ key = a.split('=').first
67
+ value = a.split('=').last
68
+ value = value.split(',') if value.include?(',')
69
+ arguments.merge! key => value
70
+ end
71
+
72
+ arguments
73
+ end
74
+
75
+ def option_parser
76
+ OptionParser.new do |opts|
77
+ opts.banner = "Usage: outliers evaluate [options]"
78
+
79
+ opts.on("-a", "--argument [NAME]", "Equals seperated key and value to pass as argument to verification (can be specified multiple times).") do |o|
80
+ @options[:arguments] << o
81
+ end
82
+
83
+ opts.on("-c", "--credential_name [CREDENTIAL_NAME]", "Name to load from credentials file.") do |o|
84
+ @options[:credential_name] = o
85
+ end
86
+
87
+ opts.on("-e", "--exclude [EXCLUDE]", "Exclude resources in collection with the given key (can be specified multiple times).") do |o|
88
+ @options[:exclude] << o
89
+ end
90
+
91
+ opts.on("-p", "--provider [PROVIDER]", "Provider of target resources.") do |o|
92
+ @options[:provider] = o
93
+ end
94
+
95
+ opts.on("-r", "--resources [RESOURCES]", "Name of resources collection to evaluate.") do |o|
96
+ @options[:resources] = o
97
+ end
98
+
99
+ opts.on("-t", "--target_resources [TARGET_RESOURCES]", "Target resources with key name (can be specified more than once).") do |o|
100
+ @options[:target_resources] << o
101
+ end
102
+
103
+ opts.on("-v", "--verification [VERIFICATION]", "Verification to perform against collection of resources.") do |o|
104
+ @options[:verification] = o
105
+ end
106
+
107
+ opts.on("--credentials [CREDENTIALS]", "Equals seperated key and value to include as credentials (can be specified multiple times).") do |o|
108
+ @options[:credentials] << o
109
+ end
110
+ end
111
+ end
112
+
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,56 @@
1
+ module Outliers
2
+ module CLI
3
+ class Process
4
+ def process
5
+ @options = {}
6
+
7
+ option_parser.parse!
8
+
9
+ Outliers.config_path @options[:directory]
10
+
11
+ @logger = Outliers.logger
12
+ @run = Run.new
13
+
14
+ begin
15
+ @run.credentials = Credentials.load_from_file "#{ENV['HOME']}/.outliers.yml"
16
+ @run.process_evaluations_in_config_folder
17
+ rescue Outliers::Exceptions::Base => e
18
+ @logger.error e.message
19
+ exit 1
20
+ end
21
+
22
+ passed = @run.passed.count
23
+ failed = @run.failed.count
24
+
25
+ @run.failed.each do |f|
26
+ @logger.info "Evaluation '#{f.description}' failed."
27
+ end
28
+
29
+ @logger.info "(#{failed} evaluations failed, #{passed} evaluations passed.)"
30
+
31
+ exit 1 unless failed.zero?
32
+ end
33
+
34
+ def command_name
35
+ 'process'
36
+ end
37
+
38
+ def command_summary
39
+ 'Process evaluations in config folder.'
40
+ end
41
+
42
+ private
43
+
44
+ def option_parser
45
+ OptionParser.new do |opts|
46
+ opts.banner = "Usage: outliers process [options]"
47
+
48
+ opts.on("-d", "--directory [DIRECTORY]", "Directory containing evaluations to load.") do |o|
49
+ @options[:directory] = o
50
+ end
51
+ end
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,29 @@
1
+ module Outliers
2
+ module CLI
3
+ class Providers
4
+ def providers
5
+ option_parser.parse!
6
+ list = Outliers::Providers.name_map
7
+ list.each_pair do |k,v|
8
+ puts k
9
+ v.credential_arguments.each_pair { |k,v| puts " #{k}: #{v}" }
10
+ end
11
+ end
12
+
13
+ def command_name
14
+ 'providers'
15
+ end
16
+
17
+ def command_summary
18
+ 'List available providers.'
19
+ end
20
+
21
+ def option_parser
22
+ OptionParser.new do |opts|
23
+ opts.banner = "Usage: outliers providers"
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,57 @@
1
+ module Outliers
2
+ module CLI
3
+ class Resources
4
+ def resources
5
+ @options = {}
6
+
7
+ option_parser.parse!
8
+
9
+ provider = @options[:provider]
10
+
11
+ @logger = Outliers.logger
12
+
13
+ unless provider
14
+ @logger.error "Required parameter 'provider' not specified."
15
+ exit 1
16
+ end
17
+
18
+ all = Outliers::Resources.collections
19
+
20
+ list = all.select { |r| r.to_human =~ /^#{provider}_.*$/ }
21
+
22
+ if list.any?
23
+ list.each do |r|
24
+ name = r.to_human
25
+ name.slice! provider
26
+ name[0] = ''
27
+ puts name
28
+ r.verifications.each { |v| puts " #{v[:name]}(#{v[:args]}) #{v[:description]}" }
29
+ end
30
+ else
31
+ puts "No resources found for '#{provider}'."
32
+ end
33
+ end
34
+
35
+ def command_name
36
+ 'resources'
37
+ end
38
+
39
+ def command_summary
40
+ 'List available resources for a provider.'
41
+ end
42
+
43
+ private
44
+
45
+ def option_parser
46
+ OptionParser.new do |opts|
47
+ opts.banner = "Usage: outliers resources [options]"
48
+
49
+ opts.on("-p", "--provider [PROVIDER]", "Provider to list resources.") do |o|
50
+ @options[:provider] = o
51
+ end
52
+ end
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,78 @@
1
+ require 'optparse'
2
+
3
+ require 'outliers/cli/evaluate'
4
+ require 'outliers/cli/process'
5
+ require 'outliers/cli/providers'
6
+ require 'outliers/cli/resources'
7
+
8
+ module Outliers
9
+ module CLI
10
+
11
+ def self.start
12
+ cmd = ARGV.shift
13
+
14
+ case cmd
15
+ when 'evaluate'
16
+ begin
17
+ CLI::Evaluate.new.evaluate
18
+ rescue OptionParser::MissingArgument => e
19
+ puts e.message
20
+ exit 1
21
+ end
22
+ when 'providers'
23
+ begin
24
+ CLI::Providers.new.providers
25
+ rescue OptionParser::MissingArgument => e
26
+ puts e.message
27
+ exit 1
28
+ end
29
+ when 'process'
30
+ begin
31
+ CLI::Process.new.process
32
+ rescue OptionParser::MissingArgument => e
33
+ puts e.message
34
+ exit 1
35
+ end
36
+ when 'resources'
37
+ begin
38
+ CLI::Resources.new.resources
39
+ rescue OptionParser::MissingArgument => e
40
+ puts e.message
41
+ exit 1
42
+ end
43
+ when '-v'
44
+ puts OUTLIERS::VERSION
45
+ else
46
+ puts "Unknown command: '#{cmd}'."
47
+ puts ''
48
+ usage
49
+ exit 1
50
+ end
51
+ end
52
+
53
+ def self.usage
54
+ puts 'Usage: outliers command'
55
+ puts ''
56
+ puts 'Append -h for help on specific subcommand.'
57
+ puts ''
58
+
59
+ puts 'Commands:'
60
+ commands.each do |cmd|
61
+ $stdout.printf " %-#{length_of_longest_command}s %s\n",
62
+ cmd.command_name,
63
+ cmd.command_summary
64
+ end
65
+ end
66
+
67
+ def self.commands
68
+ return @commands if @commands
69
+ klasses = Outliers::CLI.constants
70
+ @commands = klasses.map { |klass| Outliers::CLI.const_get(klass).new }
71
+ end
72
+
73
+ def self.length_of_longest_command
74
+ commands.map { |c| c.command_name.length }.max
75
+ end
76
+
77
+ end
78
+ end